Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5518011
  • 博文数量: 922
  • 博客积分: 19333
  • 博客等级: 上将
  • 技术积分: 11226
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-27 14:33
文章分类

全部博文(922)

文章存档

2023年(1)

2020年(2)

2019年(1)

2017年(1)

2016年(3)

2015年(10)

2014年(17)

2013年(49)

2012年(291)

2011年(266)

2010年(95)

2009年(54)

2008年(132)

分类: LINUX

2012-01-13 22:07:00

++++++APUE读书笔记-08进程控制(03)++++++
 
4、vfork
================================================
 vfork函数源于2.9BSD。有些人认为这个函数是多余的,但是本文讨论的系统都支持这个函数。实际上,BSD把这个函数从4.4BSD中删除了,但是所有从4.4BSD继承过来的开源BSD,又在它们的release中把这个函数添加进来了。vfork函数在Single UNIX Specification的version3中被标记成将被废弃的接口。
 函数vfork和fork一样,返回值也一样,但是这两个函数有所不同。
 vfork函数一般用于创建一个子进程,并且这个子进程的目的是进行exec(关于exec参见后面).
 vfork和fork一样,创建新的进程,并且它不会复制父进程的地址空间的内容到子进程中,因为子进程是将要调用exec的而不是引用父进程地址空间的数据。子进程会一直在父进程的地址空间运行,直到它调用了exit或者exec.这样的优化是为了提高一些用虚拟内存页实现的unix系统的效率。(通过前面对fork的解释我们可以发现,实际上fork现在已经使用了copy-on-write技术来提高效率,但是没有拷贝始终是要比有一些拷贝的快一些)
 fork和vfork另一个不同的地方是vfork保证了子进程先运行,直至子进程调用了exec或者exit.当子进程调用了exec或者exit的时候,父进程才重开始执行(如果子进程在调用这两个函数之前需要父进程的一些操作的话,这里可能会导致死锁)。
 总之,就两点不同:
 a)vfork的子进程不拷贝父进程空间数据而是直接运行在父进程空间内(直到调用了exec或exit)。fork的子进程拷贝一份父进程的数据。
 b)vfork之后,子进程会先继续运行,直到子进程调用exec或者exit父进程才开始运行。fork的子进程不能确定谁先继续运行。
 下面是使用vfork的一个例子:
 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 stdio */
     if ((pid = vfork()) < 0) {
         err_sys("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 = %d\n", getpid(), glob, var);
     exit(0);
 }
 从例子可以知道,子进程修改了变量,也反应到父进程中了,因为两者共享进程空间而不是拷贝关系;另外子进程会先于父进程运行。需要注意的地方是:
 我们结束子进程调用的是_exit而不是exit。因为,_exit不会做任何刷新标准输入/输出库的刷新操作;如果我们调用了exit那么,输出结果是不确定的,这依赖标准I/O库的实现,我们可能会看到和使用_exit没有什么区别,或者看到父进程printf的内容没有了。
 如果子进程调用了exit,这样会对标准I/O流进行flush操作。如果这是标准I/O库的唯一动作,那么我们不会看到和_exit有什么不同;但是如果也关闭了标准I/O流,那么内存中表示标准输出的FILE对象会被清空,由于子进程和父进程共享空间,所以当父进程再次继续运行并执行printf的时候,将会不输出任何东西,同时printf会返回-1。注意,这时候父进程的STDOUT_FILENO还是合法的,就像子进程获得了父进程的文件描述符的副本。
 大多数现代对exit的实现都不会自找麻烦地去关闭streams.因为如果进程将要退出的时候,内核会关闭所有在进程中打开的文件描述符号。如果在库中关闭他们,会增加额外的开销,没有一点好处。
参考:
 
 
阅读(501) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~