僵尸进程
ssk-wh Lv4

产生

如果子进程先退出了,父进程还未结束并且没有调用 wait 或者 waitpid 函数获取子进程的状态信息,则子进程残留的状态信息( task_struct 结构和少量资源信息)会变成僵尸进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// gcc -o zombie zombie.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
pid_t pid;

// 创建子进程
pid = fork();
if (pid < 0) {
// fork失败
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程
printf("Child process with PID: %d\n", getpid());
// 子进程立即退出
exit(EXIT_SUCCESS);
} else {
// 父进程
printf("Parent process with PID: %d, Child PID: %d\n", getpid(), pid);
}

// 父进程继续执行其他任务,例如睡眠一段时间
printf("Parent process continue do something\n");
sleep(10);

return 0;
}

在终端中输入 gcc -o zombie zombie.c 进行编译,之后运行 ./zombie 进程。此时通过 ps -ef | grep zombie 可以看到 fork 后的子进程变为僵尸进程。

image

预防

结合其产生的原因,在父进程中使用 wait 回收子进程残留的进程资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// gcc -o zombie zombie.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
pid_t pid;

// 创建子进程
pid = fork();
if (pid < 0) {
// fork失败
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程
printf("Child process with PID: %d\n", getpid());
// 子进程立即退出
exit(EXIT_SUCCESS);
} else {
// 父进程
printf("Parent process with PID: %d, Child PID: %d\n", getpid(), pid);

// 父进程等待子进程结束
int status;
pid_t wpid = wait(&status);
if (wpid == -1) {
perror("wait");
exit(EXIT_FAILURE);
}

// 检查子进程的退出状态
if (WIFEXITED(status)) {
printf("Child process exited with status %d\n", WEXITSTATUS(status));
} else {
printf("Child process terminated by signal %d\n", WTERMSIG(status));
}
}

// 父进程继续执行其他任务,例如睡眠一段时间
printf("Parent process continue do something\n");
sleep(10);

return 0;
}

image

当一个子进程结束时,内核会向其父进程发送SIGCHLD信号。父进程可以通过捕获这个信号并在信号处理函数中调用wait()或waitpid()来回收子进程的资源,从而避免僵尸进程的产生。
这种方式也优雅一些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// gcc -o zombie zombie.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

// 信号处理函数
void sigchld_handler(int signum) {
// 这里可以添加一些日志记录或其他处理
printf("SIGCHLD signal received, cleaning up child processes.\n");
// 等待所有子进程结束
while (waitpid(-1, NULL, WNOHANG) > 0); // WNOHANG选项使得waitpid不会阻塞
}

int main() {
// 设置信号处理函数
signal(SIGCHLD, sigchld_handler);

pid_t pid;
// 创建子进程
pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程
printf("Child process with PID: %d\n", getpid());
sleep(5); // 模拟子进程执行操作
exit(EXIT_SUCCESS);
} else {
// 父进程
printf("Parent process with PID: %d, Child PID: %d\n", getpid(), pid);

// 父进程继续执行其他任务
sleep(10);

// 父进程结束前,子进程可能已经结束,信号处理函数会处理SIGCHLD信号
}

return 0;
}
 Comments