【Linux】linux进程控制(fork,exit,wait详解)

张开发
2026/4/11 22:28:30 15 分钟阅读

分享文章

【Linux】linux进程控制(fork,exit,wait详解)
本文是小编巩固自身而作如有错误欢迎指出目录一、进程创建fork函数返回值规则​编辑循环fork创建子进程写时拷贝​编辑二、进程终止正常退出自愿退出异常退出出错退出被信号杀死最常见三、进程等待进程等待的重要性wait阻塞等待任意子进程waitpid更强大指定 PID、非阻塞一、进程创建fork函数在前文我们已经讲述过fork的大体功能fork()是 Linux/Unix 中创建新进程最核心的系统调用作用是复制当前进程产生一个几乎一模一样的子进程。返回值规则父进程返回子进程的 PID0子进程返回0创建失败返回-1#include stdio.h #include unistd.h int main() { printf(before fork\n); pid_t pid fork(); if (pid 0) { perror(fork failed); return 1; } if (pid 0) { // 子进程 printf(I am child, pid%d, ppid%d\n, getpid(), getppid()); } else { // 父进程 printf(I am parent, pid%d, child pid%d\n, getpid(), pid); } return 0; }循环fork创建子进程#include stdio.h #include unistd.h #include sys/wait.h // 创建 5 个子进程 #define PROC_NUM 5 int main() { int i; pid_t pid; for (i 0; i PROC_NUM; i) { pid fork(); // 创建子进程 if (pid 0) { perror(fork failed); return 1; } // // 子进程执行任务然后退出 // if (pid 0) { printf(【子进程】 pid%d, 编号%d, 父pid%d\n, getpid(), i, getppid()); // 子进程执行完任务就退出不回到循环 return 0; } // 父进程什么都不做继续下一次循环 } // // 父进程等待所有子进程退出 // for (i 0; i PROC_NUM; i) { wait(NULL); } printf(【父进程】所有子进程已回收\n); return 0; }细心的同学会发现一个问题我们在for循环每个子进程创建完毕后也有一个return这是什么原因呢?因为子进程不能回到 for 循环否则子进程也会执行fork()导致进程爆炸。写时拷贝通常父子进程代码共享当父子进程不写入数据的时候数据是共享的当任意一方试图写入的时候便另外开一份空间以拷贝的方式拷贝一份数据进行修改原有的空间的数据属于未进行修改的那一方这样两方便各自有一份数据了写时拷贝就是先共享要改再复制。更标准一点的定义多个调用者一开始共享同一份数据 / 内存当任何一方要修改写数据时系统才真正为它复制一份独立副本让它在副本上修改不影响其他人。二、进程终止正常退出自愿退出1main 函数 returnC/C 程序main()返回0或其他值等价于调用exit(返回值)2调用 exit () /_exit () /_Exit ()exit()标准库函数会做清理刷新缓冲区、调用 atexit 函数_exit()/_Exit()系统调用直接退出不清理exit 和 _exit 的区别1. exit ()温柔退出执行流程调用atexit()注册的所有清理函数刷新所有标准 IO 缓冲区printf/fwrite 等数据会真正写入关闭所有打开的文件流最后调用_exit()让内核终止进程2. _exit ()暴力退出执行流程直接进入内核立即终止进程不刷新缓冲区不执行清理函数不关闭用户态文件流内核直接回收进程资源#include stdio.h #include stdlib.h #include unistd.h void clean() { printf(清理函数执行\n); } int main() { atexit(clean); // 注册退出时执行的函数 printf(Hello World); // 没有换行数据在缓冲区 // 测试1用 exit() —— 会输出、会执行清理函数 exit(0); // 测试2用 _exit() —— 什么都不输出直接退出 // _exit(0); }exit()输出_exit()输出exit(0)输出 Hello World 清理函数执行_exit(0)无任何输出缓冲区数据丢失3最后一个线程退出多线程程序中主线程退出不代表进程退出最后一个线程退出时进程才终止。异常退出出错退出除 0非法内存访问段错误 Segmentation fault总线错误栈溢出未捕获的异常C、Java、Python内核会向进程发送致命信号进程无法忽略。被信号杀死最常见常见终止信号SIGINT (2)Ctrl CSIGQUIT (3)Ctrl \SIGKILL (9)强制杀死不可捕获、不可忽略SIGTERM (15)默认 kill 命令可捕获SIGSEGV (11)段错误SIGABRT (6)调用 abort () 或 assert 失败信号导致退出时进程会被内核强制终止。父进程退出被 init/systemd 收养后清理父进程先死子进程变成孤儿进程被 PID 1init/systemd收养子进程退出后由 PID 1 自动回收三、进程等待进程等待的重要性子进程退出如果父进程对此漠不关心那么就会造成僵尸进程进而造成内存泄露的问题。所以进程等待就是父进程调用 wait /waitpid等子进程退出然后回收它的尸体PCB防止僵尸进程。回收子进程资源防止僵尸进程Zombie获取子进程退出状态正常退出被杀死退出码多少父子进程同步父进程等子进程干完再继续wait阻塞等待任意子进程wait的作用是等待当前进程的任意一个子进程的状态改变即检测并等待进程状态变成Z僵尸状态释放进程的资源获取进程退出信息返回值 成功返回被等待进程pid失败返回-1#include stdio.h #include stdlib.h #include unistd.h #include sys/types.h #include sys/wait.h #include errno.h void RunChild() { int cnt 2; while(cnt) { printf(i am child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt); cnt--; sleep(1); } } int main() { pid_t id fork(); if(id 0) { perror(fork); return 1; } else if(id 0) { RunChild(); exit(0); } else { //id 0 sleep(4) int ret waitpid(-1, NULL, 0); if(ret id) { printf(我是父进程我等待子进程成功了, 子进程的pid: %d\n, ret); } else { printf(等待子进程失败\n); } sleep(2); } return 0; }完整执行时间线0 秒fork 创建父子进程0~2 秒子进程跑打印 2 次第 2 秒子进程执行完毕调用exit(0)子进程变成僵尸进程Z子进程资源释放但PCB 还在2~4 秒父进程还在 sleep没回收子进程→这段时间子进程是僵尸进程第 4 秒父进程 sleep 结束第 4 秒父进程调用wait(NULL)回收僵尸子进程打印等待成功4~6 秒父进程再 sleep (2)结束waitpid更强大指定 PID、非阻塞让父进程等待指定的子进程回收它防止僵尸进程。函数原型如下#include sys/wait.h pid_t waitpid(pid_t pid, int *status, int options);pid等待哪个子进程pid -1等待任意子进程等价waitpid 0等待指定 PID的子进程pid 0等待同组的任意子进程pid -1等待指定进程组的任意子进程最常用pid -1或 子进程 PIDstatus输出子进程退出状态和wait一样用宏解析WIFEXITED(status)是否正常退出WEXITSTATUS(status)获取退出码WIFSIGNALED(status)是否被信号杀死WTERMSIG(status)获取杀死信号options最关键0阻塞等待和 wait 一样WNOHANG非阻塞等待没有子进程退出就立刻返回不卡住返回值 0成功回收的子进程 PID 0子进程还在运行只有用 WNOHANG 才会返回-1出错没有子进程了现在我们就看看和普通wait最主要的区别waitpid可以非阻塞等待#include stdio.h #include unistd.h #include sys/wait.h #include stdlib.h int main() { pid_t id fork(); if (id 0) { // 子进程 printf(子进程运行3秒后退出\n); sleep(3); exit(0); } // 父进程非阻塞轮询 while (1) { int status; // WNOHANG 非阻塞 pid_t ret waitpid(id, status, WNOHANG); if (ret 0) { // 子进程还在跑 printf(父进程子进程还没退出我继续干别的...\n); sleep(1); } else if (ret id) { // 子进程退出了 printf(父进程检测到子进程退出回收成功\n); break; } } return 0; }本次分享就到这里结束了后续会继续更新感谢阅读

更多文章