exec函数族,fork, vfork

一、exec函数族接口。
1、什么是exec函数族?
exec函数是一系列的函数接口,然后这些接口的作用就是让一个程序替换(覆盖)掉原来的子进程的。

2、exec函数族接口有哪些?
功能: execl, execlp, execle, execv, execvp, execvpe - execute a file
                            //执行一些文件。

头文件:#include <unistd.h>
原型:
    int execl(const char *path, const char *arg, .../* (char  *) NULL */);
        int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
        int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
        int execv(const char *path, char *const argv[]);
        int execvp(const char *file, char *const argv[]);
        int execvpe(const char *file, char *const argv[],char *const envp[]);

参数:
    path: 需要执行的那个程序的绝对路径   /home/gec/project
    arg:以","分开所有的参数,以NULL作为结束标识 
    file: 文件名   project
    envp: 环境变量
    argv: 参数的数组

返回值:
    成功:一直执行那个文件。
    失败:只有发生错误时,才会返回-1。

3、示例: 尝试产生一个子进程,然后子进程执行"ls -l"这个程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

//int execvpe(const char *file, char *const argv[],char *const envp[]);


int main(int argc,char *argv[])
{
    //1. 现在还是一个单进程的程序。
    //2. 在单进程的状态下,去创建一个新的子进程。
    pid_t x;
    x = fork();
    
    //3. 通过判断返回值。
    if(x > 0)
    {
        //4. 父进程打印helloworld,就主动回收资源,再退出。
        printf("helloworld!\n");
        
        wait(NULL);
        
        exit(0);
    }
    
    if(x == 0)
    {
        printf("child 1\n");
        
        //5. 子进程使用exec函数族的接口去执行"ls -l"这个程序。
        //execl("/bin/ls","ls","-l",NULL); //为什么这里能执行ls,因为在/bin下找到ls   -> 99%
        //execlp("ls","ls","-l",NULL);  //为什么这里能执行ls,因为ls是在环境变量/bin下
        //execle("/bin/ls","ls","-l",NULL,NULL);
        
        char *arg[3] = {"ls","-l",NULL};
        //execv("/bin/ls",arg);
        //execvp("ls",arg);
        //execvpe("ls",arg,NULL);
        
        printf("child 2!\n");
        
        exit(0);
    }
    
    return 0;
}

4、 结论。
1)以上6个函数功能类似的,记住一个就可以了。
2)exec函数族功能替换(覆盖)掉一个进程,所以在exec函数之后的代码都变成无效的。
3)替换前后,子进程的PID号不会改变。

二、如何确保子进程先运行?  -> vfork()  -> man 2 vfork
功能: vfork - create a child process and block parent
    //创建一个子进程并且让父进程阻塞。

头文件:#include <sys/types.h>
        #include <unistd.h>

原型:
    pid_t vfork(void);

参数:无
返回值:
成功:  产生新子进程                 父进程        子进程
                    子进程的ID号  0

失败:  没有产生新的子进程           父进程
                        -1

注意:
1)fork()虽然父子随机先后,但是同时开始执行。
2)vfork()产生了孩子之后,孩子就会正常执行,但是父进程就会阻塞。
   父进程阻塞到
         1)孩子调用exit()
     2)孩子调用exec()函数族
         3)孩子调用exit(),父进程调用exit()。
     4)孩子不调用exit(),父进程调用exit(),子进程执行结束后,虽然没有exit(),但是父进程中有,也能解锁父进程。
   为止。

3)父子进程都不调用exit()就会出错。
   Aborted (core dumped)

三、分析父子进程在内存中资源问题。
1、研究fork()的资源问题。
例子1: 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
    //1. 在fork之前,我定义一个变量。
    int a = 100;
    
    //2. 现在是单进程的程序。
    //3. 在单进程的程序,创建了一个新的子进程。
    pid_t x;
    x = fork();
    
    //4. 通过返回值判断
    if(x > 0)
    {
        printf("parent &a = %p\n",&a); //0x0011
        printf("parent a = %d\n",a);   //100
        wait(NULL);
        exit(0);
    }
    
    if(x == 0)
    {
        printf("child &a = %p\n",&a);  //0x0011
        printf("child a = %d\n",a);    //100
        exit(0);
    }
    
    return 0;
}

结果:
parent &a = 0x7fffce337c90
parent a = 100
child &a = 0x7fffce337c90
child a = 100

结论:
为什么小孩能够使用a,因为fork()之前的资源,都会给小孩拷贝一份。

例子2:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
    //1. 在fork之前,我定义一个变量。
    int a = 100;
    
    //2. 现在是单进程的程序。
    //3. 在单进程的程序,创建了一个新的子进程。
    pid_t x;
    x = fork();
    
    //4. 通过返回值判断
    if(x > 0)
    {
        a = 50;
        //printf("parent &a = %p\n",&a); //0x0011
        printf("parent a = %d\n",a);   //50
        wait(NULL);
        exit(0);
    }
    
    if(x == 0)
    {
        //printf("child &a = %p\n",&a);  //0x0011
        printf("child a = %d\n",a);    //100
        exit(0);
    }
    
    return 0;
}

