红联Linux门户
Linux帮助

Linux基础知识:fork,vfork,exec

发布时间:2014-12-11 21:33:06来源:linux网站作者:woshizhouxiang

本文是涉及到fork,vfork,exec和进程通信,父子进程数据共享这几个方面的讨论。


第一点,

Linux中,创建进程的方式,只有一种,那就是调用fork(或者vfork)。 当然,系统的交换进程,init进程除外,它们是操作系统自举时用特殊方式创建的最初的进程。


第二点,

举个例子,父进程A 创建子进程B 后,进程B 就拥有了A 的所有数据(包括父进程的数据空间、堆和栈)的相同副本,并且共享代码片段(正文段)。父子进程的运行路线仅靠fork的返回值来区分。子进程从调用fork 之后的代码行继续执行,这点很重要。它意味着,之前的代码只有父进程会执行,fork之后的代码靠其返回值导向不同的流向。当然,如果fork 外部嵌套了结构控制语句,情况可能会更麻烦一点。这种情况下,父子进程可能仍有机会在跳出fork语句块之后,执行一段相同的代码。


第三点,

我们之所以用fork 调用,大多数情况是在子进程中调用exec 函数来启动另一个新的程序。即运行另一个可能是其他人所编写的程序(含有main函数的完整程序)。这时,子进程B 若调用了exec,那么就意味着进程B 被kill了,进程B 从进程A 那里继承的代码从调用exec 那行开始都无效,所有的数据也被清空。所以,在调用exec 后所编写的代码都是毫无意义的。唯一保留的是B的pid号,exec 所调用的那个程序会继承B 的pid 继续执行,直到结束。


第四点,

有了上面的这些概念,下面就开始说说vfork和父子进程的数据共享。这是我现在所关心的。首先,vfork产生的子进程共享父进程的所有地址空间(无论是正文段还是数据段),这就是程序中我使用了vfork的重要原因。因为,你要知道,若不是这样,父子进程之间的通信和数据共享的代码就又够我折腾一阵子了。不用vfork的话,我可能就不得不借助于管道通信,共享内存,乃至多线程的编写来解决这个问题。另外,vfork和fork之间的另一个区别是:vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。有一点要提醒的是,网上有人说,在Linux中vfork已经丧失了其功能,变得和fork功能一样,还好,在我实验的RedHat 和Debian版本中vfork 还保留着共享父进程数据的能力。(实际上,UNIX系统都还保留着两者的不同之处)


这篇文章是自己在编写一个Linux小程序时,遇到问题有感而发的。

vfork用于创建一个新进程,而该新进程的目的是exec一个新进程,vfork和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,不会复制页表。因为子进程会立即调用exec,于是也就不会存放该地址空间。不过在子进程中调用exec或exit之前,他在父进程的空间中运行。

为什么会有vfork,因为以前的fork当它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,而往往在子进程中会执行exec调用,这样,前面的拷贝工作就是白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与父进程共享地址空间(其实就是线程的概念了),因为这时候子进程在父进程的地址空间中运行,所以子进程不能进行写操作,并且在儿子“霸占”着老子的房子时候,要委屈老子一下了,让他在外面歇着(阻塞),一旦儿子执行了exec或者exit后,相当于儿子买了自己的房子了,这时候就相当于分家了。


vfork和fork之间的另一个区别是: vfork保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

由此可见,这个系统调用是用来启动一个新的应用程序。其次,子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。

为了避免这些问题,需要确保一旦调用vfork(),子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用exit函数。子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续。


通常,如果应用程序不是在fork()之后立即调用exec(),就有必要在fork()被替换成vfork()之前做仔细的检查。

用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序,当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行,因为调用exec并不创建新进程,所以前后的进程id 并未改变,exec只是用另一个新程序替换了当前进程的正文,数据,堆和栈段。