函数fork与vfork的区别与联系详解

 创建一个新进程的方法只有由某个已存在的进程调用fork()或vfork(),当然某些进程如init等是作为系统启动的一部风而被内核创建的。
1.fork函数介绍

#include sys/types.h>
#include unistd.h>
pid_t fork (void );

正确返回:父进程中返回子进程的进程号;子进程中返回0;(单调用双返回函数)
错误返回:-1;
子进程是父进程的一个拷贝。具体说,子进程从父进程那得到了数据段和堆栈段,但不是与父进程共享而是单独分配内存。fork函数返回后,子进程和父进程都是从fork函数的下一条语句开始执行。
由于子进程与父进程的运行是无关的,父进程可先于子进程运行,子进程也可先于父进程运行,所以下段程序可以有两种运行结果。

[root@happy src]# cat simplefork.c
#include stdio.h>
#include sys/types.h>
#include unistd.h>
int globa = 4;
int main (void )
{
        pid_t pid;
        int vari = 5;
        printf ("before fork\n" );
        if ((pid = fork())  0){
                printf ("fork error\n");
                exit (0);
        }
        else if (pid == 0){ 
                globa++ ;
                vari--;
                printf("Child changed\n");
        }
        else
                printf("Parent did not changde\n");
        printf("globa = %d vari = %d\n",globa,vari);
        exit(0);
}

运行结果:(可能出现的一种结果)

[root@happy src]# ./a.out
before fork
Child changed
globa = 5 vari = 4
Parent did not changde
globa = 4 vari = 5

2.vfork函数介绍
vfork创建新进程的主要目的在于用exec函数执行另外的程序,实际上,在没调用exec或exit之前子进程的运行中是与父进程共享数据段的。在vfork调用中,子进程先运行,父进程挂起,直到子进程调用exec或exit,在这以后,父子进程的执行顺序不再有限制。

[root@happy src]# cat simplevfork.c
#include stdio.h>
#include sys/types.h>
#include unistd.h>
int globa = 4;
int main (void )
{
        pid_t pid;
        int vari = 5;
        if ((pid = vfork())  0){
                printf ("fork error\n");
                exit (0);
        }
        else if (pid == 0){
                globa++ ;
                vari--;
                printf("Child changed\n");
                _exit(0);
        }
        else
                printf("Parent did not changde\n");
        printf("globa = %d vari = %d\n",globa,vari);
        exit(0);
}
运行结果(固定的):
[root@happy src]# gcc simplevfork.c
[root@happy src]# ./a.out
Child changed
Parent did not changde
globa = 5 vari = 4

总结
区别:
    1、fork()用于创建一个新进程。由fork()创建的子进程是父进程的副本。即子进程获取父进程数据空间,堆和 栈的副本。父子进程之间不共享这些存储空间的部分。而vfork()创建的进程并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec (或exit)于是也就不会存放该地址空间。相反,在子进程调用exec或exit之前,它在父进程的空间进行。
    2、vfork()与fork()另一个区别就是:vfork保证子进程先运行,在调用exec或exit之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。
    3/vfork和fork之间的还有一个区别是: vfork保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
相同:
  1、两者被调用一次,但是返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。

下面是一个典型的例子:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void test()
{
      pid_t pid;
      pid=vfork();//创建子进程
      if(pid==-1)//异常处理
      {
          perror("vfork error!\n");
          exit(EXIT_FAILURE);
      }
      else if(pid == 0)
      {

          printf("1:child pid = %d,ppid = %d\n",getpid(),getppid());
          return;
      }
      else
          printf("2:parent pid = %d,ppid = %d\n",getpid(),getppid());
                 
}

void fun()
{
    int i;
    int buf[100];
    for(i=0;i<100;i++)
            buf[i]=0;
    printf("3:child  pid=%d,ppid=%d\n",getpid(),getppid());
}

int main(int argc ,char *argv[])
{
         test();
         fun();
         return 0;
}
运行之后效果如下:

可以看到程序在后续执行的时候出现了错误,并且可以知道时在主进程中出现的错误,为什么?
vfork调用的时候时子进程先于主进程运行的,在调用test函数之后,子进程执行玩之后,将清理test函数的栈空间,子进行再调用fun函数,将覆盖掉test的栈空间,继续执行fun函数。
但是当子进程退出的时候,执行父进程,在test函数返回的时候该栈空间已经被子进程破坏了,不存子在了,所以就出现了栈错误!

 

posted @ 2012-04-27 11:58  地瓜日记  阅读(1465)  评论(1编辑  收藏  举报