写在前面:
兄弟们,我手里有个长期项目,考虑接私活的可以看看我GitHub!
https://github.com/ccy-233/coder2retire
进程就是运行中的程序,是系统资源分配的基本单位
每个进程都有唯一的PID(进程ID)
进程有父子关系,通过ps -ef可以查看
pid_t pid = fork();
- 简单理解:fork()就像细胞分裂,一个变两个
调用一次,返回两次:父进程得到子进程PID,子进程得到0
子进程是父进程的复制品
execl("/bin/ls", "ls", "-l", NULL);
- 保留进程ID,但内容完全变成新程序
就像换灵魂:外壳不变,内在全变
常用函数:execl, execlp, execle, execv, execvp等
pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
- 父进程等待子进程结束并回收资源
避免僵尸进程(zombie)的产生
waitpid比wait更灵活,可以非阻塞等待
exit(0); // 标准C库函数
_exit(0); // 系统调用
exit会刷新缓冲区,exit直接退出
返回值0表示正常退出
就像文件的"身份证号码"
标准输入(0)、标准输出(1)、标准错误(2)
每个进程最多可打开的文件数有限制
// 打开文件 int fd = open("test.txt", O_RDWR | O_CREAT, 0644); // 读取文件 char buf[1024]; int n = read(fd, buf, sizeof(buf)); // 写入文件 write(fd, "hello", 5); // 关闭文件 close(fd); // 文件指针定位 lseek(fd, 0, SEEK_SET); // 回到文件开头
struct stat st; stat("file.txt", &st); - 获取文件大小:st.st_size
DIR *dir = opendir("."); struct dirent *entry; while ((entry = readdir(dir)) != NULL) { printf("%s\n", entry->d_name); } closedir(dir);
int fd[2]; pipe(fd); // fd[0]读端,fd[1]写端
单向通信:水管模型,一端进一端出
只能用于有亲缘关系的进程
适合简单的数据传输
// 创建命名管道 mkfifo("myfifo", 0644); // 使用方法与普通文件类似 int fd = open("myfifo", O_WRONLY);
- 可用于无关进程间通信
// 信号处理函数 void sig_handler(int signo) { printf("收到信号: %d\n", signo); } // 设置信号处理 signal(SIGINT, sig_handler);
- 常见信号:
SIGINT (2): Ctrl+C中断
SIGKILL (9): 强制终止(不可捕获)
SIGTERM (15): 终止信号
SIGCHLD (17): 子进程状态改变
SIGALRM (14): 定时器到期
void daemonize() { pid_t pid = fork(); if (pid < 0) exit(1); if (pid > 0) exit(0); // 父进程退出 setsid(); // 创建新会话 // 关闭标准输入输出错误 close(0); close(1); close(2); // ...更多操作 }
while (1) { printf("myshell> "); fgets(cmd, sizeof(cmd), stdin); if (fork() == 0) { // 子进程执行命令 execlp(cmd, cmd, NULL); exit(0); } else { // 父进程等待 wait(NULL); } }
僵尸进程:子进程结束但未被回收
孤儿进程:父进程先于子进程结束
内存泄漏:申请的内存未释放
文件描述符泄漏:打开文件未关闭
strace:跟踪系统调用
gdb:代码调试器
valgrind:内存错误检测
lsof:查看打开的文件