前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux-程序替换

Linux-程序替换

作者头像
用户11029129
发布2024-06-04 14:11:36
790
发布2024-06-04 14:11:36
举报
文章被收录于专栏:编程学习编程学习

文章目录:

进程控制

execl接口介绍

多进程版本程序替换

其他exec接口

接口介绍 替换本地程序

总结


前言:

  在Linux系统中,进程程序替换是一种重要的操作,通过进程程序替换,程序可以更新自己的代码和数据,让进程富有动态性和灵活性,话不多说,开始今天的话题。

在这里插入图片描述
在这里插入图片描述

🚀execl接口介绍

  我们的程序只能执行该程序自己的代码,这是众所周知的,但是今天,我想要创建一个子进程来执行别的文件的代码是否可行呢?

在这里插入图片描述
在这里插入图片描述

  在Linux下是可实现的,因为Linux给我们提供了对应的接口:

在这里插入图片描述
在这里插入图片描述

  这些接口支持我们程序在运行的过程中进行程序替换,从而执行到自己想执行的程序。

int execl(const char* path, const char* arg, ...) :

path :表示带路径文件名的字符串,从而搜索到对应的文件 arg, ...:表示可变参数列表,参数不确定,可传入一个或多个 最后必须以NULL结尾

  首先第一个接口,以下面代码来理解:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h> 
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());
    printf("exec begin...\n");

    execl("/usr/bin/ls", "ls", "-a", "-l", NULL); //程序替换,可变参数

    printf("exec end  ...\n");
    return 0;
}
在这里插入图片描述
在这里插入图片描述

  能够清晰观察到,在begin之后,程序被替换为了ls 指令,并且选项为 -al,执行程序,发现运行成功了,但是仔细观察之后,在execl之后的printf并没有起作用。

结论1

程序在执行完exec* 的接口之后,是不会再执行后续的代码了,因为后续代码已经被替换。

  从man手册里有exec* 接口返回值的描述:

在这里插入图片描述
在这里插入图片描述

结论2

exec* 只有失败有返回值,为-1。成功就是成功替换了,所以没返回值。

  替换完成后是属于创建了新的进程还是旧的进程不变呢?我们不妨做个测试:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h> 
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());
    printf("exec begin...\n");
    sleep(5);

    execl("/usr/bin/top", "top", NULL); 

    printf("exec end  ...\n");
    return 0;
}
在这里插入图片描述
在这里插入图片描述

  虽然在替换之后进程的名字变了,但是前后两次的pid并没有变化。

结论3

进程替换并不会创建新的程序,依旧是原来进程的pid


🚀多进程版本程序替换

  通过之前的学习,我们知道进程之间相互独立,那么我们就可以创建一个子进程,让其来执行程序替换,而父进程回收结果:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());

    pid_t id = fork();
    if(id == 0)
    {
        sleep(3);
        printf("exec begin...\n");
        execl("usr/bin/ls", "ls", "-a", "-l", NULL);
        printf("exec end  ...\n");
        exit(1);
    }

    pid_t rid = waitpid(id, NULL, 0);
    if(rid > 0)
    {
        printf("wait success\n");
    }
    
    exit(1);
}

  运行之后,就可以让子进程执行程序替换,并且父进程回收子进程的资源。

在这里插入图片描述
在这里插入图片描述

  我们来思考一个问题:程序替换为什么对父进程没有影响?这是因为,进程具有独立性,在程序替换的时候发生写时拷贝


🚀其他exec接口
✈️接口介绍

  我们通过man手册查询exec*接口,发现不止一个接口,还有六个接口:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  我们需要了解这七个接口的含义以及用法,但是在这里我不会全部一一列举,因为有些接口是类似的,这些类似的接口我只需要说一个就够了。

  首先,这些接口中带有 ‘p’ 字符的接口都有 path 这个参数,实际上这个参数的意义是:

