wordpress费用,网站seo排名优化工具在线,广州网站建设哪里有,正能量网站窗口免费进1. 理解子进程终止的机制
在Unix/Linux系统中#xff0c;当子进程终止时#xff0c;会向父进程发送一个SIGCHLD信号。父进程需要捕捉这个信号#xff0c;并通过调用wait()或waitpid()等函数来回收子进程的资源。这一过程被称为“回收僵尸进程”。
如果父进程没有及时调用w…
1. 理解子进程终止的机制
在Unix/Linux系统中当子进程终止时会向父进程发送一个SIGCHLD信号。父进程需要捕捉这个信号并通过调用wait()或waitpid()等函数来回收子进程的资源。这一过程被称为“回收僵尸进程”。
如果父进程没有及时调用wait()或相关函数子进程将会成为僵尸进程占用系统资源直到父进程终止或调用相应的等待函数。
2. 使用wait()和waitpid()函数
wait()使父进程阻塞直到任一子进程终止。它会返回终止子进程的PID并存储子进程的退出状态。waitpid()提供更精细的控制可以等待特定的子进程或采用非阻塞方式。
例子阻塞等待子进程终止
#include iostream
#include unistd.h
#include sys/types.h
#include sys/wait.hint main() {pid_t pid fork(); // 创建子进程if (pid 0) {std::cerr Fork failed! std::endl;return 1;}else if (pid 0) {// 子进程执行的代码std::cout 子进程PID getpid() 启动。 std::endl;sleep(2); // 模拟子进程工作std::cout 子进程PID getpid() 结束。 std::endl;return 42; // 子进程以状态42退出}else {// 父进程执行的代码std::cout 父进程PID getpid() 等待子进程结束。 std::endl;int status;pid_t terminated_pid wait(status); // 阻塞等待任一子进程结束if (terminated_pid 0) {if (WIFEXITED(status)) {std::cout 子进程PID terminated_pid 以状态 WEXITSTATUS(status) 退出。 std::endl;}else if (WIFSIGNALED(status)) {std::cout 子进程PID terminated_pid 被信号 WTERMSIG(status) 终止。 std::endl;}}else {std::cerr 等待子进程失败。 std::endl;}}return 0;
}输出示例
父进程PID12345 等待子进程结束。
子进程PID12346 启动。
子进程PID12346 结束。
子进程PID12346 以状态 42 退出。代码解释
父进程调用fork()创建子进程。子进程执行自己的任务后以状态42退出。父进程调用wait()阻塞等待子进程结束并获取子进程的退出状态。父进程通过宏WIFEXITED和WEXITSTATUS判断子进程是否正常退出及其退出状态。
3. 使用SIGCHLD信号处理异步回收
为了避免父进程被阻塞可以通过信号处理函数异步处理子进程的终止。这在需要父进程继续执行其他任务时非常有用。
例子使用SIGCHLD处理子进程终止
#include iostream
#include unistd.h
#include sys/types.h
#include sys/wait.h
#include signal.h
#include cstring// 信号处理函数
void sigchld_handler(int signum) {// 循环回收所有已终止的子进程while (true) {int status;pid_t pid waitpid(-1, status, WNOHANG);if (pid 0) {break;}if (WIFEXITED(status)) {std::cout [Signal Handler] 子进程PID pid 以状态 WEXITSTATUS(status) 退出。 std::endl;}else if (WIFSIGNALED(status)) {std::cout [Signal Handler] 子进程PID pid 被信号 WTERMSIG(status) 终止。 std::endl;}}
}int main() {// 注册SIGCHLD信号处理函数struct sigaction sa;sa.sa_handler sigchld_handler;sigemptyset(sa.sa_mask);sa.sa_flags SA_RESTART | SA_NOCLDSTOP;if (sigaction(SIGCHLD, sa, NULL) -1) {std::cerr 无法注册SIGCHLD处理器 strerror(errno) std::endl;return 1;}pid_t pid fork(); // 创建子进程if (pid 0) {std::cerr Fork failed! std::endl;return 1;}else if (pid 0) {// 子进程执行的代码std::cout 子进程PID getpid() 启动。 std::endl;sleep(2); // 模拟子进程工作std::cout 子进程PID getpid() 结束。 std::endl;return 24; // 子进程以状态24退出}else {// 父进程执行的其他任务std::cout 父进程PID getpid() 正在执行其他任务。 std::endl;// 模拟父进程执行其他任务for (int i 0; i 5; i) {std::cout 父进程执行中 i 1 std::endl;sleep(1);}// 父进程结束前确保所有子进程已被回收// 可以调用wait(NULL)或者让信号处理器完成回收}return 0;
}输出示例
父进程PID12345 正在执行其他任务。
子进程PID12346 启动。
父进程执行中1
父进程执行中2
子进程PID12346 结束。
[Signal Handler] 子进程PID12346 以状态 24 退出。
父进程执行中3
父进程执行中4
父进程执行中5代码解释
注册SIGCHLD信号处理器 使用sigaction结构体注册sigchld_handler函数作为SIGCHLD信号的处理器。SA_RESTART标志用于在信号处理后自动重启被中断的系统调用。SA_NOCLDSTOP标志表示当子进程停止或继续时父进程不接收SIGCHLD信号。 创建子进程 子进程执行自己的任务并以状态24退出。 父进程执行其他任务 父进程在等待子进程结束的同时继续执行其他任务不会被阻塞。 信号处理函数sigchld_handler 当子进程终止时SIGCHLD信号会被触发sigchld_handler函数会被调用。在函数内部使用waitpid和WNOHANG选项非阻塞地回收所有已终止的子进程防止僵尸进程的产生。
4. 避免僵尸进程的策略
及时调用wait()或waitpid()确保父进程在子进程终止后立即回收其资源。使用信号处理器如上文所示通过注册SIGCHLD信号处理器可以在子进程终止时自动回收资源而不需要父进程主动等待。设置SIGCHLD为SIG_IGN在一些系统上可以通过将SIGCHLD信号的处理方式设置为忽略从而自动回收子进程资源。这种方法不适用于所有情况需谨慎使用。
例子设置SIGCHLD为忽略
#include iostream
#include unistd.h
#include signal.h
#include cstringint main() {// 设置SIGCHLD为忽略signal(SIGCHLD, SIG_IGN);pid_t pid fork(); // 创建子进程if (pid 0) {std::cerr Fork failed! std::endl;return 1;}else if (pid 0) {// 子进程执行的代码std::cout 子进程PID getpid() 启动。 std::endl;sleep(2); // 模拟子进程工作std::cout 子进程PID getpid() 结束。 std::endl;return 0;}else {// 父进程执行其他任务std::cout 父进程PID getpid() 正在执行其他任务。 std::endl;sleep(5); // 父进程等待子进程结束}return 0;
}注意事项
这种方法依赖于系统对SIGCHLD的具体实现不保证在所有Unix/Linux系统中都有效。尽管简便但可能无法获取子进程的退出状态限制了错误处理和日志记录的能力。
5. 处理多个子进程的终止
当父进程创建多个子进程时需要确保所有子进程的终止都被正确处理以避免僵尸进程。可以在SIGCHLD处理函数中使用循环调用waitpid直到所有终止的子进程都被回收。
例子处理多个子进程
#include iostream
#include unistd.h
#include sys/types.h
#include sys/wait.h
#include signal.h
#include vector
#include cstring// 信号处理函数
void sigchld_handler(int signum) {// 循环回收所有已终止的子进程while (true) {int status;pid_t pid waitpid(-1, status, WNOHANG);if (pid 0) {break;}if (WIFEXITED(status)) {std::cout [Signal Handler] 子进程PID pid 以状态 WEXITSTATUS(status) 退出。 std::endl;}else if (WIFSIGNALED(status)) {std::cout [Signal Handler] 子进程PID pid 被信号 WTERMSIG(status) 终止。 std::endl;}}
}int main() {// 注册SIGCHLD信号处理函数struct sigaction sa;sa.sa_handler sigchld_handler;sigemptyset(sa.sa_mask);sa.sa_flags SA_RESTART | SA_NOCLDSTOP;if (sigaction(SIGCHLD, sa, NULL) -1) {std::cerr 无法注册SIGCHLD处理器 strerror(errno) std::endl;return 1;}std::vectorpid_t child_pids;// 创建多个子进程for (int i 0; i 3; i) {pid_t pid fork();if (pid 0) {std::cerr Fork failed! std::endl;return 1;}else if (pid 0) {// 子进程执行的代码std::cout 子进程PID getpid() 启动。 std::endl;sleep(2 i); // 模拟不同的工作时间std::cout 子进程PID getpid() 结束。 std::endl;return i;}else {// 父进程记录子进程PIDchild_pids.push_back(pid);}}// 父进程执行其他任务std::cout 父进程PID getpid() 正在执行其他任务。 std::endl;sleep(6); // 等待所有子进程结束// 由于SIGCHLD处理器已经回收了子进程父进程无需再次调用waitreturn 0;
}输出
父进程PID12345 正在执行其他任务。
子进程PID12346 启动。
子进程PID12347 启动。
子进程PID12348 启动。
子进程PID12346 结束。
[Signal Handler] 子进程PID12346 以状态 0 退出。
子进程PID12347 结束。
[Signal Handler] 子进程PID12347 以状态 1 退出。
子进程PID12348 结束。
[Signal Handler] 子进程PID12348 以状态 2 退出。代码解释
创建多个子进程循环调用fork()创建三个子进程每个子进程有不同的工作时间。信号处理函数sigchld_handler会被多次调用以回收每个子进程的资源。父进程执行其他任务父进程在子进程运行期间继续执行其他任务不会被阻塞。无需显式等待由于信号处理器已经负责回收子进程父进程无需再次调用wait()或waitpid()。
6. 使用prctl设置子进程终止时的行为
在某些情况下可以通过prctl系统调用设置子进程终止时的行为。例如可以设置PR_SET_CHILD_SUBREAPER使得特定的进程成为“子进程的收割者”用于复杂的进程管理。
例子设置PR_SET_CHILD_SUBREAPER
#include iostream
#include unistd.h
#include sys/types.h
#include sys/wait.h
#include signal.h
#include cstring
#include sys/prctl.hint main() {// 将当前进程设置为子进程的收割者if (prctl(PR_SET_CHILD_SUBREAPER, 1) -1) {std::cerr prctl failed: strerror(errno) std::endl;return 1;}pid_t pid fork();if (pid 0) {std::cerr Fork failed! std::endl;return 1;}else if (pid 0) {// 子进程执行的代码std::cout 子进程PID getpid() 启动。 std::endl;sleep(2);std::cout 子进程PID getpid() 结束。 std::endl;return 0;}else {// 父进程作为子进程的收割者等待子进程结束std::cout 父进程设置为子进程的收割者等待子进程结束。 std::endl;int status;pid_t terminated_pid wait(status);if (terminated_pid 0) {if (WIFEXITED(status)) {std::cout 子进程PID terminated_pid 以状态 WEXITSTATUS(status) 退出。 std::endl;}}}return 0;
}代码解释
prctl(PR_SET_CHILD_SUBREAPER, 1)将当前进程设置为子进程的收割者使其能够回收其子孙进程的资源。这种设置在需要复杂的子进程管理或在容器化环境中非常有用。