先看一个fork的例子:

int glob = ;

int main(void)
{
int var, pid;
var = ; if ((pid = fork()) < ) {
printf("vfork error");
exit(-); } else if (pid == ) { /* 子进程 */
var++;
glob++;
exit();
} printf("pid=%d, glob=%d, var=%d\n", getpid(), glob, var); return ;
}

运行结果:

[root@localhost tmp]# ./a.out
pid=, glob=, var=

可见,子进程修改的局部变量var和全局变量glob后,父进程是不可见的。

如果把代码中的fork替换成vfork,再次运行,得到的结果:

[root@localhost tmp]# ./a.out
pid=, glob=, var=

可见,父进程共享了子进程的修改操作。

在使用vfork时,如果子进程使用return语句结束,会发生什么呢?

int glob = ;

int main(void)
{
int var, pid;
var = ; if ((pid = vfork()) < ) {
printf("vfork error");
exit(-); } else if (pid == ) { /* 子进程 */
var++;
glob++;
return ;
} printf("pid=%d, glob=%d, var=%d\n", getpid(), glob, var); return ;
}

在我的机器上,导致了无限循环(直到vfork调用出错),这是因为子进程调用return语句破坏了父进程的栈。

fork与vfork的区别:

  • fork 是 创建一个子进程,并把父进程的地址空间copy到子进程中;
  • vfork是 创建一个子进程,并和父进程的地址空间share一起用。

我们知道,fork通常采用写时复制技术(copy-on-write, COW)创建子进程,以提高进程clone的性能;但在更早还没有COW的年代,fork创建子进程时时需要完整的复制父进程地址空间到子进程中,如果我们创建子进程的目的是为了调用exec,那么这种复制就显得既低效又无必要。而vfork让子进程共享父进程的地址空间,而不作克隆操作,就是为了节省这种不必要的复制开销。

回到上面return导致程序crash的例子,return会释放局部变量,并弹栈,回到上级函数执行。exit直接退掉。如果你用c++ 你就知道,return会调用局部对象的析构函数,exit不会。(注:exit不是系统调用,是glibc对系统调用 _exit()或_exitgroup()的封装)

可见,子进程调用exit() 没有修改函数栈,所以,父进程得以顺利执行。而子进程调用return,相当于在父进程的栈上执行了弹栈操作,父进程也就跪了。

注意:

1、vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行;

2、子进程在调用exec或exit之前是在父进程的地址空间中运行的。

可见,vfork的设计初衷是为了应对那些子进程需要马上调用exec的场景,因此不对父进程的地址空间做任何复制。


再看一个fork的有趣例子,

int main(void)
{
int i, pid = ; for (i = ; i < ; i++) { pid = fork(); if (pid == ) {
printf("pid:%d\n", getpid());
}
} return ;
}

问题是,执行这段代码,一共产生了几个进程呢?

从执行结果来看,printf函数打印了3次,fork被调用了3次,连上main进程一共有4个进程。

再看下面这个例子,一共打印了多少个 “_” 呢?

int main(void)
{
int i; for(i=; i<; i++){ fork(); printf("-"); } return ;
}

按照上面的例子,程序运行过程中一共有4个进程,把main进程记为A,则有

i=0时,A进程 fork调用,产生子进程B1,然后A、B1各打印一个"_";

i=1时,A进程 fork调用,产生子进程B2,然后A、B2各打印一个"_";

与此同时,B1进程fork调用,产生子进程C1,然后B1、C1各打印一个"_";

看起来,好像有6个"_"被打印了,但这段代码的执行结果却是8个,这是为啥呢?

先来看下,这4个进程间的关系如下:

A --> B1  --> C1

|--> B2

可见,B1、B2继承自A,而C1继承自B1。

1、B1是在i=0时复制A的,此时A还没有调用过printf函数;

2、B2是在i=1时复制A的,此时A已经调用过一次printf函数;

3、C1是在i=1时复制B1的,此时B1已经调用过一次printf函数;

我们知道,fork进程会让子进程完整复制父进程的地址空间,这也就包括了I/O缓冲区,这就是为什么最终打印了8个"_"的原因。

参考文档:

http://coolshell.cn/articles/12103.html

http://coolshell.cn/articles/7965.html

