上一节提到,当子进程执行结束,父进程还在执行,在父进程结束之前子进程会成为僵尸进程,那么怎么销毁僵尸进程呢?父进程主动接收子进程的返回值。
销毁僵尸进程的方法:
1:使用wait函数
2:使用waitpid函数
3:利用信号
1:使用wait函数销毁僵尸进程
#include <sys/wait.h> pid_t wait(int *status); // 成功返回终止的子进程id,失败返回-1
在父进程中调用wait函数以后,如果有子进程已经执行结束,那么子进程传回的返回值将存储到status所指的内存中,但是如果没有子进程执行结束,父进程将会阻塞在wait函数,直到有子进程执行结束,这是一种不好的实现方法。
// wait函数销毁僵尸进程
#include <iostream> #include <unistd.h> #include <sys/wait.h> using namespace std; int main() { pid_t pid = fork(); if (pid < 0) { cout << "fork() failed" << endl; return 0; } if (pid == 0) { return 5; } else { int status = 0; wait(&status); if (WIFEXITED(status)) { // 子进程正常结束 cout << "child return: " << WEXITSTATUS(status) << endl; } sleep(300); } return 0; }
2:使用waitpid函数销毁僵尸进程
wait函数会使程序阻塞,可换用waitpid函数杀死僵尸进程,同时能避免程序阻塞。
#include <sys/wait.h> pid_t waitpid(pid_t pid, int *status, int options); // 成功返回终止的子进程id,失败返回-1 // pid : 等待终止的子进程id,传-1代码任意子进程终止 // status:和wait函数的参数一样 // options:传递WNOHANG,没有终止的子进程也不进入阻塞状态,返回0
#include <iostream> #include <unistd.h> #include <sys/wait.h> using namespace std; int main() { pid_t pid = fork(); if (pid < 0) { cout << "fork() failed" << endl; return 0; } if (pid == 0) { sleep(30); return 5; } else { int status = 0; while (!waitpid(pid, &status, WNOHANG)) { sleep(3); cout << "child proc is not finish" << endl; } if (WIFEXITED(status)) { // 子进程正常结束 cout << "child return: " << WEXITSTATUS(status) << endl; } } return 0; }
3:利用信号销毁僵尸进程
利用wait函数能销毁僵尸进程,但是会阻塞父进程;利用waitpid也能销毁僵尸进程并且不阻塞父进程,但是也需要不停的去检查子进程结束没有。所以wait及waitpid方式都不完美。于是引入了信号,信号是在特定事件发生时由操作系统向进程发送的消息,接收到消息的进程做信号处理。信号的使用方法是先注册信号,告诉操作系统,当某个信号发生时,要做什么事情。
signal(SIGCHLD, do_what); SIGCHLD是子进程结束的信号,当子进程结束时,执行do_what函数。
其他常用信号,SIGALRM:已到通过调用alarm函数注册的时间;SIGINT:输入CTRL+C
#include <iostream> #include <cstdlib> #include <unistd.h> #include <signal.h> #include <sys/wait.h> using namespace std; void read_child_proc(int sig) { int status; pid_t pid = waitpid(-1, &status, WNOHANG); if (WIFEXITED(status)) { // 子进程正常结束 cout << "child proc finish: " << pid <<" " << WEXITSTATUS(status) << endl; } } int main() { pid_t pid = fork(); if (pid < 0) { cout << "fork() failed" << endl; return 0; } signal(SIGCHLD, read_child_proc); // 注册子进程结束信号,当子进程结束时调用read_child_proc函数 if (pid == 0) { return 5; } else { sleep(30); cout << "master proc wake up" << endl; } return 0; }
执行程序发现 master proc wake up并不是等30秒以后才输出,而输出很快,说明当注册子进程结束信号以后,当有子进程执行结束时,会马上唤醒sleep的进程。