sth about fork() in linux

回顾下fork相关的基本内容,这个应该是各种鄙视面试常爱考的知识点了,fork本身来说,也是个神奇的东西。。。

关于fork以及进程相关

找工作的相关题目中好多是对于fork的考察,感觉自己有些基础支持还是掌握的不牢靠。

具体的问题来源于一个典型的面试题,大概是问 fork||fork 操作会生成几个新的进程
类似的面试题,也有,比如更复杂一点的情况,参考这个:

再简单回顾一下fork

fork主要的功能就是说,把已有的一个进程复制一个出来,这两个新的进程几乎完全是一样的。执行fork之后,生成的两个进程每个都会启动一个从同一位置开始执行的线程,执行到fork函数中的时候,子进程就会复制父进程的堆栈段,所以两个进程实际上都陷入在fork中,还没有执行完,这样的话,fork其实可能有两种不同的返回值,一个是原先的父进程的fork执行完之后的返回值,另一个是新生成的子进程的fork得到的返回值。
当然两种返回值是不同的,这也是fork相关题目的一个考点:

  • 父进程中,fork函数的返回值是子进程的ID
  • 子进程中,fork函数的返回值是0
  • 如果出错,则返回最值是-1
    由于在c++里面,bool的形式由0以及>0的数字来表示,这里往往喜欢和||以及&&操作放在一起考察,注意公司里的那些题目总是爱考察&&与||的短路功能,对于&&来说,当condition1为false,直接返回false,对于||当condition1为true直接返回true。

可以这样理解,父进程实质上没变,多出来的子进程复制了父进程的堆栈,父进程要对其有一个引用,就像一个链表一样,因此就返回了子进程的id,而子进程没有新生成的进程可以引用,就返回了0。系统就是根据返回值来区别到底是父进程还是子进程的。

实际上在调度的时候也无法知道当前到底是父进程还是子进程,这个需要探讨更底层的实现,因此,通常用fork函数的返回值来判断到底当前是父进程还是子进程,之后再执行对应的操作。

关于fork还有一个常见的考点:fork之后的进程会保留父进程的那些特性?说白了就是明确新fork出来的进程与原来的父进程哪些相同,哪些不相同。

使用fork函数得到的子进程从父进程中继承了整个进程的地址空间,包括:进程上下文,进程堆栈,内存信息,打开文件的描述符,信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等等。
子进程与父进程的区别(区别要牢记):新fork出来的进程有自己的数据空间,子进程的ID和父进程不同,由fork所生成的子进程不能继承父进程所设置的文件锁。

再回顾下那个题目

就是先前列出的博客里的

第一次fork 生成了一个新的进程,此时有两个进程

第二次 fork && fork || fork 按照博客里的图,每一个最上端的父进程会生成了四个新的进程(中间有好几次是新生成的子进程又充当了父进程),开始的时候有,经过第一次的操作,有两个进程,它们分别作为最顶端的父进程,这样就生成了4+4=8个新的进程

第三次 又fork 经过了前两步的操作 已经有10个进程了 每个进程执行一次fork会新生成一个新的进程 这样就又生成了10个新的进程 于是整个过程就一共产生了 10+8+1=19 个新的进程

再这个基础上,单纯的 fork || fork 操作应该是新生成了两个进程 condition1||confition2逻辑或操作,如果condition1为true,结果返回true,如果condition1为false,同时condition2为true,则返回true,condition2为false则返回false。

用图片的方式表示比较直接,可以很明显的看出来,算上本身的主进程,程序在运行过程中一共创建了3个进程:

插入图片(todo)

相关参考资料

(这个来龙去脉讲的比较透彻)http://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940154.html

关于僵尸进程与孤儿进程

linux提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。

  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
  • 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
    僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程,所以,解决方法就是kill那个父进程,于是僵尸进程就可以被init进程接收,释放。

试题中僵尸进程会被 init 进程接管,不会造成资源浪费;这种说法明显是错的,因为僵尸进程的父进程仍然是存在的,这里主要的考察点是僵尸进程和孤儿进程的来源。

推荐文章