8.4 vfork函数-进程控制

8.4 vfork函数进程控制

vfork函数的调用序列和返回值与fork相同,但两者的语义不同。

vfork用于创建一个新进程,而该新进程的目的是exec一个新程序(如上节(2) 中一样)。程序1 – 5中的shell基本部分就是这种类型程序的一个例子。vforkfork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec (exit ),于是也就不会存访该地址空间。不过在子进程调用execexit之前,它在父进程的空间中运行。这种工作方式在某些UNIX的页式虚存实现中提高了效率(与上节中提及的,在fork之后跟随exec,并采用在写时复制技术相类似)。

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

实例

在程序8 – 1中使用vfork代替fork,并做其他相应修改得到程序8 – 2。删除了对于标准输出的write调用,另外,我们也不再需要让父进程调用sleepvfork已保证在子进程调用execexit之前,内核会使得父进程处于休眠状态。

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>



int glob = 6; /* external variable in initialized data */



int main(void)

{

int var; /* automatic variable on the stack */

pid_t pid;



var = 88;

printf("before vforkn"); /* we don't flush stdio */

if ((pid = vfork()) < 0) {

perror("vfork error");

} else if (pid == 0) { /* child */

glob++; /* modify parent's variables */

var++;

_exit(0); /* child terminates */

}

/*

* Parent continues here.

*/

printf("pid = %d, glob = %d, var = %dn", getpid(), glob, var);

exit(0);

}

运行该程序得到:

$ a.out

before vfork

pid = 2777, glob = 7, var = 89

子进程对变量globvar做增1操作,结果改变了父进程中的变量值因为子进程在父进程的地址空间中运行,所以这并不令人惊讶。但是其作用的确与fork不同。

注意,在程序8 – 2中,调用了_ exit而不是exit。正如8 . 5节所述,_ exit并不执行标准I / O缓存的刷新操作。如果用exit而不是_ exit,则该程序的输出是不确定的。它依赖于标准IO库的实现。可能什么也不输出:

$ a.out

before vfork

从中可见,父进程printf的输出消失了。其原因是子进程调用了exit,它刷新开关闭了所有标准I / O流,这包括标准输出。虽然这是由子进程执行的,但却是在父进程的地址空间中进行的,所以所有受到影响的标准I/O FILE对象都是在父进程中的。当父进程调用printf时,标准输出已被关闭了,于是printf返回– 1