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

今天看到知乎上有人问了一个由于不恰当的使用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

时间: 2024-10-16 21:07:03

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

TCP,UDP,IP 协议简析

现在的操作系统基本都实现了TCP/IP协议,TCP/IP协议栈分为五层: 应用层:向用户提供的一组常用的应用程序,如TELNET,FTP,SMTP,SNTP,DNS,HTTP,这些应用程序有一个端口用来标识. 传输层:主要协议是TCP和UDP,提供应用程序的通信. 网络层:主要协议是IP协议,定义了IP地址格式,是不同应用程序的数据在网络上通畅传输的关键. 链路层:这是TCP/IP软件的最低层,负责接收IP数据包并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层. 物理层: 每

JDK框架简析--java.lang包中的基础类库、基础数据类型

题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说.还是在一个层级上,它们都是须要被编译成字节码.在JRE中执行的,JDK编译后的结果就是jre/lib下的rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平. 本系列全部文章基于的JDK版本号都是1.7.16. 源代码下载地址:https://jdk7.jav

简析Spring MVC 数据解析

简析Spring MVC 数据解析 特别说明:本文使用spring 版本为 4.1.3 常用数据提交方式: 1. form 表单提交数据 1.1 解析form表单数据(无图片等数据) 前端代码事例: <form action="test/entity" method="post"> 用户ID:<input type="text" name="userid"/><br> 用户名:<inp

PostgreSQL的 pg_hba.conf 文件简析

最近试用PostgreSQL 9.04,将pg_hba.conf配置的一些心得分享. pg_hba.conf是客户端认证配置文件,定义如何认证客户端. 下面是常用的pg_hba.conf配置: 1 2 3 4 5 6 7 8 9 10 # TYPE  DATABASE  USER  CIDR-ADDRESS  METHOD   # "local" is for Unix domain socket connections only local    all      all      

英语本科毕业论文常见的八大问题简析

英语专业本科结业论文是对英语本科生四年学习结果的末了一次查验,能全面反应门生在四年学习历程中对所学的词汇.语法.写作知识的掌握程度,能查验门生利用所学知识阐发.办理题目标本领,综合表现某一文化.语言.商务等知识与写作.科研本领的联合运用程度.结业论文属于研究论文写作范畴,有特定的范例和方式,差别于平凡文学创作,不行以自出机杼.一样通常来讲,毕业论文包罗三部门:第一部门包罗标题.中文择要.英文择要.目次:第二部门是正文,包罗先容.文献回首.科研要领.论点睁开和论证部门,结论:第三部门包罗参考文献和

JAVA泛型简析

泛型是JDK1.5支持的新功能之一,在目前的JAVA编程中被广泛的使用,下面我们就来看看泛型和它的属性. 1.Java泛型 其实Java的泛型就是创建一个用类型作为参数的类.就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1.str2的值是可变的.而泛型也是一样的,这样写class Java_Generics<K,V>,这里边的K和V就象方法中的参数str1和str2,也是可变.下面看看例子: //code list 1 i

Unity3D研究之asset bundle 格式简析详解

Unity3D 的 asset bundle 的格式并没有公开.但为了做更好的差异更新,我们还是希望了解其打包格式.这样可以制作专门的差异比较合并工具,会比直接做二进制差异比较效果好的多.因为可以把 asset bundle 内的数据拆分为独立单元,只对变更的单元做差异比较即可. 网上能查到的资料并不是官方给出的,最为流行的是一个叫做 disunity 的开源工具.它是用 java 编写的,只有源代码,而没有给出格式说明(而后者比代码重要的多).通过阅读 disunity 的代码,我整理出如下记

web应用构架LAMT及tomcat负载简析

Httpd    (mod_jk.so) workers.properties文件 uriworkermap.properties文件 <--AJP1.3--> Tomcat  --> jdk 大致流程:apache服务器通过mod_jk.so 模块处理jsp文件的动态请求.通过tomcat worker等待执行servlet/JSP的tomcat实例.使用 AJP1.3协议与tomcat通信.tomcat有借助jdk解析. 负载就是 多台tomcat.共同解析apache发送的jsp请

CentOS的网络配置简析

我们在进行对CentOS的网络配置时,一般会从IP地址(IPADDR).子网掩码(NETMASK).网关(Gateway).主机名(HOSTNAME).DNS服务器等方面入手.而在CentOS中,又有着不同的命令或配置文件可以完成这些配置操作,接下来,我们将从ifcfg系命令,iproute2系命令以及配置文件3个方面来简析网络配置的方法. 一.ifcfg系命令 ifcfg系命令包括ifconfig,route,netstat和hostname. 1.ifconfig命令 用来配置一个网络接口.