Linux进程替换(下)

举报
跳动的bit 发表于 2022/08/30 08:02:08 2022/08/30
【摘要】 💦 函数解释及使用这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回 -1。所以 exec 函数只有出错的返回值而没有成功的返回值。✔ 测试用例一:单进程,父进程亲自干活。#include<stdio.h> #include<unistd.h>int main(){ printf("my process begin!\n"); execl("...
💦 函数解释及使用
  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回 -1。
  • 所以 exec 函数只有出错的返回值而没有成功的返回值。

✔ 测试用例一:

在这里插入图片描述

单进程,父进程亲自干活。

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

int main()
{
    printf("my process begin!\n");
    execl("/usr/bin/ls", "ls", "-a", "-l", "-i", NULL);
    printf("my process end!\n");                       
    return 0;                                          
}                                                      

多进程,父进程创建子进程干活。

#include<stdio.h>  
#include<stdlib.h>  
#include<unistd.h>  
#include<sys/types.h>  
#include<sys/wait.h>  
  
int main()  
{  
    pid_t id = fork();  
    if(id == 0)  
    {  
        printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());  
        execl("/usr/bin/ls", "ls", "-a", "-l", "-i", NULL);                                 
        exit(1);  
    }                                                                                      
    //father                                                                               
    int status = 0;                                                                        
    pid_t ret = waitpid(id, &status, 0);                                                   
    if(ret > 0)                                                                            
    {                                                                                      
        printf("child status -> sig: %d, code: %d\n", status & 0x7F, (status >> 8) & 0xFF);
    }                                                                                      
    else                                                                                   
    {                                                                                      
        printf("wait error!\n");                                                           
    }                                                                                      
                                                                                           
    return 0;                                                                              
}                                                                                          

💨运行结果:

在这里插入图片描述

在这里插入图片描述

为什么图一没有输出 my process end ! && 图二的退出码是 0 ❓

  因为在这之前 execl 已经程序替换了,所以 execl 后面的代码已经不是当前进程的代码了,所以图二获取到的退出码 0 是 ls 的退出码。换言之,一旦程序替换,你到底执行正确与否是取决于 ls 程序。

  所以 exec 系列的函数不用考虑返回值,只要返回了,一定是这个函数调用或程序替换失败了。

  注意编程规范是父进程创建子进程干活。

加载器 ❓

在这里插入图片描述

  一个完整的集成开发环境的组件肯定包括编辑器、编译器、调试器、加载器等。一个软件被加载到内存里,肯定是运行起来,形成进程,进程再调用 exec 系列的函数就可以完成加载的过程。所以 exec 可以理解成一种特殊的加载器。

✔ 测试用例二:

在这里插入图片描述

execv 与 execl 较为类似,它们的唯一差别是,如果需要传多个参数,那么:execl 是以可变参数的形式进行列表传参;execv 是以指针数组的形式传参。

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

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
    	//const char* const my_argv[] = {"ls", "-l", "-a", "-i", NULL};//err,注意要与函数原型的参数类型匹配
        char* const my_argv[] = {"ls", "-l", "-a", "-i", NULL};                            
        printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
        execv("/usr/bin/ls", my_argv);
        exit(1);
    }
    //father    
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret > 0)
    {
        printf("child status -> sig: %d, code: %d\n", status & 0x7F, (status >> 8) & 0xFF);
    }
    else
    {
        printf("wait error!\n");
    }

    return 0;
}

💨运行结果:

在这里插入图片描述

✔ 测试用例三:

在这里插入图片描述

execlp 相比 execl 在命名上多了 1 个 p,且参数只有第 1 个不同:不同点在于 execlp 不需要带路径,execlp 在执行时,它会拿着你要执行的程序自动的在系统 PATH 环境变量中查找你要执行的目标程序。

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

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
        execlp("ls", "ls", "-a", "-i", "-l", NULL);                                        
        exit(1);
    }
    //father    
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret > 0)
    {
        printf("child status -> sig: %d, code: %d\n", status & 0x7F, (status >> 8) & 0xFF);
    }
    else
    {
        printf("wait error!\n");
    }

    return 0;
}

💨运行结果:

在这里插入图片描述

带 p 的含义就是不用带路径,系统会自动搜索你要执行的程序,不带 p 则相反。所以你要执行哪个程序,背后的含义是 a) 你在哪 b) 你是谁。可见 execlp 就是 b,execl 就是 ab。当然这里的搜索默认只有系统的命令才能找到,如果需要执行自己的命令,需要提前把自己的命令与 PATH 关联。

✔ 测试用例四:

在这里插入图片描述
所以 execvp 无非就是不带路径,使用指针数组传参。

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

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        char* const my_argv[] = {"ls", "-l", "-a", "-i", NULL};                            
        printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
        execvp("ls", my_argv);
        exit(1);
    }
    //father    
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret > 0)
    {
        printf("child status -> sig: %d, code: %d\n", status & 0x7F, (status >> 8) & 0xFF);
    }
    else
    {
        printf("wait error!\n");
    }

    return 0;
}                                                                        

💨运行结果:

在这里插入图片描述

✔ 测试用例五:

e 表示传入默认的或者自定义的环境变量给目标可执行程序。

在这里插入图片描述

子进程跑自己的程序 mycmd.c。

在这里插入图片描述

makefile 里需要 make 时一次生成 2 份可执行程序。

在这里插入图片描述

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

int main(int argc, char* argv[], char* env[])
{
    pid_t id = fork();
    if(id == 0)
    {
        char* const my_env[] = {"MYENV=hellobit!", NULL};
        printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());

        //测试1
        //execle("./mycmd", "mycmd", NULL, my_env);

        //测试2
        //char* const my_argv[] = {"mycmd", NULL};
        //execve("./mycmd", my_argv, my_env);

        //测试3
        //execvpe("mycmd", my_argv, my_env);

        //测试4
        execle("./mycmd", "mycmd", NULL, env);                                             

        exit(1);
    }
    //father    
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret > 0)
    {
        printf("child status -> sig: %d, code: %d\n", status & 0x7F, (status >> 8) & 0xFF);
    }
    else
    {
        printf("wait error!\n");
    }

    return 0;
}

💨运行结果:

  • 测试 1 && 测试 2

    在这里插入图片描述

  • 测试 3

    在这里插入图片描述

      execvpe 并没有替换成功,因为它带了 p,所以它会在环境变量里查找目标程序,但是此时目标程序就在当前路径。所以当前场景是不适合使用带 p 的函数的。如果想让 execvpe 查找到目标程序,就只能将当前路径添加到环境变量中,或将目标程序添加到任意环境变量的路径下。

  • 测试 4

    在这里插入图片描述

  main 函数可以获得环境变量,环境变量再传给子进程。所以现在我们就能理解环境变量是怎么被子进程继承的,本质是通过 exec 函数将环境变量传入的。

💦 命名理解

这些函数原型看起来很容易混淆,但只要掌握了规律就很好记。

  • l(list),表示参数采用列表。
  • v(vector),表示参数使用数组。
  • p(path),自动搜索环境变量 PATH。
  • e(env),表示自己维护环境变量。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。