PATH: 并不需要告诉系统程序的具体位置,只需要告诉系统程序的名称,系统在进行替换的时候,会自动在PATH环境变量中去查找。

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());

    pid_t id = fork();
    if(id == 0)
    {
        sleep(3);
        printf("exec begin...\n");
        execlp("ls", "ls", "-a", "-l", NULL);//使用带有'p'的接口
        printf("exec end  ...\n");
        exit(1);
    }

    pid_t rid = waitpid(id, NULL, 0);
    if(rid > 0)
    {
        printf("wait success\n");
    }
    
    exit(1);
}
在这里插入图片描述
在这里插入图片描述

  使用带 ‘p’ 字符的接口,就不需要带替换程序的路径了,只需要替换程序的名字,在OS中会 依照PATH环境变量来寻找该程序

 下面就是带有 ‘v’ 字符的接口,实际上这个v 在参数里表示的是 const char* argv[],我们在main函数里面是见过的,也就是 命令行参数表

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());

    pid_t id = fork();
    if(id == 0)
    {
        char *const argv[] = {
            (char*)"ls",
            (char*)"-a",
            (char*)"-l"
        };
        sleep(3);
        printf("exec begin...\n");
        execv("/usr/bin/ls", argv);//带有 'v' 字符的接口
        printf("exec end  ...\n");
        exit(1);
    }

    pid_t rid = waitpid(id, NULL, 0);
    if(rid > 0)
    {
        printf("wait success\n");
    }
    
    exit(1);
}
在这里插入图片描述
在这里插入图片描述

 最开始我们也见过带有 ‘l’ 字符的接口,它表示的是 list,也就是列表,把需要执行的命令和参数全部放在接口内。

  那么带 ‘vp’ 的其实就是传 程序名,以及参数列表即可:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());

    pid_t id = fork();
    if(id == 0)
    {
        char *const argv[] = {
            (char*)"ls",
            (char*)"-a",
            (char*)"-l"
        };
        sleep(3);
        printf("exec begin...\n");
        execvp("ls", argv);//带 'vp' 的接口
        printf("exec end  ...\n");
        exit(1);
    }

    pid_t rid = waitpid(id, NULL, 0);
    if(rid > 0)
    {
        printf("wait success\n");
    }
    
    exit(1);
}
在这里插入图片描述
在这里插入图片描述

✈️替换本地程序

  我们前面的程序替换全部都是使用系统提供好的程序,我们使用自己写的程序该当何如?

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>

int main(int argc, const char* argv[])
{
    for(int i = 0; argv[i]; ++i)
    {
        printf("argv[%d]:%s\n", i, argv[i]);
    }
    printf("I'm test process!\n");
    printf("I'm test process!\n");
    printf("I'm test process!\n");
    printf("I'm test process!\n");
    printf("I'm test process!\n");
    return 0;
}

  此时我们使用之前学习的make语法已经行不通了,因为无论怎样,只能编译过一个,今天我们来看点别的:

代码语言:javascript
复制
.PHONY:all#由依赖关系无依赖方法
all:myprocess mytest 

mytest:mytest.c
	gcc -o $@ $^ -g -std=c99 
mybin:mybin.c
	gcc -o $@ $^ -g -std=c99

.PHONY:clean
clean:
	rm -f mybin mytest 

  在需要生成多个文件之前使用 .PHONY加上依赖关系,但是不需要依赖方法,这样就能 根据依赖关系 从前到后依次 生成可执行文件

  那么现在我mybin.c文件的子进程要替换 mytest 程序,我们可以这么写:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("I am a process, pid: %d\n", getpid());

    pid_t id = fork();
    if(id == 0){ //child process
        sleep(1);
        printf("exec begin...\n");
        execl("./mytest", "./mytest", "-a", "-b", "-c", NULL);//切换自己写的程序
        printf("exec end  ...\n");
        exit(1);
    }

    pid_t rid = waitpid(id, NULL, 0);
    if(rid > 0)
    {
        printf("wait success\n");
    }
    
    exit(1);
}
在这里插入图片描述
在这里插入图片描述

  当然,这里我是用C语言调用C语言程序,但是我们可以调用其他语言吗?答案是 可以调用 其他语言写的程序。

  这是因为:不论什么语言,运行之后都是进程,只要是进程就都能在Linux下运行

  我们修改test文件,让其打印系统环境变量表:

