全部博文(584)
分类: LINUX
2010-09-02 12:01:05
上图表示一个含有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的代码。
这里有三个点非常关键:
1、P执行了所有程序,而Q只执行了Part. B,即fork()后面的程序。(这是因为Q继承了P的PC-程序计数器)
2、Q继承了fork()语句执行时当前的环境,而不是程序的初始环境。
3、P中fork()语句启动子进程Q,并将Q的PID返回,而Q中的fork()语句不启动新进程,仅将0返回。
解题
下面利用上文阐述的知识进行解题。这里我把两个问题放在一起进行分析。
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从第二个fork后开始 执行,结束后输出“pid1:1001, pid2:0”。
6、接着看P1,P1中第一条fork返回0给pid1,然后接着执行后面的语句。而后面接着的语句是pid2 = fork();执行到这里,P1又产生了一个新进程,设为P3。先不管P3。
7、P1中第二条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、一共执行了四个进程。(P0, P1, P2, P3)
2、另外几个进程的输出分别为:
pid1:1001, pid2:0
pid1:0, pid2:1003
pid1:0, pid2:0
进一步可以给出一个以P0为根的进程树:
验证
下面我们去linux下实际执行这个程序,来验证我们的答案。
程序如下图:
用gcc编译、执行后结果如下:
由于我们不太可能刚巧碰上PID分配到1001的情况,所以具体数值可能和答案有所差别。不过将这里的2710看做基数的话,结果和我们上面的解答是一 致的。
总结
应该说这不是一道特别难或特别刁钻的题目,但是由于fork函数运行机制的复杂性,造就了当两个fork并排时,问题就变得很复杂。解这个题的关键,一 是要对linux下进程的机制有一定认识,二是抓住上文提到的几个关于fork的关键点。朋友说,这个题给的时间是5分钟,应该说时间还算充裕,但是在面 试的场合下,还是很考验一个人对进程、fork的掌握程度和现场推理能力。
希望本文能帮助朋友们对fork的执行机制有一个明晰的认识。