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.

扫描二维码关注公众号,回复: 1207492 查看本文章

三、那么什么时候要用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

猜你喜欢

转载自chuanwang66.iteye.com/blog/1997301
今日推荐