孤儿进程
产生原因
父进程先于子进程退出,子进程就变了孤儿进程
代码模拟产生孤儿进程
当父进程退出的时候,子进程依旧在循环中,这种父进程先于子进程退出的,子进程就变成了孤儿进程。
我们来看一下代码执行的效果:
我们来看一下这个父子进程的关系:
6610号进程由1068号进程创建,6611号进程由6610号进程创建,那我们是如何让创建出6610号进程的呢?
我们解释一下在命令行当中启动一个进程的原理:命令行解释器的进程,创建出子进程,让子进程进行进程程序替换,替换为目标程序。为什么要进行程序替换呢?因为父进程在调用fork函数创建出来的子进程,其代码段是和父进程完全相同的,当我们需要子进程去执行和父进程不同的功能,就需要进行进程程序替换,将子进程的代码段替换为目标程序。
此时,6611号进程的父进程是6610号进程,也就是把它创建出来的那个进程,但是我们来看看下面循环打印的语句:
下面是一直在循环打印的,趁着循环的时候我们来看一下进程信息:
已经找不到父进程了,说明父进程已经退出了。这个时候我们的子进程就变成了孤儿进程。也就是说,上面一直循环打印那句话的进程是孤儿进程打印出来的。
我们仔细观察,发现此时打印的ppid是1,说明子进程此时的父进程不是创建它的6610号进程,而是1号进程。说明没有“亲生父亲”的子进程会被1号进程“领养”。
一号进程
那什么是1号进程呢?
1号进程:操作系统的init进程,操作系统很多进程都是由1号进程创建出来的。
孤儿进程的父进程变成了1号进程,同时我们也知道,子进程退出的时候,由父进程回收子进程的退出状态信息,此时孤儿进程被1号进程领养后,退出的状态信息由1号进程进行回收。
前台进程、后台进程
当orphan的子进程在运行的时候,我们在命令行解释器中输入其他命令,也可以执行,这是为什么呢?
我们来看一下此时orphan的子进程的进程状态:
我们现在来创建一个新的进程,举个例子,来看看进程运行的过程什么情况下不能在命令行中输入命令,创建新的进程。
程序代码:
我们看一下执行结果:
在程序循环执行打印的时候,我们输入其他命令是完全不起作用的,为什么呢?
我们来看一下程序的进程状态信息:
对比一下上面的和下面的进程状态信息最大的不一样就是多了一个“+”号,带有“+”号的是前台进程,不带“+”号的是后台进程。
前台进程:阻塞bash运行的进程称为前台进程,反之,不阻塞的称为后台进程。
联系到我们上面提到的在命令行启动一个进程的原理,前台进程阻塞了bash运行,bash就是命令行解释器的进程,命令行解释器的进程被阻塞,bash就不能创建新的子进程,不阻塞的话,在命令行解释器输入命令的时候,bash就能先fork一个子进程出来,再进行进程程序替换,就能执行新的命令了。
所以我们就能明白上面两个例子了,第一个例子是因为bash的创建的子进程退出了,且这个子进程创建出来的子进程变成了孤儿进程,同时也是后台进程,所以就没有能阻塞bash的进程了,bash就能解释新的命令了;第二个例子bash创建出来的子进程一直在循环中,一直是前台进程,bash被阻塞,就不能去创建新的进程,不能解释新的命令了。
我们给出一个结论:在命令行当中运行的进程,一般情况下都是要阻塞bash进程的运行的。
孤儿进程有危害吗?
首先给出结论:孤儿进程没有任何危害,1号进程会回收孤儿进程的退出状态信息。
给出一段代码来验证一下:
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 pid_t ret = fork();
6 if (ret < 0){
7 return 0;
8 }else if(ret == 0){
9 //child
10 int count = 10;
11 while(count--){
12 printf("I am child,pid is %d,ppid is %d\n",getpid(),getppid());
13 sleep(2);
14 }
15 }else{
16 printf("I am father,pid is %d,ppid is %d\n",getpid(),getppid());
17 }
18 return 0;
19 }
~
看一下运行结果:
在25712号进程运行的时候查看进程状态信息:
这个时候,创建25712的父进程25711已经退出了,所以我们的25712号进程的父进程变成了1号进程。
孤儿进程执行完之后,如果它的退出状态信息没有人回收,就可能变成僵尸进程,我们来看一下它结束之后的进程状态信息:
很明显,孤儿进程没有变成僵尸进程,它的退出状态信息被1号进程回收了,所以我们说孤儿进程是没有危害的。