进程控制之vfork函数
vfork函数的调用序列和返回值与fork相同,但两者的语义不同。
vfork用于创建一个新进程,而新进程的目的是exec一个新程序。vfork和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),于是也就不会存访该地址空间。相反,在子进程调用exec或exit之前,它在父进程的空间中运行。
vfork和fork之间的另一个区别是:vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。)
程序清单8-2 vfork函数实例
[root@localhost apue]# cat prog8-2.c #include "apue.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 vfork\n"); /* we don't flush stdout */ if((pid = vfork()) < 0) { err_sys("vfork error"); } else if(pid == 0) /* child */ { glob++; /* modify variables */ var++; _exit(0); /* child terminates */ } /* * Parent continues here. */ printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0); }
运行该程序得到:
[root@localhost apue]# ./prog8-2 before vfork pid = 15792, glob = 7, var = 89
子进程最变量glob和var做增1操作,结果改变了父进程中的变量值。因为子进程在父进程的地址空间中运行。
注意,在程序清单8-2中,调用了_exit而不是exit。_exit并不执行标准I/O缓冲的冲洗操作。如果调用的是exit而不是_exit,则该程序的输出是不确定的。它依赖于标准I/O库的实现,我们可能会见到输出没有发生变化,或者发现没有出现父进程的printf输出。
如果子进程调用exit,而exit的实现只是冲洗所有标准I/O流,那么我们会见到输出与子进程调用_exit所产生的输出完全相同,没有任何区别。如果exit的实现除了冲洗所有标准I/O流之外,还关闭标准I/O流,那么表示标准输出FILE对象的相关存储区将被清0。因为子进程借用了父进程的地址空间,所以当父进程恢复运行并调用printf时,也就不会产生任何输出,printf返回-1。注意,父进程的STDOUT_FILENO仍旧有效,子进程得到的是父进程的文件描述符数组的副本。
本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。