fork与vfork的更多相关文章

  1. fork、vfork、clone区别

    在Linux中主要提供了fork.vfork.clone三个进程创建方法. 问题 在linux源码中这三个调用的执行过程是执行fork(),vfork(),clone()时,通过一个系统调用表映射到s ...

  2. Linux下fork()、vfork()、clone()和exec()的区别

    转自Linux下fork().vfork().clone()和exec()的区别 前三个和最后一个是两个类型.前三个主要是Linux用来创建新的进程(线程)而设计的,exec()系列函数则是用来用指定 ...

  3. c语言exit和return区别,在fork和vfork中使用

    转自c语言exit和return区别,在fork和vfork中使用 exit函数在头文件stdlib.h中. 简述: exit(0):正常运行程序并退出程序: exit(1):非正常运行导致退出程序: ...

  4. 进程创建函数fork()、vfork() ,以及excel()函数

    一.进程的创建步骤以及创建函数的介绍 1.使用fork()或者vfork()函数创建新的进程 2.条用exec函数族修改创建的进程.使用fork()创建出来的进程是当前进程的完全复制,然而我们创建进程 ...

  5. fork与vfork详解

    一.fork函数 要创建一个进程,最基本的系统调用是fork,系统调用fork用于派生一个进程,函数原型如下: pid_t fork(void)  若成功,父进程中返回子进程ID,子进程中返回0,若出 ...

  6. linux 进程创建clone、fork与vfork

    目录: 1.clone.fork与vfork介绍 2.fork说明 3.vfork说明 4.clone说明5.fork,vfork,clone的区别 内容: 1.clone.fork与vfork介绍 ...

  7. 1.2 Linux中的进程 --- fork、vfork、exec函数族、进程退出方式、守护进程等分析

    fork和vfork分析: 在fork还没有实现copy on write之前,Unix设计者很关心fork之后立即执行exec所造成的地址空间浪费,也就是拷贝进程地址空间时的效率问题,所以引入vfo ...

  8. UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid

    本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID   1 进程标识符(Process Identifie ...

  9. fork()、vfork()、clone()和exec()

    前三个和最后一个是两个类型.前三个主要是Linux用来创建新的进程(线程)而设计的,exec()系列函数则是用来用指定的程序替换当前进程的所有内容.所以exec()系列函数经常在前三个函数使用之后调用 ...

随机推荐

  1. 51nod 1422(强行YY)

    1422 沙拉酱前缀 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 沙拉酱非常喜欢数字序列.这正是他要弄一个关于 ...

  2. win7 系统无法复制粘贴解决方案

    用远程桌面登陆服务器不能在本机和远程服务器之间粘贴文本了,即不能从本机复制文本粘贴到服务器,也不能从服务器复制文本粘贴到本机. 以下是解决方法之一,试了几次都很管用户:在服务器上打开任务管理器,查看进 ...

  3. TCP连接——爱的传声筒

    TCP连接——爱的传声筒 TCP通信最重要的特征是:有序(ordering)和可靠(reliable).有序是通过将文本流分段并编号实现的.可靠是通过ACK回复和重复发送(retransmission ...

  4. 匿名内部类为什么访问外部类局部变量必须是final的?

    1.内部类里面使用外部类的局部变量时,其实就是内部类的对象在使用它,内部类对象生命周期中都可能调用它,而内部类试图访问外部方法中的局部变量时,外部方法的局部变量很可能已经不存在了,那么就得延续其生命, ...

  5. lottery概率问题

    问题:1~n编号的彩票,要买全,等概率条件下平均要买几张要求写出算法. 回答:已经买了m张时,买中剩下的概率为1-m/n,则要买的张数为1/(1-m/n)n=2,s=1+1/(1-1/2);n=3,s ...

  6. Android数据存储-文件操作

    一.预备知识 1.Android中的MVC设计模式 MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器.一个逻辑模型可以对于多种视图模型,比如一批统计 ...

  7. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platform)

    catalog . 引言 . Windows 2000网络结构和OSI模型 . NDIS驱动 . NDIS微端口驱动编程实例 . NDIS中间层驱动编程实例 . NDIS协议层驱动编程实例 . TDI ...

  8. ObjC 巧用反射和KVC实现JSON快速反序列化成对象

    1.简单的KVC介绍 KVC是一种间接访问对象属性的机制,不直接调用getter 和 setter方法,而使用valueForKey 来替代getter 方法,setValue:forKey来代替se ...

  9. [IOS NSUserDefaults]的使用:登陆后不再显示登录界面。

    之前搜了好多地方都没找到实现“登陆后不再显示登录界面,而默认自动登录”的方法. 待我发现有种存储方式叫NSUserDefaults的时候,立马又感觉新技能get了. 简介: NSUserDefault ...

  10. Scala类基础

    最近在开始学习Scala,本篇文章我们来讲解一下Scala中类的使用 class Counter { var defaultValue = 0 val valValue = 0 private var ...