`
v5qqbrowser
  • 浏览: 356661 次
文章分类
社区版块
存档分类
最新评论

Linux fork的运行机制

 
阅读更多
从一道面试题谈linux下fork的运行机制
今天一位朋友去一个不错的外企面试linux开发职位,面试官出了一个如下的题目:

<wbr><wbr><wbr><wbr><wbr><span style="color:#08080;">给出如下C程序,在linux下使用gcc编译:</span></wbr></wbr></wbr></wbr></wbr>

#include "stdio.h"
#include "sys/types.h"
 #include "unistd.h"
 
  int main()
 {
 pid_t pid1;
 pid_t pid2;
 
 pid1 = fork();
 pid2 = fork();
 
 printf("pid1:%d, pid2:%d\n", pid1, pid2);
}

<wbr><wbr><wbr><wbr><wbr><span style="color:#08080;">要求如下:</span></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 已知从这个程序执行到这个程序的所有进程结束这个时间段内,没有其它新进程执行。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 1、请说出执行这个程序后,将一共运行几个进程。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 2、如果其中一个进程的输出结果是“pid1:1001, pid2:1002”,写出其他进程的输出结果(不考虑进程执行顺序)。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 明显这道题的目的是考察linux下fork的执行机制。下面我们通过分析这个题目,谈谈linux下fork的运行机制。</wbr></wbr></wbr></wbr></wbr>

预备知识

<wbr><wbr><wbr><wbr><wbr> 这里先列出一些必要的预备知识,对linux下进程机制比较熟悉的朋友可以略过。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 1、进程可以看做程序的一次执行过程。在linux下,每个进程有唯一的PID标识进程。PID是一个从1到32768的正整数,其中1一般是特殊进程init,其它进程从2开始依次编号。当用完32768后,从2重新开始。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 2、linux中有一个叫进程表的结构用来存储当前正在运行的进程。可以使用“ps aux”命令查看所有正在运行的进程。</wbr></wbr></wbr></wbr></wbr>

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

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

解题的关键

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

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

<wbr><wbr><wbr><wbr><wbr> step1、设由shell直接执行程序,生成了进程P。P执行完Part. A的所有代码。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> step2、当执行到pid = fork();时,P启动一个进程Q,Q是P的子进程,和P是同一个程序的进程。Q继承P的所有变量、环境变量、程序计数器的<span style="color:#ff00;"><strong>当前</strong></span>值。</wbr></wbr></wbr></wbr></wbr>

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

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

<wbr><wbr><wbr><wbr><wbr> 这里有三个点非常关键:</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><span style="color:#ff990;"><strong><span style="color:#ff00;">1、P执行了所有程序,而Q只执行了Part. B,即fork()后面的程序。(这是因为Q继承了P的PC-程序计数器)</span></strong></span></wbr></wbr></wbr></wbr></wbr>

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

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

解题

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

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

<wbr><wbr><wbr><wbr><wbr> 2、当执行到pid1 = fork();时,P0启动一个子进程P1,由题目知P1的PID为1001。我们暂且不管P1。</wbr></wbr></wbr></wbr></wbr>

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

<wbr><wbr><wbr><wbr><wbr> 4、P0中的第二个fork返回1002给pid2,继续执行完后续程序,结束。所以,P0的结果为“pid1:1001, pid2:1002”。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 5、再看P2,P2生成时,P0中pid1=1001,所以P2中pid1继承P0的1001,而作为子进程pid2=0。P2从第二个fork后开始执行,结束后输出“pid1:1001, pid2:0”。</wbr></wbr></wbr></wbr></wbr>

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

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

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

<wbr><wbr><wbr><wbr><wbr> 9、至此,整个执行过程完毕。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 所得答案:</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><span style="color:#339966;">1、一共执行了四个进程。(P0, P1, P2, P3)</span></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr>2、另外几个进程的输出分别为:</wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr>pid1:1001, pid2:0</wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> pid1:0, pid2:1003</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> pid1:0, pid2:0</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 进一步可以给出一个以P0为根的进程树:</wbr></wbr></wbr></wbr></wbr>

验证

<wbr><wbr><wbr><wbr><wbr> 下面我们去linux下实际执行这个程序,来验证我们的答案。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 程序如下图:<img src="http://hi.csdn.net/attachment/201107/11/0_1310372368FrrM.gif" alt=""></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 用gcc编译、执行后结果如下:</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 由于我们不太可能刚巧碰上PID分配到1001的情况,所以具体数值可能和答案有所差别。不过将这里的2710看做基数的话,结果和我们上面的解答是一致的。</wbr></wbr></wbr></wbr></wbr>

总结

<wbr><wbr><wbr><wbr><wbr> 应该说这不是一道特别难或特别刁钻的题目,但是由于fork函数运行机制的复杂性,造就了当两个fork并排时,问题就变得很复杂。解这个题的关键,一是要对linux下进程的机制有一定认识,二是抓住上文提到的几个关于fork的关键点。朋友说,这个题给的时间是5分钟,应该说时间还算充裕,但是在面试的场合下,还是很考验一个人对进程、fork的掌握程度和现场推理能力。</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> 希望本文能帮助朋友们对fork的执行机制有一个明晰的认识。</wbr></wbr></wbr></wbr></wbr>

分享到:
评论

相关推荐

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

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

    linux下fork的运行机制参考.pdf

    linux下fork的运行机制参考.pdf

    linux下用fork()函数实现多进程调用带来的一些思考

    在文档中针对一事例程序进行了分析讲解。...并将程序运行后输出到屏幕结果与输出重定向到文件后的结果进行比较分析,给出了存在差异的原因,这里主要涉及到了printf()输出缓冲机制问题。并在分析基础上给出了解决办法。

    Linux 进程控制与进程互斥(附源代码)(附实验报告)

    1、利用fork函数创建新进程,并根据fork函数的返回值,判断自己是处于父进程还是子进程中;...4、利用信号量机制控制多线程的运行顺序,并实现多线程中数据的共享; 5、分析Linux系统下多进程与多线程中的区别。

    清华大学Linux操作系统原理与应用

    2.3.5 Linux中的分页机制 30 2.4 Linux中的汇编语言 31 2.4.1 AT&T与Intel汇编语言的比较 31 2.4.2 AT&T汇编语言的相关知识 32 2.5 Linux系统地址映射示例 33 习题2 35 第3章 进程 37 3.1 进程介绍 37 3.1.1 程序和...

    linux操作系统内核技术-uestc课件

     2熟悉进程描述符的组织,进程上下文和进程状态转换,和fork,exec,wait,exit,clone,linux线程和内核线程的实现原理和应用。了解COW和避免出现孤儿进程技术。(4小时)  3介绍支持SMP的O(1)调度,用户和内核...

    Linux内核反向映射机制的详细资料说明

    为了系统的安全性,Linux内核将各个用户进程运行在各自独立的虚拟地址空间,用户进程之间通过虚拟地址空间相互隔离,不能相互访问,一个进程的奔溃不会影响到整个系统的异常也不会干扰到系统以及其他进程运行。Linux...

    浅析如何在c语言中调用Linux脚本

    一、引言对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执行一次却返回两个值。fork函数是Unix系统最杰出的成就之一,它是七十年代UNIX早期的开发者经过长期在理论和实践上的艰苦探索后...

    操作系统课程实验.rar

    Linux 是开源操作系统,用户可以根据自身系统需要裁剪、修改内核,定制出功能更加 合适、运行效率更高的系统,因此,编译 linux 内核是进行内核开发的必要基本功。 在系统中根据需要添加新的系统调用是修改内核的一...

    详解linux下避免僵尸进程的几种方法

    linux下我们可以调用fork函数创建子进程,创建的子进程将会得到父进程的数据空间、堆、栈……副本(采用写时复制机制),子进程将会继承父进程的信号掩码、信号处理方式、当前工作目录、会话id、组id……。当子进程...

    Java-Programmer-Practice:Java工程师成长之路,包含JDK源码解析、Java并发编程、JVM实例解析、Spring全家桶、Spring Cloud以及各类中间件代码实例与教程。欢迎fork、star

    Java工程师成长之路 本项目致力于为Java后端...Linux的七个运行级别 Linux进程启动顺序 深入理解Linux下的守护进程 理解Linux配置文件 下一阶段更新计划 增加JDK源码的注释 + 教程 加入SpringCloud的教程 Fork & Star

    操作系统实验报告.docx

    1、以Linux系统进程和线程机制为背景,掌握fork()和clone()系统调用的形式和功能,以及与其相适应的高级通讯方式。由fork派生的子进程之间通过pipe通讯,由clone创建的线程之间通过共享内存通讯,对于后者需要考虑...

    linux系统编程之线程.zip

    参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。 参数4:线程主函数执行期间所使用的参数。 在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下...

    【嵌入式软件工程师面经】Linux系统编程(线程进程).pdf

    如果进程间需要通信(IPC,进程间通信),必须使用操作系统提供的机制,如管道、信号量、共享内存、消息队列等。 在多任务操作系统中,多个进程可以同时运行,操作系统的调度器负责管理这些进程的执行,给它们分配...

    一个进程池的服务器程序

    if (write_pid() ) //避免同时有多个该程序在运行 return -1; if (pipe(fd1) ) { perror("pipe failed"); exit(-1); } if (s_pipe(fd2) ) { perror("pipe failed"); exit(-1); } int port = atoi(argv...

    华中科技大学操作系统实验报告.docx

    3. 掌握Linux系统中编辑、编译、调试、运行一个C语言程序的全过程。 二.实验内容 1、实验要求 1. 在VMWare虚拟机环境或真实物理机器上,安装一个Linux操作系统。 2. 体验Linux操作系统中X Windows系统的使用。 3. ...

    linux网路编程 中文 23M 版

    第1 章Linux操作系统概述................... .......................................................................... 2 1.1 Linux发展历史........................................................ 2 ...

    3操作系统实验.doc

    实验一:信号机制 实验目的 1、了解什么是信号 2、熟悉LINUX系统中进程之间软中断通信的基本原理 实验内容 1、编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键...

Global site tag (gtag.js) - Google Analytics