vfork与进程创建

进程创建是否只依赖与fork()和execve()?

  1. fork()通过完整复制当前进程的方式创建新进程。

  1. execve()根据参数覆盖进程数据(一个不留)。会重置内存空间,通过fork()辛辛苦苦复制的进程空间没有了。

vfork


NAME
       vfork - create a child process and block parent

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

       pid_t vfork(void);
  1. vfork()用于创建子进程,然而不会复制父进程空间中的数据。(为了效率考虑,不会做无畏的拷贝操作)

  1. vfork()创建的子进程直接使用父进程空间(没有完整独立的进程空间)。

  1. vfork()创建的子进程对数据(变量)的修改会直接反馈到父进程中。

  1. vfork()是为了execve()系统调用而设计。

下面来看一个demo。


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

int main(int argc,char* argv[])
{
    pid_t pid=0;
    int var =88;

    if((pid=vfork()) < 0)
    {
        printf("vfork error\n");
    }
    else if(pid == 0) //子进程
    {
        printf("pid=%d,var=%d\n",getpid(),var);    //88
        var++;
        printf("pid=%d,var=%d\n",getpid(),var);    //89
        return 0; //子进程结束
    }

    printf("pid=%d,var=%d\n",getpid(),var); //由父进程打印

    return 0;
}

首先,思考一下,23行会打印出来什么?23行到底打印88,还是89?

编译运行,来看一下结果:


wj@wj:~/WORK/Learning/DT/07$ ./test.out 
pid=57553,var=88
pid=57553,var=89
pid=57552,var=1260087904
段错误 (核心已转储)

为什么会出现段错误???

vfork深度分析

  1. vfork创建出来的进程没有独立的内存空间,和父进程的内存空间进行共享。

  1. 子进程会使用父进程的堆栈段,数据段,代码段等。

vfork要点分析

  1. vfork()成功后,父进程将等待子进程结束。

  1. 子进程可以使用父进程的数据(堆、栈、全局数据区)。

  1. 子进程可以从创建点调用其他函数,但不要从创建点返回。

3.1 当子进程执行流回到创建点/需要结束时,使用 _exit(0)系统调用。

3.2 如果使用return 0,这个时候需要回收栈帧,那么将破坏栈结构,导致后续父进程执行出错,这也是上述那个demo第三次打印var,出现那么奇怪的值的原因了。

解决方案


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

int main(int argc,char* argv[])
{
    pid_t pid=0;
    int var =88;

    if((pid=vfork()) < 0)
    {
        printf("vfork error\n");
    }
    else if(pid == 0) //子进程
    {
        printf("pid=%d,var=%d\n",getpid(),var);
        var++;
        printf("pid=%d,var=%d\n",getpid(),var);
        //return 0; //子进程结束
        _exit(0);
    }

    printf("pid=%d,var=%d\n",getpid(),var); //由父进程打印

    return 0;
}

程序编译运行输出:


wj@wj:~/WORK/Learning/DT/07$ gcc test.c -o test.out
wj@wj:~/WORK/Learning/DT/07$ ./test.out 
pid=61065,var=88
pid=61065,var=89
pid=61064,var=89 //输出符合预期,父进程并没有改变var的值;子进程改变了var的值,所以父进程输出89

fork与vfork的选择

  1. 为什么选用vfork?因为我们觉得fork的效率比较低下。

  1. fork不容易产生问题,vfork陷阱点比较多

fork的现代优化

copy-on-Write技术

  1. 多个任务访问同一资源,如果只是读资源,是没有问题的。当然,如果是写资源,就可能发生问题。

  1. 在写入操作修改资源时,复制资源的原始副本。(类型延时绑定,把事情推迟到不得不做的时候才来做)。

fork引入copy-on-write之后,父子进程共享相同的进程空间

  1. 当父进程或者子进程的其中之一修改内存数据,则实时复制进程空间

  1. fork() + execve() <-> vfork() + execve()

  1. vfork()是一个替代品,不是万不得已,不得使用vfork()


///helloworld.c
#include <stdio.h>

int main(int argc,char* argv[])
{
    printf("hello world\n");
    
    return 0;
}

//test.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

/*
    wait: true  :代表父进程是否等待子进程结束
    wait: false :代表父进程不等待子进程结束
*/


int create_process(char* path,char* const args[],char* const env[],int wait)
{
    int ret = fork();

    if(ret == 0)
    {
        if(execve(path,args,env) == -1)
        {
            exit(-1);
        }
    }

    if(wait && ret)  // 1.wait为true。2.并且父进程是否成功创建子进程?来共同解决父进程是否等待子进程结束?
    {
        waitpid(ret,&ret,0);
    }

    return ret;   //1.如果等待,ret为子进程的退出状态;2.如果不等待,返回为子进程的pid
}


int main(int argc,char* argv[])
{
    char* target = argv[1];
    char* const ps_argv[] = {target,NULL};
    char* const ps_envp[] = {"PATH=/bin:/usr/bin","TEST=Delphi",NULL};

    int result =0;

    if(argc < 2) exit(-1);

    printf("current: %d\n",getpid());

    result = create_process(target,ps_argv,ps_envp,0);

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

    return 0;
}

编译运行输出:


wj@wj:~/WORK/Learning/DT/07$ gcc helloworld.c -o helloworld.out
wj@wj:~/WORK/Learning/DT/07$ gcc test.c -o test.out
wj@wj:~/WORK/Learning/DT/07$ ./test.out helloworld.out
current: 68411
result = 68412
wj@wj:~/WORK/Learning/DT/07$ hello world

我们可以看到上述输出结果,hello world是之后打印输出的,先打印 result = 68412,所以,我们可以确认父进程是没有等待子进程的。

如果我们改为父进程等待子进程结束,如下代码所示:


result = create_process(target,ps_argv,ps_envp,1);

编译运行输出为:


wj@wj:~/WORK/Learning/DT/07$ gcc test.c -o test.out
wj@wj:~/WORK/Learning/DT/07$ ./test.out helloworld.out
current: 68568
hello world
result = 0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

repinkply

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值