fork函数分析二

一个简单的例子:

#include "stdio.h"
#include "sys/types.h"
#include "unistd.h"

int main()
{
        pid_t pid1,pid2;

        pid1 = fork();
        pid2 = fork();

        printf("pid1:%d,pid2:%d\n",pid1,pid2);
}
                                                                                                                                            

  已知从这个程序执行到这个程序的所有进程结束这个时间段内,没有其他新进程执行,

    问:1.请说出执行这个程序后,将一共运行几个进程?

            2.如果其中一个进程的输出结果是"pid1:1001,pid2:1002",写出其他进程的输出结果,不考虑进程执行顺序

预备知识:

    1.所谓进程可以看成是程序的一次执行过程,比如浏览器程序,如果不打开浏览器,是不会有进程的,一旦打开浏览器,就创造了1个浏览器进程,当然,可以打开多个浏览器,实现多个进程执行,在linux中,每个进程都有一个唯一的PID(process ID)标识,PID是一个从1到32678的正整数,其中1一般是特殊进程init,其他进程从2开始依次编号,当用完32768后,从2重新开始。

    2.Linux下有一个叫做进程表的结构用来存储当前正在运行的进程,可以用 ps aux 命令查看所有正在运行的进程。

    3.进程在linux中呈树状结构,init为根节点,其他进程均有父进程,某进程的父进程就是启动这个进程的进程,这个进程叫做父进程的子进程。

    4.fork的作用是复制一个与当前进程一样的进程,新进程所有的数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是却是一个全新的进程,并作为原进程的子进程。

    5.fork有两次返回,或者认为有两个返回值,这一点尤为重要,就是这两次不同的返回值,才能实现切换的,对于父进程,会返回子进程的pid值(>1),对于子进程,则返回0值。


有了上面的预备知识,我们再看看上面程序理解的关键:需要认识到fork有两次返回,要认识到fork将程序切呈了2段,如下图所示:

上图表示一个含有fork的程序,而fork语句可以看成将程序切为A、B两个部分,然后整个程序就会如下运行:

    step1:设由shell直接执行程序,生成了进行P,P执行玩Part.A的所有代码。

    step2:  当执行到pid = fork();时,P启动了一个进程Q,Q是P的子进程,和P是同一个程序的进程,Q继承了P的所有变量、环境变量、程序计数器的当前值。

    step3、在P进程中,fork()将Q的PID返回给变量pid,并继续执行Part. B的代码。

    step4、在进程Q中,将0赋给pid,并继续执行Part. B的代码。

这里有3个关键点:

1、P执行了所有程序,而Q只执行了Part. B,即fork()后面的程序。(这是因为Q继承了P的PC-程序计数器)

2、Q继承了fork()语句执行是当前的环境,而不是程序的初始环境。

3、P中fork()语句启动子进程Q,并将Q的PID返回,而Q中的fork()语句不启动新进程,仅将0返回,有点绕,这个就是所谓的2次返回动作。

利用上面的知识点,我们回到开始的那个例程序,我们把2个问题放到一起进行分析:

1、从shell中执行此程序,启动了一个进程,我们设这个进程为P0,设其PID为XXX(解题过程不需知道其PID)。

2、当执行到pid1 = fork();时,P0启动了一个子进程P1,由题目知P1的PID为1001,我们赞帖不管P1.

3、P0中的fork返回1001给pid1,继续执行pid2 = fork();,此时启动了另一个新进程,设为P2,由题目可知P2的PID为1002,同样的暂且不管P2。

4、P0中的第二个fork返回1002给pid2,继续执行后续的程序,结束,所以P0的结果为"pid1:1001,pid2:1002"

5、再看P2,P2生成时,P0中的PID1=1001,所以P2中的pid1继承了P0的1001,而作为子进程的pid2 = 0;P2从第2个fork后开始执行,结束后输出"pid1:1001,pid2:0".

6、接着看P1,P1中第1条fork返回0给pid1,然后接着执行后面的语句,而后面接着的语句是pid2 = fork();执行到这里P1又陈胜一个新进程P3,暂且先不管P3.

7、P1中第2条fork将P3的PID返回给pid2,由预备知识可知P3的PID为1003,所以P1的pid2 = 1003,P1继续执行后续的程序,输出“pid1:0,pid2:1003”.

8、P3作为P1的子进程,继承P1中pid1=0,并且第二条fork将0返回给pid2,所以P3最后输出“pid1:0, pid2:0”。

9、至此,整个执行过程完毕。

所得答案:

1、一共执行了4个进程。

2.另外几个进程的输出分别为:

pid1:1001,pid2:0

pid1:0,pid2:1003

pid1:0,pid2:0

进一步可以给出一个以P0为根的进程树,如下图所示:

总结:题目比较绕,但是理解的关键还是fork函数有2次返回,一次是生成新进程,对于父进程执行fork,然后再接着运行下面的代码,而子进程的运行是从fork的下一条语句开始执行代码,等于说fork代码下面的语句会被执行2遍。

猜你喜欢

转载自blog.csdn.net/u012351051/article/details/80766771