fork()产生的子进程ppid有时不是父进程pid-Linux中的僵尸进程处理
写完之后,我想到了更严谨的验证方法,在文章尾部,
前面的问题类似于随机出现的问题,找到问题并解决,
后面的验证是将这个随机问题通过sleep()函数复现,佐证前文结论。
概述
父进程若先于子进程结束,子进程变成僵尸进程,
此时linux会将子进程分配给init进程,令其作为新的父进程结束子进程,
可在代码最后加上wait()函数,令父进程等待子进程消亡。
问题描述
在使用fork()函数产生子进程时,有时子进程的ppid是父进程的pid,有时又不是,是什么导致的?
如下图,第二次运行这段程序时,子进程的ppid不再是父进程,但父进程的ppid却始终是终端pid
五次运行结果不同:
ps命令显示终端pid:
ps命令显示pid为1693的进程:
fork()与进程产生相关知识
首先要了解进程是如何产生的,
在linux中,进程直接是父子关系,父进程有子进程的控制权,
在windows中,父子关系被句柄代替,句柄可传递,感兴趣可看这篇:操作系统原理学习笔记(六)-进程控制
linux的所有进程,都有一个父进程,
一般认为pid=1的init进程作为优先级最高的进程,并由其创建其他用户进程。
进程的pid为其进程唯一标识符,ppid则是其父进程的pid。
其次,要了解fork()函数的作用,
fork()函数可以拷贝当前进程的数据空间,赋予新生成的子进程,
fork()出的子进程的ppid是父进程的pid。
三个问题
为什么在fork()的子进程的ppid有时不是父进程的pid,而变成了systemd的pid?
systemd在这里的作用是什么?
如何防止这种僵尸进程的出现?
linux对僵尸进程的处理
实际上,出现这种情况,是因为父进程先于子进程结束,
此时,子进程并未终止,父进程结束,子进程就变成了僵尸进程。
僵尸进程是无法关闭的,因为其没有父进程控制,
但linux将僵尸进程分配给init进程,就解决了这个问题,
在ubuntu20.04的linux系统中,使用了systemd来完成部分init进程的工作,
也就出现了僵尸进程被分配给systemd进程的情况了,
systemd在这里的作用,就是结束这个子进程。
如何防止父进程先于子进程终止?
在代码末尾使用wait()函数,
wait()函数,可以让该进程等待,直到所有子进程消亡,才继续运行。
问题参考代码
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
int main(){
int pid = fork();
if(pid < 0) {
printf("error in fork()\n");
}
else if(pid == 0){
printf("child process pid is %d, ppid is %d\n", getpid(), getppid());
}
else {
printf("parent process pid is %d, ppid is %d\n", getpid(), getppid());
//wait(NULL); //当加入这行代码,父进程就不会先于子进程结束。
}
return 0;
}
wait()效果
将wait()函数加入后,运行10次该程序,不再出现上述问题。
更加严谨的验证:通过sleep()函数
在上方代码中,去除了wait()函数,
并在pid==0的子进程代码块末尾,添加了两行代码:sleep(1);
printf("\nafter sleep(1), child pid is: %d, ppid is %d\n", getpid(), getppid());
修改后效果如下图
可以看出,图中1、3、4、5四次运行中,
在sleep(1)之前的child ppid都是父进程pid,即此时父进程未先于子进程结束。
而在第2次运行中,
sleep(1)前子进程的ppid不是父进程pid,此时父进程先于子进程结束。
这里就是前文提到的随机出现的情况。
而在使用sleep(1)后,问题可以有效复现了,
可以看出,在使用sleep(1)后,child ppid每次都是1776,而不是父进程pid
1776是前文提到的systemd进程,即init进程。
如果此时加入wait()后,child ppid会显示正确,则表示验证了前文的观点:
父进程可能先于子进程结束,成为僵尸进程的子进程会被分配给init进程,如果在父进程中使用wait()函数,可防止该情况的发生。
可以看出,加上wait()后,
经过sleep(1)的子程序的ppid显示正确。
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
int main(){
int pid = fork();
if(pid < 0) {
printf("error in fork()\n");
}
else if(pid == 0){
printf("child process pid is %d, ppid is %d\n", getpid(), getppid());
sleep(1);
printf("\nafter sleep(1), child pid is: %d, ppid is %d\n", getpid(), getppid());
}
else {
printf("parent process pid is %d, ppid is %d\n", getpid(), getppid());
//wait(NULL); //当加入这行代码,父进程就不会先于子进程结束。
}
return 0;
}
共有 0 条评论