如何正确地使用vfork():简析vfork()与fork()的不同

标签: 正确 vfork vfork | 发表时间:2014-11-24 00:53 | 作者:kxcfzyk
出处:http://blog.csdn.net

今天看到知乎上有人问了一个 由于不恰当的使用vfork()而导致的一个奇怪现象,底下的回答非常精彩。趁此机会我也仔细了解了一下vfork()的特性。


其实对vfork()最完备、权威的表述莫过于man手册里面的解释了。


简单的说,vfork()跟fork()类似,都是创建一个子进程,这两个函数的的返回值也具有相同的含义。但是vfork()创建的子进程基本上只能做一件事,那就是立即调用_exit()函数或者exec函数族成员, 调用任何其它函数(包括exit())、修改任何数据(除了保存vfork()返回值的那个变量)、执行任何其它语句(包括return)都是不应该的。此外,调用vfork()之后, 父进程会一直阻塞,直到子进程调用_exit()终止,或者调用exec函数族成员。


关于如何正确使用vfork(),上面这一段就是全部了。但是为什么vfork()会这样呢? 其实vfork()和fork()之间只有两点不同:

  1. fork()会复制父进程的页表,而vfork()不会复制,直接让子进程共用父进程的页表;
  2. fork()使用了写时复制技术,而vfork()没有,它任何时候都不会复制父进程地址空间。

即使算上vfork()会阻塞父进程而fork()不会,也只有三点不同,没有更多不同了。所以vfork()产生的子进程跟父进程完全共同使用同一个地址空间,甚至共享同一个函数堆栈!也就是子进程中对任何数据变量的修改,不管是局部的还是全局的,都会影响到父进程。而任何一个函数调用都会修改栈空间,这就是为什么vfork()的子进程不能随便调用别的函数。


但需要注意的是,由于vfork()毕竟还是产生一个新的进程,所以子进程拥有自己的进程描述符,拥有自己的寄存器,最重要的是,拥有自己的打开文件列表!


注意拥有自己的打开文件列表非常重要,因为如果子进程只是简单地共用父进程的打开文件列表,那么当子进程调用_exit()退出时,_exit()内部会自动关闭当前进程打开的所有文件描述符,也就是打开文件列表里面的文件,这将导致父进程恢复执行时,无法访问到自己之前已经打开过的文件,包括标准输入、标准输出和标准错误输出。所幸的是这永远不会发生,子进程会复制父进程的打开文件列表,并增加文件引用计数。


那为什么vfork()子进程中可以调用_exit(),却不可以调用exit(),也不可以直接return呢?


exit()是对_exit()的封装,它自己在调用_exit()前会做很多清理工作,其中包括刷新并关闭当前进程使用的流缓冲(比如stdio.h里面的printf等),由于vfork()的子进程完全共享了父进程地址空间,子进程里面的流也是共享的父进程的流,所以子进程里面是不能做这些事的。


直接return就更不行了,子进程return以后,会从当前函数的外部调用点后面继续执行,这后面子进程可能将会执行很多语句,结果就没法预料了。


最后看一段程序,如果理解了这段程序,那么对vfork()的理解基本上就没什么大问题了:

#include <stdio.h>
#include <unistd.h>

void stack1() {
    vfork();
}

void stack2() {
    _exit(0);
}

int main() {

    stack1();

    printf("%d goes 1\n", getpid());
    stack2();

    printf("%d goes 2\n", getpid());
    return 0;
}

如果父进程pid为1000,子进程pid为1001,那么输出将会是:

1001 goes 1

1000 goes 2

作者:kxcfzyk 发表于2014-11-23 16:53:03 原文链接
阅读:37 评论:0 查看评论

相关 [正确 vfork vfork] 推荐:

如何正确地使用vfork():简析vfork()与fork()的不同

- - CSDN博客编程语言推荐文章
今天看到知乎上有人问了一个 由于不恰当的使用vfork()而导致的一个奇怪现象,底下的回答非常精彩. 趁此机会我也仔细了解了一下vfork()的特性. 其实对vfork()最完备、权威的表述莫过于man手册里面的解释了. 简单的说,vfork()跟fork()类似,都是创建一个子进程,这两个函数的的返回值也具有相同的含义.

正确理解ThreadLocal

- - Java - 编程语言 - ITeye博客
转自: http://www.iteye.com/topic/103804. 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.

正确重置MySQL密码

- xxg - 火丁笔记
谁都不想弄丢家门钥匙,但不管多么小心,时间长了,这样的事情总会发生几次. MySQL密码也是一样,把它写在文档上不太安全,记在脑子里又难免会忘记. 如果你忘记了MySQL密码,如何重置它呢. 首先停止MySQL服务,然后使用skip-grant-tables参数启动它:. 此时无需授权就可以进入到MySQL命令行,使用SQL重置MySQL密码:.

正确使用银行卡

- - 雨中发呆
请大家看清楚了,是网上银行汇款. 不是银行柜台上汇!柜台上的手续费比网上银行贵的. 但有一个例外,邮政储蓄要收0.5%. 工行:0.9%,最低0.9元/笔,最高45元/笔. 农行:0.4% ,最低1元/笔,最高20元(柜台手续费是0.5%,最高50元).  跨省:如果对方是银行卡:转账金额的0.06% (也就是万分之六),最低1元/笔,最高12元/笔.

女孩正确的生活方式

- bourne - 佳人
女孩正确的生活方式,关于健康、饮食、运动、爱情、伴侣、承诺、友情、微笑、心态、人生、幸福等方面的建议,为自己心爱的女孩收起来吧. 生理期不吃巧克力,因为会加重痛经. 通过运动而非调整型内衣来塑造曲线. 去年的衣服要进行曝晒后才可以穿. 即使爱美,也不要在耳朵上部的外缘软骨部位穿耳洞. 了解自己的家庭病史,特别是母亲和外婆的病史.

正确理解javascript的this关键字

- BeerBubble - 三水清
javascript有this关键字,this跟javascript的执行上下文密切相关,很多前端开发工程师至今对this关键字还是模棱两可,本文将结合代码讲解下javascript的this关键字. 定义了一个person对象,对象中包含了name、gender属性,还包括了一个getName的方法,其作用是输出person对象的name.

如何正确配置Nginx+PHP

- - 火丁笔记
对很多人而言,配置Nginx+PHP无外乎就是搜索一篇教程,然后拷贝粘贴. 听上去似乎也没什么问题,可惜实际上网络上很多资料本身年久失修,漏洞百出,如果大家不求甚解,一味的拷贝粘贴,早晚有一天会为此付出代价. 假设我们用PHP实现了一个前端控制器,或者直白点说就是统一入口:把PHP请求都发送到同一个文件上,然后在此文件里通过解析「REQUEST_URI」实现路由.

(转)如何正确地处理时间

- - jackyrong
日期和时间在程序中应用广泛,每种程序开发语言都自带处理日期和时间的相关函数,很多开发者把日期和时间存入数据库中,但是,一旦涉及到跨时区的日期和时间的处理时,大多数开发者根本就不明白如何正确地处理日期和时间. 首先,我们来看大部分的程序都是这么创建当前时间并存入数据库的:. 这么做的问题在于,数据库的DateTime类型没有时区(time zone)信息,因此,存入的是本地时间,并且丢掉了时区信息.

如何正确设置job的interval?

- - ITeye博客
首先,先了解interval的定义. 即在job开始执行时根据date function估算job下次执行的时间(NEXT_DATE). 通常,我们采用两种方式来设置job的interval:. 由date function估算出的时间是固定的. 由date function估算出的时间是非固定的.