结果:
parent a = 50
child a = 100

结论: 父进程与子进程拥有独立的空间,父亲的a不会影响小孩的a

例子3:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
    //1. 在fork之前,我定义一个变量。
    //int a = 100;
    
    //2. 现在是单进程的程序。
    //3. 在单进程的程序,创建了一个新的子进程。
    pid_t x;
    x = fork();
    
    //4. 通过返回值判断
    if(x > 0)
    {
        int a = 100;
        //a = 50;
        //printf("parent &a = %p\n",&a); //0x0011
        printf("parent a = %d\n",a);   
        wait(NULL);
        exit(0);
    }
    
    if(x == 0)
    {
        //printf("child &a = %p\n",&a);  //0x0011
        printf("child a = %d\n",a);    
        exit(0);
    }
    
    return 0;
}

结果:编译不通过。
原因:子进程的a没有声明。

例子4:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
    //1. 在fork之前,我定义一个变量。
    //int a = 100;
    
    //2. 现在是单进程的程序。
    //3. 在单进程的程序,创建了一个新的子进程。
    pid_t x;
    x = fork();
    
    //4. 通过返回值判断
    if(x > 0)
    {
        int a = 100;
        //a = 50;
        //printf("parent &a = %p\n",&a); //0x0011
        printf("parent a = %d\n",a);   //100
        wait(NULL);
        exit(0);
    }
    
    if(x == 0)
    {
        int a = 50;
        //printf("child &a = %p\n",&a);  //0x0011
        printf("child a = %d\n",a);    //50
        exit(0);
    }
    
    return 0;
}

结果:
parent a = 100
child a = 50

==========================================================
综上所述:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
    //政府有规定,只要生了一个小孩,本来的父亲有多少财产,政府就会补贴一模一样的数据给这个小孩。
    //1. 现在还是只有1个父亲,结果父亲中奖,中了100万。
    int a = 100;
    
    //2. 这时候父亲带着这个100万,去生了一个小孩。
    pid_t x;
    x = fork();
    
    if(x > 0)
    {
        //2.5 父亲生完小孩之后,偷偷去买了一套200平方的房子(fork之后的资源,不会继承给孩子)
        int b = 200;
        
        //3. 父亲查看一下自己的财产,看看有多少钱。
        printf("parent a = %d\n",a); //100
        
        //6. 看到自己有钱了,赶紧去澳门,输了50万。
        a -= 50;
        
        //7. 父亲再次查看自己的财产,看看有多少钱。
        printf("parent a = %d\n",a); //50
        
        //8. 父亲去看看自己的房子,去休息一下。
        printf("parent b = %d\n",b); //200
        b = 300;
    }
    
    if(x == 0)
    {
        //4. 孩子查看一下自己的财产,看看有多少钱。
        printf("child a = %d\n",a); //100
        
        //5. 小孩有点累,想睡眠一下
        sleep(3);
        
        //8. 这时候小孩醒了之后,发现父亲去赌博输钱,赶紧查看自己的财产:
        printf("child a = %d\n",a); //100  心里想着父亲去赌博输钱了,关我屁事  表面父子
        
        //9. 孩子也想去父亲的新房看看。
        printf("child b = %d\n",b); //父亲说,这是我的房子,关你屁事,还要把你告上法庭(编译出错)
    }
    
    return 0;
}

2、研究vfork()资源问题。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
    //fork(): 父子进程的资源分开的。
    //vfork(): 父子进程的资源共享的。
    
    int a = 0;
    pid_t x;
    x = vfork();
    if(x > 0)
    {
        int b;
        a++;
        printf("parent a = %d\n",a);//3
        printf("parent b = %d\n",b);//0   -> vfork()之后的资源b,不会与子进程的资源b共享。
        wait(NULL);
    }
    
    if(x == 0)
    {
        int b = 200;
        a+=2;
        printf("child a = %d\n",a); //2
        printf("child b = %d\n",b); //200
        exit(0);
    }
    
    return 0;
}

结果:
child a = 2
child b = 200
parent a = 3
parent b = 0

总结fork()与vfork()异同:
1)fork()与vfork()都是可以创建一个新的子进程。
2)fork()与vfork()函数返回值都是一样的。
3)在fork之前的所有资源,在fork()之后,都会拷贝一份给子进程,父子进程拥有独立的空间。
   所以修改了父进程的资源,子进程的资源不会修改。
       修改了子进程的资源,父进程的资源不会修改。
       在父进程中定义的变量,在子进程中不可以使用。
       在子进程中定义的变量,在父进程中不可以使用。
4)在vfork()之前的所有资源,在vfork()之后,可以共享给子进程。    
   所以修改了父进程的资源,子进程的资源跟着修改。
       修改了子进程的资源,父进程的资源跟着修改。
       在父进程中定义的变量,在子进程中不可以使用。
       在子进程中定义的变量,在父进程中不可以使用。
5)fork()随机先后运行。
   vfork()确保子进程先运行,子进程中调用exit()/exec()就会导致父进程解锁。
   
                   fork()      vfork()
分裂之前的资源      分开         共享
分裂之后的资源      分开         分开

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值