【Linux】进程程序替换

1、程序替换现象

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    printf("I am a process\n");

    // 执行ls -a -l 程序,替换当前进程映像
    execl("/usr/bin/ls", "ls", "-a", "-l", NULL);

    // execl调用成功后下面两行永远不会执行
    printf("代码运行中...\n");
    printf("代码运行中...\n");

    return 0;
}

调用程序替换函数后,进程没有执行下面的printf输出,而是去执行了ls命令,所以可以看出,程序替换函数execl可以用其他进程替换当前的进程

2、替换原理

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新进程,所以调用 exec 前后该进程的pid为改变

替换的完整流程:

  1. 一个进程拥有独立的PCB、虚拟地址空间
  2. 调用exec函数后,操作系统从磁盘读取目标可执行程序
  3. 直接覆盖当前进程的虚拟地址空间里所有原代码、数据、堆栈内容
  4. 页面重新映射到新程序对应的物理内存
  5. CPU跳转到新程序的入口函数开始运行

注意 :整个过程不创建新进程,替换前后getpid() 获取的进程ID完全相同,exec调用成功后,原程序后续代码全被覆盖

exec函数返回值:

  • 调用成功:无返回值,原代码直接消失;
  • 调用失败:返回 - 1,回到原程序继续向下执行

3、exec函数

一共有六种以exec开头的函数:

cpp 复制代码
#include <unistd.h> // 头文件依赖

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

六个函数命名字母有固定含义:

字母 含义
l list,参数以可变参数列表传递,末尾必须填NULL
v vector,参数存字符串数组,数组最后一个元素为NULL
p path,自动读取环境变量PATH,只需传程序名不用写完整路径
e environ,自定义环境变量,手动传入环境变量数组

3.1 execl

cpp 复制代码
int execl(const char *path, const char *arg, ...);
  • path:目标程序完整绝对路径(如/usr/bin/ls
  • arg...:可变参数列表,命令行参数依次填写,最后必须以NULL结尾
  • 示例:execl("/usr/bin/ls", "ls", "-l", "-a", NULL);

3.2 execlp

cpp 复制代码
int execlp(const char *file, const char *arg, ...);
  • 带p:自动搜索PATH环境变量,第一个参数直接写程序名即可
  • 传参方式和execl一致,可变参数 + NULL收尾
  • 示例:execlp("ls", "ls", "-l", NULL);

3.3 execle

cpp 复制代码
int execle(const char *path, const char *arg, ..., char *const envp[]);
  • l:列表传参,e:自定义环境变量
  • path需要完整绝对路径

3.4 execv

cpp 复制代码
int execv(const char *path, char *const argv[]);
  • v:数组传参,所有命令参数提前单独放到字符串数组中
  • path:依旧需要完整绝对路径

示例:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    printf("I am a process\n");
    pid_t id = fork();

    if (id == 0)
    {
        sleep(2);
        printf("下面的代码都是子进程\n");
        // 命令行参数单独写入字符串数组中
        char *argv[] = {
            (char *)"ls",
            (char *)"-l",
            (char *)"--color=auto",
            NULL
        };
        execv("/usr/bin/ls", argv);
        // execv执行失败才会执行下面,打印错误信息
        perror("execv 执行失败");
        exit(1);
    }

    // 等待子进程结束
    wait(NULL);
    printf("代码运行中...\n");
    printf("代码运行中...\n");
    return 0;
}

3.5 execvp

cpp 复制代码
int execvp(const char *file, char *const argv[]);
  • v:数组传参,p:自动匹配PATH
  • file:只需要传程序名
  • argv:参数封装数组

示例:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    printf("I am a process\n");
    pid_t id = fork();

    if (id == 0)
    {
        sleep(2);
        printf("下面的代码都是子进程\n");
        char *argv[] = {
            (char *)"ls",
            (char *)"-l",
            (char *)"--color=auto",
            NULL
        };
        execvp(argv[0], argv);

        // execvp执行成功不会走到这里,失败才执行exit
        exit(1);
    }

    // 父进程逻辑
    wait(NULL);  // 阻塞等待子进程执行完毕
    printf("代码运行中...\n");
    printf("代码运行中...\n");

    return 0;
}
  • argv0:命令名,也就是"ls"

3.6 execvpe

cpp 复制代码
int execvpe(const char *file, char *const argv[], char *const envp[]);
  • 集齐v+p+e,数组传参、自动PATH、自定义环境变量
cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    printf("I am a process\n");
    pid_t id = fork();

    if (id == 0)
    {
        sleep(2);
        printf("下面的代码都是子进程\n");
        // 命令参数数组
        char *argv[] = {
            (char *)"othercmd",
            (char *)"-a",
            (char *)"-b",
            NULL
        };
        // 自定义环境变量数组,以NULL结尾
        char *envp[] = {
            (char*)"PATH=/home/me/lesson6/cmd",
            NULL
        };

        // 使用 execvpe 替换程序,第一个参数写完整绝对路径
        execvpe("/home/me/lesson6/cmd/othercmd", argv, envp);

        // 执行失败才会走到这里
        perror("execvpe failed");
        exit(1);
    }

    // 父进程等待子进程结束
    wait(NULL);
    printf("代码运行中...\n");
    printf("代码运行中...\n");

    return 0;
}
cpp 复制代码
// othercmd.cpp
#include <iostream>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[], char* env[])
{
    printf("I am a process\n");
    for (int i = 0; argv[i]; i++)
    {
        cout << i << ":" << argv[i] << endl;
    }
    cout << "环境变量" << endl;
    for (int i = 0; env[i]; i++)
    {
        cout << i << ":" << env[i] << endl;
    }

    return 0;
}

系统中的环境变量被修改成了我们自定义的环境变量,还可以用这种方式运行.cpp文件,达到了.c文件和.cpp文件混合使用的效果

相关推荐
张小姐的猫9 分钟前
【Linux】网络编程 —— 手动部署 & 测试内网穿透
linux·运维·服务器·网络·tcp/ip
深念Y16 分钟前
ZTE B860AV1.1-T2 机顶盒改造记录:从 Android 到 Linux 服务器
android·linux·运维·服务器·boot·cma·机顶盒
祖力5519 分钟前
进程相关的函数与exec函数簇
linux·运维·服务器·进程·exec函数簇·进程相关的函数
Discipline~Hai21 分钟前
Linux应用软件编程04-线程
linux·服务器·c语言·开发语言·linux应用软件编程
charlie11451419125 分钟前
Cinux · musl 静态移植:对齐 Linux ABI、铺初始栈,以及一个被 SMAP 拦下的潜伏 bug
linux·开发语言·c++·操作系统·开源项目
2401_868534789 小时前
指令集-最常用 Linux 命令速查手册(整合版)
linux·网络协议
ltl9 小时前
小文件问题:文件数量如何放大元数据与 I/O 成本
linux
ltl9 小时前
网络延迟优化:Nagle、TCP_NODELAY 与中断亲和
linux
张文君9 小时前
ubuntu26.04从ext4改成mdadm的raid1+lvm启动
linux·运维·网络