僵尸进程
-
产生原因
- 子进程在调用
exit(n);或return n;返回后,会将返回值传递给操作系统,进入终止状态- 这时候操作系统并不会销毁子进程
- 若父进程仍未终止,那处在此状态的子进程就是僵尸进程
- 可通过
ps au命令的STAT列为Z+验证
- 可通过
- 若父进程终止了,那子进程也会被销毁
- 子进程在调用
-
一般的解决方法
-
让父进程主动接收子进程的返回值即可
-
方法1——使用wait函数
#include <sys/wait.h> pid_t wait(int* statloc);- 此函数会将状态填入statloc指向的地址,若成功则返回接收的子进程ID
- 父进程到状态后,需要用宏进行分离
int status; wait(&status); if (WIFEXITED(status)) { //检查是否正常终止 printf("Return Value: %d \n", WEXITSTATUS(status)); //获取返回值 } - 缺点
- 调用wait时,如果没有已终止的子进程,那父进程必将阻塞,因此要谨慎使用
- 若不想阻塞,可以使用方法2并配合WNOHANG使用
- 调用wait时,如果没有已终止的子进程,那父进程必将阻塞,因此要谨慎使用
-
方法2——使用waitpid函数
#include <sys/wait.h> pid_t wait(pid_t pid, int* statloc, int options);- pid 等待终止的目标子进程ID
- 若传递-1,则可等待任意子进程
- options
- 建议传递<sys/wait.h>中声明的WNOHANG,可在没有已终止的子进程时,直接返回0,不阻塞父进程
- 使用例子
int status; while (!waitpid(-1, &status, WNOHANG)) { sleep(3); } if (WIFEXITED(status)) { //检查是否正常终止 printf("Return Value: %d \n", WEXITSTATUS(status)); //获取返回值 }
- pid 等待终止的目标子进程ID
-
-
更好的解决方法
- 让父进程监听子进程终止的信号,并触发提前指定的回调函数
- 旧方法——使用signal函数
- 不推荐,原因是在不同的Unix系统中可能存在差别
#include <signal.h> void (*signal(int signo, void (*func)(int)))(int)- signal 函数名
- signo 参数1,代表监听的信号
- SIGALRM —— 已超过调用alarm函数注册的时间
- SIGINT —— 键盘输入CTRL+C
- SIGCHLD —— 子进程终止
- func 参数2,代表信号发生时调用的回调函数指针
- 一般传递函数名即可
- 返回void型函数指针,一般不使用
- 使用例子
#include <stdio.h> #include <unistd.h> #include <signal.h> void alarmHandler(int sig) { if (sig == SIGALRM) puts("Timeout"); alarm(2); } int main(int argc, char* argv[]) { signal(SIGALRM, alarmHandler); alarm(2); //2秒后触发SIGALRM信号 for(int i = 0; i < 3; i++) { puts("sleep"); sleep(100); //休眠100秒 } return 0; }- 输出结果
sleep Timeout sleep Timeout sleep Timeout - 注意: 信号触发时将会唤醒休眠中的进程,将会立刻进入下一次循环输入sleep
- 输出结果
- 推荐方法——使用sigaction函数
#include <signal.h> int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact)- signo 代表监听的信号
- act 结构体指针,内含信号发生时调用的回调函数指针
struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; } - oldact 可传入指针,获取之前注册的回调函数
- 等价使用例子
#include <stdio.h> #include <unistd.h> #include <signal.h> void alarmHandler(int sig) { if (sig == SIGALRM) puts("Timeout"); alarm(2); } int main(int argc, char* argv[]) { struct sigaction act; act.sa_handler = alarmHandler; sigemptyset(&act.sa_mask) //置0 act.sa_flags = 0; //置0 sigaction(SIGALRM, &act, 0); alarm(2); //2秒后触发SIGALRM信号 for(int i = 0; i < 3; i++) { puts("sleep"); sleep(100); //休眠100秒 } return 0; } - 解决僵尸进程
void read_childprocess() { int status; pid_t id = waitpid(-1, &status, WNOHANG) if (WIFEXITED(status)) { //检查是否正常终止 printf("Removed Process ID: %d \n", id); printf("Return Value: %d \n", WEXITSTATUS(status)); //获取返回值 } } int main(int argc, char* argv[]) { struct sigaction act; act.sa_handler = read_childprocess; sigemptyset(&act.sa_mask) //置0 act.sa_flags = 0; //置0 sigaction(SIGCHLD, &act, 0); //下面正常调用fork即可 }