【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文件混合使用的效果

相关推荐
鸠摩智首席音效师2 小时前
Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead
linux
tianyuanwo2 小时前
DevOps主机管理员视角:企业级Linux主机权限管理策略与实践
linux·服务器·devops
郭涤生2 小时前
Windows + Ubuntu 双系统启动故障修复
linux·windows·ubuntu
三言老师2 小时前
CentOS8 Firewalld 高级规则中级实操教程
linux·运维·服务器·网络·centos
tedcloud1234 小时前
Impeccable 部署指南:开源前端设计工具 Linux 环境搭建实践
linux·运维·服务器·前端·人工智能·开源
风曦Kisaki4 小时前
# Kubernetes(K8s)笔记Day12 :K8s 七层代理(Ingress 和 Ingress Controller)
linux·笔记·云原生·容器·kubernetes
大黄说说4 小时前
SQL Server 执行计划怎么看?快速定位 SQL 慢的根源
java·linux·数据库
yunwei377 小时前
eBPF 教程:精准隔离已建立的 TCP 连接
linux·安全·开源
weixin_445476687 小时前
linux下lims部署详细步骤参考
linux