从一道面试题谈linux下fork的运行机制

今天一位朋友去一个不错的外企面试linux开发职位,面试官出了一个以下的题目:
linux

给出以下C程序,在linux下使用gcc编译:面试


#include "stdio.h"shell

#include "sys/types.h"函数

#include "unistd.h"3d


int main()cdn

{blog

pid_t pid1;继承

pid_t pid2;进程


pid1 = fork();开发

pid2 = fork();


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

}

要求以下:

已知从这个程序执行到这个程序的全部进程结束这个时间段内,没有其它新进程执行。

一、请说出执行这个程序后,将一共运行几个进程。

二、若是其中一个进程的输出结果是“pid1:1001, pid2:1002”,写出其余进程的输出结果(不考虑进程执行顺序)。

明显这道题的目的是考察linux下fork的执行机制。下面咱们经过分析这个题目,谈谈linux下fork的运行机制。

预备知识

这里先列出一些必要的预备知识,对linux下进程机制比较熟悉的朋友能够略过。

一、进程能够看作程序的一次执行过程。在linux下,每一个进程有惟一的PID标识进程。PID是一个从1到32768的正整数,其中1通常是特殊进程init,其它进程从2开始依次编号。当用完32768后,从2从新开始。

二、linux中有一个叫进程表的结构用来存储当前正在运行的进程。可使用“ps aux”命令查看全部正在运行的进程。

三、进程在linux中呈树状结构,init为根节点,其它进程均有父进程,某进程的父进程就是启动这个进程的进程,这个进程叫作父进程的子进程。

四、fork的做用是复制一个与当前进程同样的进程。新进程的全部数据(变量、环境变量、程序计数器等)数值都和原进程一致,可是是一个全新的进程,并做为原进程的子进程。

解题的关键

有了上面的预备知识,咱们再来看看解题的关键。我认为,解题的关键就是要认识到fork将程序切成两段。看下图:


上图表示一个含有fork的程序,而fork语句能够当作将程序切为A、B两个部分。而后整个程序会以下运行:

step一、设由shell直接执行程序,生成了进程P。P执行完Part. A的全部代码。

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

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

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

这里有三个点很是关键:

一、P执行了全部程序,而Q只执行了Part. B,即fork()后面的程序。(这是由于Q继承了P的PC-程序计数器)

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

三、P中fork()语句启动子进程Q,并将Q的PID返回,而Q中的fork()语句不启动新进程,仅将0返回。

解题

下面利用上文阐述的知识进行解题。这里我把两个问题放在一块儿进行分析。

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

二、当执行到pid1 = fork();时,P0启动一个子进程P1,由题目知P1的PID为1001。咱们暂且无论P1。

三、P0中的fork返回1001给pid1,继续执行到pid2 = fork();,此时启动另外一个新进程,设为P2,由题目知P2的PID为1002。一样暂且无论P2。

四、P0中的第二个fork返回1002给pid2,继续执行完后续程序,结束。因此,P0的结果为“pid1:1001, pid2:1002”。

五、再看P2,P2生成时,P0中pid1=1001,因此P2中pid1继承P0的1001,而做为子进程pid2=0。P2从第二个fork后开始执行,结束后输出“pid1:1001, pid2:0”。

六、接着看P1,P1中第一条fork返回0给pid1,而后接着执行后面的语句。然后面接着的语句是pid2 = fork();执行到这里,P1又产生了一个新进程,设为P3。先无论P3。

七、P1中第二条fork将P3的PID返回给pid2,由预备知识知P3的PID为1003,因此P1的pid2=1003。P1继续执行后续程序,结束,输出“pid1:0, pid2:1003”。

八、P3做为P1的子进程,继承P1中pid1=0,而且第二条fork将0返回给pid2,因此P3最后输出“pid1:0, pid2:0”。

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

所得答案:

一、一共执行了四个进程。(P0, P1, P2, P3)

二、另外几个进程的输出分别为:

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的执行机制有一个明晰的认识。

相关文章
相关标签/搜索