【知识讲解】 Linux程序替换相关接口讲解


目录

前言

[Part1. exec介绍](#Part1. exec介绍)

[Part2. exec 系列函数分类](#Part2. exec 系列函数分类)

[Part3. execl](#Part3. execl)

[Part4. execlp](#Part4. execlp)

[Part5. execv](#Part5. execv)

[Part6. execvp](#Part6. execvp)

[Part7. execle](#Part7. execle)

[Part8. execve](#Part8. execve)

[Part9. exec相关补充](#Part9. exec相关补充)

[Part10. 结语](#Part10. 结语)


前言

Linux系统中,我们可以进行程序的替换,对于他的理解是非常重要的,接下来来跟随小编的视角来看看吧。


let's go!!!!!!!!


Part1. exec介绍

在 Linux 中,fork() 只能创建一个和父进程代码相同的子进程。如果想让子进程去运行另一个全新的程序,就需要使用 exec 系列函数。


exec 的作用不是创建新进程,而是:
把当前进程的代码段、数据段、堆、栈替换成新程序,让这个进程转而运行另一个程序。


所以:

• fork():创建新进程

• exec():替换进程运行的程序

Linux 中运行一个新程序,通常是:
fork() + exec()
先复制出一个子进程,再让子进程执行别的程序。


Part2. exec 系列函数分类

#include <unistd.h>
exec 不是一个单一函数,而是一组函数:
execl
execlp
execle
execv
execvp
execve


它们的区别可以按三个维度记忆:
维度 含义
l 以可变参数列表传递命令行参数
v 以字符串数组传递命令行参数
p 自动在 PATH 环境变量中查找程序
e 可以传入自定义环境变量


Part3. execl

int execl(const char* path, const char* arg, ...);
参数说明

• path:程序的完整路径

• arg:第一个参数,通常是程序名本身

• ...:后续参数,构成命令行参数列表

• 最后必须以 NULL 结尾


示例
execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
对应命令:
ls -l -a
注意点

1. 第一个参数是完整路径:/usr/bin/ls

2. 第二个参数是 argv0,一般写程序名:"ls"

3. 参数依次往后传

4. 最后必须传 (char*)NULL

5. const char* 传 NULL 时建议强转:(char*)NULL


Part4. execlp

int execlp(const char* file, const char* arg, ...);


execlp 和 execl 的区别是:
函数 是否自动搜索 PATH
execl 否,必须写完整路径
execlp 是,可以直接写程序名


示例
execlp("ls", "ls", "-l", NULL);
系统会在环境变量 PATH 指定的目录中查找 ls,比如:
/usr/bin/ls
/bin/ls
所以 execlp("ls", ...) 能运行,而 execl("ls", ...) 通常会失败。


Part5. execv

int execv(const char* path, char* const argv\[\]);


execv 不使用可变参数列表,而是把参数放到一个字符串数组里。

示例
char* argv\[\] = {"ls", "-l", "-a", NULL};
execv("/usr/bin/ls", argv);
等价于:
ls -l -a
适合参数数量动态变化的场景。


Part6. execvp

int execvp(const char* file, char* const argv\[\]);


execvp = execv + p

也就是:

• 用数组传参

• 自动搜索 PATH

示例
char* argv\[\] = {"ls", "-l", NULL};
execvp("ls", argv);


Part7. execle

int execle(const char* path, const char* arg, ..., char* const envp\[\]);


execle 可以给新程序传入自定义环境变量。

示例
char* envp\[\] = {"HOME=/root", "PATH=/usr/bin", NULL};
execle("/usr/bin/ls", "ls", "-l", NULL, envp);
新程序运行时,就能拿到这些环境变量。


Part8. execve

int execve(const char* path, char* const argv\[\], char* const envp\[\]);


execve 是真正的系统调用,其他 exec 函数通常在底层调用 execve。

参数 说明
path 程序完整路径
argv 命令行参数数组
envp 环境变量数组


Part9. exec相关补充

如果 exec 成功,当前进程的代码已经被替换,原来的代码不会继续执行。
exec 成功,不返回。
失败时

只有 exec 失败时,才会返回 -1,并设置 errno。

execl("/usr/bin/ls", "ls", "-l", NULL);
perror("execl failed");

常见失败原因:

• 文件不存在

• 没有执行权限

• 路径错误

• 参数格式错误

• 内存不足


为什么最后要加 NULL
execl("/usr/bin/ls", "ls", "-l", "-a", NULL);

NULL 的作用是:
标记参数列表结束。因为 execl 是可变参数函数,编译器不知道参数有多少个,必须用 NULL 告诉它参数结束了。


exec 和 fork 的配合

cpp 复制代码
pid_t pid = fork();

if (pid == 0) {
    // 子进程
    execl("/usr/bin/ls", "ls", "-l", NULL);
    perror("execl");
    exit(1);
} else if (pid > 0) {
    // 父进程
    wait(NULL);
}

执行流程:

1. fork() 创建子进程

2. 子进程调用 execl()

3. 子进程的代码被替换成 ls

4. ls 开始运行

5. 父进程 wait() 回收子进程


exec 前后进程身份不变

调用 exec 后,虽然程序换了,但:

• PID 不变

• PPID 不变

• 用户 ID 不变

• 进程组不变

• 大部分文件描述符继续有效

所以 exec 不是"创建新进程",而是"同一个进程运行新程序"。


exec 常用示例

示例 1:运行 ls
execl("/usr/bin/ls", "ls", "-l", NULL);

示例 2:运行 pwd
execlp("pwd", "pwd", NULL);

示例 3:运行 echo
execlp("echo", "echo", "hello", "world", NULL);

示例 4:运行自定义程序
execl("./a.out", "a.out", NULL)


Part10. 结语

这篇文章我们认识并知道了Linux程序替换,接下来,小编还会带来更多Linux相关的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

相关推荐
星源~3 小时前
zephyr-Linux环境下搭建步骤
linux·mcu·嵌入式开发·zephyr
Huangjin007_3 小时前
【Linux 系统篇(二十)】进程(八):进程创建、进程退出
linux·运维·服务器
吴声子夜歌3 小时前
Shell编程——开发规范
linux·运维·shell
Shadow(⊙o⊙)3 小时前
epoll三大接口到内核+正确认识应用层与内核的联系(6000字)
linux·服务器·网络
皓月盈江3 小时前
Linux系统使用VSCode搭建GO开发环境
linux·vscode·ubuntu·golang·debian
CHHH_HHH4 小时前
【Linux系统篇】进程间通信揭秘:从管道到共享内存
linux·服务器·c语言·开发语言·后端·ubuntu
.冰块.4 小时前
华为云计算服务学习笔记|ECS + 镜像 IMS + 弹性伸缩 AS 完整踩坑实录
linux·华为云·ims·as·ecs·弹性云服务器·计算类云服务
一技安身13 小时前
【信创】银河麒麟V10、统信UOS解压7Z文件
linux·运维·服务器
奔跑的架构师14 小时前
[A-50]ARMv9/v8-DVFS系统架构
linux·arm开发·架构·系统架构·arm