fork fvck vfork!

Linux高级程序设计的第七章,看到fork和vfork的时候晕了, 

先来看fork和vfork的区别,

1、fork调用成功后,子进程会复制父进程的几乎所有信息,当然pid除外,例如父进程的代码段,数据段,BSS,堆,栈,打开的文件描述符,另外,子进程还继承父进程的一些属性,比如实际用户/组号,有效用户/组号,保留的用户/组号,进程组号,环境变量,对文件的执行时的标识,信号的处理方式,信号掩码,当前工作目录,根目录,文件模式及创建掩码,文件大小限制等

这样的话,父子进程相互独立,互不影响。

2、而vfork具有更高的效率,因为vfork创建新进程时,无需完全复制父进程的地址空间,如果子进程只是执行类似exec()函数,就用不到父进程的数据空间

这样,父子进程会共享所有的资源,例如数据段和BSS段。


因此,使用vfork可能会出现问题,书上举了一个vfork导致父进程出现段错误例子。

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

void test()                //test 函数, 在此函数中,调用vfork
{
    pid_t pid;
    pid = vfork();
    if(pid == -1)
    {
        perror("vfork");
        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] = i;
    }
    printf("3:child pid = %d, ppid = %d\n", getpid(),getppid());
    
    return;
}
int main(int argc, char** argv)
{
    pid_t pid;              //为了演示给出临时变量,没有使用
    test();                 //调用test
    printf("%d:after test\n", getpid());
    fun();                  //调用fun
    printf("%d:after fun\n", getpid());
    return 0;
}

执行结果:

1:child pid = 16358, ppid = 16357
16358:after test
3:child pid = 16358, ppid = 16357
16358:after fun
2:parent pid = 16357, ppid = 11820
Segmentation fault (core dumped)

书上给的解释,是子进程在执行完test函数,再调用fun函数的时候,把调用test函数的栈空间覆盖了,因此,等到父进程从test函数返回时,它的栈空间已经不存在了(因为他们共用的调用test栈空间)。

这也说得通,但是,稍微如果我把fun函数改一下,

void fun()                 //此函数代码,从结果看,在子进程中可以正常执行
{                          //父进程中也没有能够执行,但是没有出现段错误
    int i;
    int buf[100];
    for(i = 0; i < 100; i++)
    {
        buf[i] = i;
    }
    printf("3:child pid = %d, ppid = %d\n", getpid(),getppid());
 
 -  return;
 +  exit(0);
}

执行结果是:

1:child pid = 16398, ppid = 16397
16398:after test
3:child pid = 16398, ppid = 16397
2:parent pid = 16397, ppid = 11820
16397:after fun

先看return和exit的区别:

1、return只从函数主体中返回,exit退出当前进程,因此在主函数中使用return和exit是没有区别的

2、调用exit时要调用一段终止处理程序,然后关闭所有I/O流。注:_exit也不会处理标准I/O缓冲区。

好像帮助不大,我是这样理解的,首先不管调用fun的栈空间有没有被释放掉,当父进程返回的时候拿到的“返回地址”已经不是test函数的栈空间的“返回地址”了,前一个“返回地址"指向fun的下一条语句,也就是

printf("%d:after fun\n", getpid());
而后一个“返回地址”指向的语句是,

printf("%d:after test\n", getpid());
这样就解释了父进程为什么只打印了after fun,而没有打印after test


但是疑点仍然是为什么会出现段错误,我的理解是这样的,如果直接调用_exit,栈空间来不及释放,父进程退出test之时还能访问到覆盖后的栈空间,但是调用return,栈空间会立即释放,所以,父进程退出test函数,实际想执行after test这个地址的时候已经是非法了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值