`

Linux进程管理(三)——fork() vs exec || fork() vs. vfork()

 
阅读更多

 

一、关于fork()和exec系列区别的文字,很浅显易懂:

1、fork()
    一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。

   事实上,目前大多数的unix系统在实现上并没有作真正的copy。一般的,CPU都是以“页”为单位分配空间的,象INTEL的CPU,其一页在通常情况下是4K字节大小,而无论是数据段还是堆栈段都是由许多“页”构成的,fork函数复制这两个段,只是“逻辑”上的,并非“物理”上的,也就是说,实际执行fork时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,系统就将有区别的“页”从物理上也分开。系统在空间上的开销就可以达到最小。

2、对于exec系列函数
    一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到。

 

二、关于fork()和vfork()系列区别

vfork与fork主要有三点区别:
  (1) fork():子进程拷贝父进程的数据段,堆栈段
       vfork():子进程与父进程共享数据段
  (2) fork()父子进程的执行次序不确定vfork 保证子进程先运行,在调用 exec 或 exit 之前与父进程数据是共享的,在它调用 exec或 exit 之后父进程才可能被调度运行。
  (3) vfork()保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

  1)先用fork()进行试验

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

int main(void) {
  pid_t pid;
  int count=0;
  pid=fork();

  count++;

  printf("count= %d\n",count);

  return 0;
}

分析:
  通过上面fork()的说明,这个程序的输出应该是:
  ./test
  count= 1
  count= 1

  2)而将fork()换成vfork()呢,程序如下

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

int main(void) {

  pid_t pid;

  int count=0;

  pid=vfork();

  count++;

  printf("count= %d\n",count);

  return 0;
}

  执行结果:
  ./test
  count= 1
  count= 1
  Segmentation fault (core dumped)

  分析:
  通过将fork()换成vfork(),由于vfork()是共享数据段,为什么结果不是2呢,答案是:
  vfork保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁.

  3)做最后的修改,在子进程执行时,调用_exit(),程序如下:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

int main(void) {
  pid_t pid;

  int count=0;

  pid=vfork();

  if(pid==0) {
       count++;
       _exit(0);
  } else {
       count++;
  }

  printf("count= %d\n",count);

  return 0;
}

  执行结果:
  ./test
  count= 2

  分析:如果子进程中如果没有调用_exit(0),则父进程不可能被执行,在子进程调用exec(),exit()之后父进程才可能被调用.
  所以加上_exit(0),使子进程退出,父进程执行.
  这样 else 后的语句就会被父进程执行,又因在子进程调用 exec 或 exit 之前与父进程数据是共享的,
  所以子进程把父进程的数据段count改成1了,子进程退出后,父进程又执行,最终就将count 变成了 2.

 

三、那么什么时候要用vfork() ,而不用fork()呢:


(1)vfork()共享父进程数据段,比fork()的COW节省了开销:这样的好处是在子进程被创建后仅仅是为了调用exec执行另一个程序时,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的,通过vfork可以减少不必要的开销。

(2)vfork()保证先“子”后“父”执行:vfork创建子进程后,保证子进程先运行,在它调用exec或exit后父进程才可能调度运行(再次之前父进程一直阻塞)。而fork的父子进程运行顺序是不定的,它取决于内核的调度算法。

 

 

 

参考文献

http://linux.chinaitlab.com/c/831529.html

http://linux.chinaitlab.com/c/831529_2.html

http://wenku.baidu.com/link?url=83YXzTr6TKlTw_kfnKzgZQ-jjLV2wFYxU7ZONaFHbhZ6_xxBBuR8qgG0UUcFC2GPvO0wClASwJY8V1LctSTjJwFjBGXN1ODQ6dmdmrNaPgS

 

神奇的vfork:   http://hi.baidu.com/_kouu/item/93af230d0a22bc354ac4a3d9

 

分享到:
评论

相关推荐

    深入解读Linux进程函数fork(),vfork(),execX()

    本文研究的主要是Linux进程函数fork(),vfork(),execX()的相关内容,具体介绍如下。 函数fork() fork函数:创建一个新进程 1、fork()成功后,将为子进程申请PCB和用户内存空间。 2、子进程会复制父进程用户空间的...

    【Linux】进程二 (PCB & fork/vfork & wait/waitpid & exit/_exit & exec函数族 & 环境变量)

    一、描述进程——PCB ·进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合 ·我们称为PCB,Linux操作系统下的PCB是:task struct 2、task_struct——PCB的一种 ·在Linux中描述进程的结构体...

    《linux编程技术》-实验3.doc

    实验3 进程的管理 一、实验内容 实验一  编写代码,实现以下功能:  打印当前所有环境变量的值;  添加新的环境变量NEWENV=first;  修改环境变量NEWENV的值为second;  打印环境变量NEWENV的值。 实验二 ...

    Linux 创建子进程执行任务的实现方法

    本文将介绍如何使用 fork/vfork 系统调用来创建新进程并使用 exec 族函数在新进程中执行任务。 fork 系统调用 要创建一个进程,最基本的系统调用是 fork: # include pid_t fork(void); pid_t vfork(void); 调用 ...

    unix 编程进程控制详细介绍

    unix 编程进程控制: fork函数 fork创建子进程,子进程是父进程的副本,会...1:vfork子进程先执行,并且子进程调用exec函数 2:vfork子进程不会copy父进程的地址空间,也就是会公用。 exit函数 exit函数会关闭所以I

    UNIX环境高级编程(第二版,英文版)

    此书对于linux环境编程的同志应该是必读的书,目录如下: Copyright Praise for Advanced Programming in the UNIX® Environment, Second Edition Praise for the First Edition Addison-Wesley ...

    UNIX高级编程 计算机科学丛书

    5. IPC——进程间通信(第十四、十五章)。 6. 实例—一个数据库的函数库(第十六章)、与Postscrip打印机的通信(第十七章)、调制解调器拨号程序(第十八章)以及使用伪终端(第十九章)。 如果对C语言较熟悉并...

    UNIX环境高级编程_第二版中文

    8.4 vfork函数  8.5 exit函数  8.6 wait和waitpid函数  8.7 waitid函数  8.8 wait3和wait4函数  8.9 竞争条件  8.10 exec函数  8.11 更改用户ID和组ID  8.12 解释器文件  8.13 system函数  8.14...

    UNIX环境高级编程(第二版中文).pdf

    8.4 vfork 函数 145 8.5 exit函数 147 8.6 wait和waitpid函数 148 8.7 wait3和wait4函数 152 8.8 竞态条件 153 8.9 exec函数 156 8.10 更改用户ID和组ID 160 8.10.1 setreuid 和setregid函数 162 8.10.2 seteuid和 ...

    UNIX环境高级编程_第2版.part1

    8.4 vfork函数176 8.5 exit函数178 8.6 wait和waitpid函数179 8.7 waitid函数183 8.8 wait3和wait4函数184 8.9 竞争条件185 8.10 exec函数188 2 目录 8.11 更改用户id和组id 192 8.12 解释器文件196 8.13 ...

    UNIX环境高级编程_第2版.part2

    8.4 vfork函数176 8.5 exit函数178 8.6 wait和waitpid函数179 8.7 waitid函数183 8.8 wait3和wait4函数184 8.9 竞争条件185 8.10 exec函数188 2 目录 8.11 更改用户id和组id 192 8.12 解释器文件196 8.13 ...

    UNIX环境高级编程.pdf

    8.4 vfork 函数 145 8.5 exit函数 147 8.6 wait和waitpid函数 148 8.7 wait3和wait4函数 152 8.8 竞态条件 153 8.9 exec函数 156 8.10 更改用户ID和组ID 160 8.10.1 setreuid 和setregid函数 162 8.10.2 seteuid和 ...

    UNIX环境高级编程(第八章)

    第8章 进程控制 141 8.1 引言 141 8.2 进程标识 141 8.3 fork函数 142 8.4 vfork 函数 145 8.5 exit函数 147 8.6 wait和waitpid函数 148 8.7 wait3和wait4函数 152 8.8 竞态条件 153 8.9 exec函数 156 8.10 更改用户...

    UNIX环境高级编程

    8.4 vfork 函数 145 8.5 exit函数 147 8.6 wait和waitpid函数 148 8.7 wait3和wait4函数 152 8.8 竞态条件 153 8.9 exec函数 156 8.10 更改用户ID和组ID 160 8.10.1 setreuid 和setregid函数 162 8.10.2 seteuid和 ...

    posix-spawn:Ruby进程生成库

    在fork()的许多常见用法中,其后是exec函数家族之一以生成子进程( Kernel#system , IO::popen , Process::spawn等),可以消除此开销通过使用特殊的进程生成接口( posix_spawn() , vfork()等) posix-spawn...

    UNIX环境高级编程 不扣分哦

    本书的前15章着重于理论知识的阐述,主要内容包括UNIX文件和目录、进程环境、进程控制、进程间通信以及各种I/O。在此基础上,分别按章介绍了多个应用实例,包括如何创建数据库函数库,PostScript 打印机驱动程序,...

Global site tag (gtag.js) - Google Analytics