200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > fork()产生的子进程ppid有时不是父进程pid-Linux中的僵尸进程处理

fork()产生的子进程ppid有时不是父进程pid-Linux中的僵尸进程处理

时间:2022-08-03 23:43:41

相关推荐

fork()产生的子进程ppid有时不是父进程pid-Linux中的僵尸进程处理

文章首发及后续更新:https://mwhls.top/929.html

新的更新内容请到mwhls.top查看。

无图/无目录/格式错误/更多相关请到上方的文章首发页面查看。

写完之后,我想到了更严谨的验证方法,在文章尾部,

前面的问题类似于随机出现的问题,找到问题并解决,

后面的验证是将这个随机问题通过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;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。