如何用fork、wait、waitpid让父进程等待所有层级子进程执行完毕?
解决方法:用wait/waitpid等待子进程,确保进程状态可被ps捕获
问题根源
你的子进程因为执行逻辑太简单(无耗时操作),在父进程调用system("ps")前就已退出,导致ps无法捕获;同时未调用wait/waitpid的话,退出的子进程会变成僵尸进程(如当前输出里的[a]),无法正常显示为运行状态。
核心思路
- 让子进程保持运行直到父进程执行ps:给子进程添加
sleep()等延时操作,避免提前退出。 - 每个父进程(包括中间进程)等待自己的直接子进程:顶层父进程等待自己创建的中间进程、另一个子进程;中间进程等待自己的子进程,避免出现孤儿进程或僵尸进程。
- 顶层父进程在所有子进程运行时执行ps,之后再等待所有子进程完成(满足你看到完整进程列表的需求)。
方法1:用wait()等待所有直接子进程
wait()会阻塞当前进程,直到任意一个直接子进程退出,返回该子进程的PID。需要针对每个子进程单独调用,或循环调用直到所有子进程被回收。
示例代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid_mid, pid_child; // 创建中间进程 pid_mid = fork(); if (pid_mid == 0) { // 中间进程创建自己的子进程 pid_t pid_grand = fork(); if (pid_grand == 0) { // 中间进程的子进程:保持运行2秒,让父进程ps能捕获到 printf("中间进程的子进程 PID: %d,PPID: %d\n", getpid(), getppid()); sleep(2); exit(0); } else if (pid_grand > 0) { // 中间进程:等待自己的子进程完成 wait(NULL); printf("中间进程 %d 已等待子进程完成\n", getpid()); sleep(1); exit(0); } else { perror("创建中间进程的子进程失败"); exit(1); } } else if (pid_mid > 0) { // 顶层父进程:创建另一个子进程 pid_child = fork(); if (pid_child == 0) { printf("另一个子进程 PID: %d,PPID: %d\n", getpid(), getppid()); sleep(2); exit(0); } else if (pid_child > 0) { // 执行ps命令,此时所有子进程都在运行 printf("\n=== 当前进程状态 ===\n"); system("ps -f --ppid 306,%d,%d", getpid(), pid_mid); // 替换306为你的实际父进程PID // 等待所有直接子进程完成 int status; waitpid(pid_mid, &status, 0); // 等待中间进程 waitpid(pid_child, &status, 0); // 等待另一个子进程 printf("\n所有子进程已完成\n"); } else { perror("创建另一个子进程失败"); exit(1); } } else { perror("创建中间进程失败"); exit(1); } return 0; }
方法2:用waitpid()灵活等待所有子进程
waitpid()可以指定等待特定PID的子进程,或用-1等待任意子进程,适合不确定子进程数量的场景。
示例代码(循环等待所有子进程)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { int child_num = 0; pid_t pid; // 创建中间进程 pid = fork(); if (pid == 0) { // 中间进程创建子进程 pid_t grand_pid = fork(); if (grand_pid == 0) { printf("孙子进程 PID: %d,PPID: %d\n", getpid(), getppid()); sleep(2); exit(0); } else if (grand_pid > 0) { waitpid(grand_pid, NULL, 0); // 中间进程等待自己的子进程 printf("中间进程 %d 已等待孙子进程完成\n", getpid()); sleep(1); exit(0); } perror("创建孙子进程失败"); exit(1); } else if (pid > 0) { child_num++; } else { perror("创建中间进程失败"); exit(1); } // 创建另一个子进程 pid = fork(); if (pid == 0) { printf("另一个子进程 PID: %d,PPID: %d\n", getpid(), getppid()); sleep(2); exit(0); } else if (pid > 0) { child_num++; } else { perror("创建另一个子进程失败"); exit(1); } // 执行ps查看进程状态 printf("\n=== 进程状态 ===\n"); system("ps -f --ppid 306,%d", getpid()); // 替换306为你的实际父进程PID // 循环等待所有直接子进程完成 printf("\n等待所有子进程结束...\n"); while (child_num > 0) { pid_t finished_pid = waitpid(-1, NULL, 0); if (finished_pid > 0) { printf("子进程 %d 已完成\n", finished_pid); child_num--; } } printf("所有子进程处理完毕\n"); return 0; }
关键说明
- 子进程延时:添加
sleep()是为了让父进程的ps命令能捕获到运行中的子进程,否则子进程会瞬间退出,ps只能看到僵尸进程或完全看不到。 - 层级等待:中间进程必须等待自己的子进程,否则孙子进程会变成孤儿进程,被系统
init/systemd进程收养,顶层父进程无法通过wait捕获它。 - wait与waitpid的区别:
wait():阻塞等待任意一个直接子进程退出,无法指定PID。waitpid():可以指定等待某个PID的子进程,或用-1等待任意子进程,还能通过WNOHANG选项实现非阻塞等待(不阻塞父进程,轮询子进程状态)。
内容的提问来源于stack exchange,提问作者Anuj Shah
相关产品推荐
相关产品推荐