代码语言:javascript
复制
#include<stdio.h>
#include<stdlib.h>

int main(int argc, const char* argv[], const char
{
    for(int i = 0; env[i]; ++i)
    {
        printf("env[%d]:%s\n", i, env[i]);
    }
    
    return 0;
}

  此时再使用程序替换,让子进程执行这段代码,父进程等待子进程资源回收:

在这里插入图片描述
在这里插入图片描述

  我们也可以使用系统变量 environ,来获取环境变量:

代码语言:javascript
复制
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
    extern char **environ;
    for(int i = 0; environ[i]; ++i)
    {
        printf("env[%d]:%s\n", i, environ[i]);
    }
    
    return 0;
}
在这里插入图片描述
在这里插入图片描述

  我们使用 mybin 文件来执行程序的:

在这里插入图片描述
在这里插入图片描述

  在mybin.c 中,我们并没有传递环境变量表给子进程,但是子进程却能默认拿到环境变量表?

  实际上,子进程会默认拿到父进程环境变量表,那么mybin 也是子进程,是bash的子进程,所以mybin能拿到bash的环境变量,而mybin的子进程可以拿到父进程环境变量:

  我们导入一个新环境变量在系统里以供猜想:

代码语言:javascript
复制
export VAL=youcanseeme
在这里插入图片描述
在这里插入图片描述

  我们在进程地址空间那一节说过,进程地址空间内在 栈的上方存储命令行参数以及环境变量的地方

在这里插入图片描述
在这里插入图片描述

  而在本文的最开始,我们也说了,进程替换替换的仅仅是进程的代码和数据,环境变量是不变的

  如果我们想单纯新增环境变量呢?我们可以使用 putenv:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  此时我在程序内写入了mytest环境变量,但是当我们在系统中查询时:

在这里插入图片描述
在这里插入图片描述

  此时并没有在系统中出现,但是当我们运行程序之后:

在这里插入图片描述
在这里插入图片描述

此时进程内就多了一项mytest的环境变量,而这个环境变量的导入位置是mytest 父进程传给子进程的环境变量,而mytest的父进程是bash,也就是说,在这里bash将从 0-24号环境变量传给了进程mytest,而mytest 使用了putenv新增了环境变量给子进程。

  而现在我想 设置全新的环境变量给子进程,这个时候我们就需要用到带有 ‘e’ 字符的接口了

 接口中还存在带 ‘e’ 字符的接口,e表示的就是 env:const char* env[], 也就需要 环境变量表

在这里插入图片描述
在这里插入图片描述

  其实这是以 覆盖 的方式来传递环境变量,也就相当于子进程设置了全新的环境变量了。

在这里插入图片描述
在这里插入图片描述

  我在最前面总共列举了七个接口,一个程序替换为什么会有这么多的接口呢?但他们的功能都是进行程序替换,所以他们在功能上没有区别。

  他们仅仅是在传参上有区别,其实我们 程序替换的系统调用只有一个,就是 execve 接口,剩下的六个全部都是由这个接口进行封装的。

在这里插入图片描述
在这里插入图片描述

📒✏️总结
  •  进程不仅仅只能运行自己的程序,和可以运行其他程序,使用 exec* 的接口 就可以做到,被称为 程序替换
  •  exec* 接口有七个,他们的 功能全部相同,仅仅是 使用参数不同
  •  子进程会 默认 继承父进程的环境变量表,并 不需要父进程显示传给子进程
  •  程序替换 不看 是什么 语言 的程序,因为 在Linux下运行起来都是进程
  •  七个接口只有 execve 是系统调用,其他6个全是由此接口进行封装。

  创作不易,还望三联支持博主呀~~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🚀execl接口介绍
  • 🚀多进程版本程序替换
  • 🚀其他exec接口
    • ✈️接口介绍
      • ✈️替换本地程序
      • 📒✏️总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档