关于vfork的小知识

介绍

vfork和fork一样都可以用来创建一个新进程,与fork相比,它有一些自己独特的用处。下面是他们的一些异同
* vfork 与fork一样都是调用一次,返回两次(一个是父进程调用vfork后的返回值,该返回值为子进程的pid。一个是子进程调用vfork的返回值,该返回值为0)
* 使用fork创建一个子进程的时候,子进程只是完整复制子进程的资源。这样得到的子进程独立于父进程,具有良好的并发性。而使用vfork创建子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork创建的子进程共享父进程的地址空间,也就是说子进程完全运行在父进程的地址空间上。子进程对该地址空间中任何数据的修改同样为父进程所见。
* 使用fork创建一个子进程时,哪个进程先运行取决于系统的调度算法。而vfork一个进程时,vfork保证子进程先运行,当它调用exec或exit后,父进程才可以被调度运行。如果在调用exec或exit之前子进程要依赖父进程的某几个行为,就会导致死锁。
fork创建一个子进程时,子进程要将父进程几乎每种资源都复制,所以fork是一个开销很大的系统调用,这些开销并不是所有情况都适用。比如fork一个进程后,立即调用exec执行另一个应用程序,那么fork过程中子进程对父进程地址空间的复制将是一个多余的过程。vfork不会拷贝父进程的地址空间,大大减少了系统开销。
我们看看下面这段程序。

代码

fork

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

int qvar=5;

int main()
{   
    pid_t pid;
    int var=1,i;
    char str[]="I'm here";

    printf("fork is different with vfork\n");

    pid=fork();
    //pid=vfork();
    switch(pid)
    {
        case 0:
            i=3;
            while(i--)
            {
                printf("Child process is running\n");
                qvar++;
                var++;
                sleep(1);
            }

            printf("Child's qvar = %d,var = %d,str = %s\n",qvar,var,str);
            exit(0);
            break;
        case -1:
            perror("failed\n");
            exit(0);
            break;
        default:
            i=5;
            while(i-->0)
            {
                printf("Parent is running\n");
                qvar++;
                var++;
                sleep(1);
            }
            printf("Parent's qvar = %d,var = %d,str = %s\n",qvar,var);
            exit(0);
    }
}

fork结果

这里写图片描述

分析

可见用fork创建子进程时,子进程继承了父进程的全剧变量和局部变量。而且根据子进程的qvar,var均增加了3。父进程的qvar,var也增加了3.且两者的值并未影响可知父子进程有各自独立的地址空间。

我们将上面vfork的注释去掉,然后再注释调fork。

vfork结果

这里写图片描述

分析

首先可见子进程要先于父进程运行,并且由qvar,var的值递增了8可见,vfork后子进程和父进程共享了一段地址空间。

注意

如果我们将vfork下子进程的exit(0)去掉,那么我们看看会发生什么情况
这里写图片描述
我们可以看到父进程的var成了随机值,str也找不到了。
这是因为vfork是这样的工作的:
(1)、保证子进程先执行。
(2)、当子进程调用exit()或exec()后,父进程往下执行。
当去掉子子进程下的exit后,程序默认return。而return会将栈释放,因此当子进程执行完,回到父进程时,父进程的栈已被子进程释放掉了(这就是赤裸裸的坑爹!),因此var为随机值。
因此当我们使用vfork时一定记得在子进程结束后用exit结束,不然会出现一些意想不到的结果。关于这点可以参考
用return和exit结束fork和vfork创建的子进程的思考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章