019 进程控制 —— 进程程序替换

AI-摘要
小米里的大麦 GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
019 进程控制 —— 进程程序替换
小米里的大麦进程控制 —— 进程程序替换
1. 替换原理
进程程序替换是指在一个已经存在的进程中,通过系统调用将当前进程的代码、数据等全部替换为新程序的内容,也就是说,新程序加载到当前进程的地址空间中,原来进程的内容被完全覆盖。在这一过程中,进程的 PID
保持不变,但内存空间、寄存器的内容和代码逻辑均变为新程序的内容。
【关键点】
- 进程替换不创建新进程,而是复用现有进程的
PID
,这对需要快速切换任务、降低资源开销有很大好处。 - 一旦替换成功,原进程的代码 立即终止,从新程序的
main
函数开始执行。 - 替换过程会关闭原进程中打开的文件描述符(除非这些描述符被标记为在 exec 之后保留),
fcntl(fd, F_SETFD, FD_CLOEXEC)
标记文件描述符在exec
后关闭(了解)。
2. execl 的单进程使用
1. 函数原型
1 | int execl(const char* path, const char* arg, ..., (char*)NULL); |
参数解析:
path
:新程序的绝对路径(如/bin/ls
),必须明确指定位置!arg
:命令行参数列表,第一个参数通常是程序名,最后必须加NULL
表示结束。
1 |
|
- 替换成功后,
printf("原进程...")
之后的代码 不再执行。 - 若替换失败(如路径错误),
perror
会输出错误信息。
运行示例:
3. execl 的多进程使用
我们先 fork()
创建一个 子进程,在子进程中使用 execl()
替换自身为另一个程序,而 父进程继续执行原有逻辑。这种方式不会影响父进程,同时能让子进程跑一个完全不同的程序,是 非常经典的进程控制模式。
1 |
|
运行示例:
1 | 我是父进程,PID: 18882,创建了子进程: 18883 |
4. exec
函数“全家桶”
Linux 下有 7 种以 exec
开头的函数,统称 exec
函数。
- 系统调用类:
exec
是由内核提供的系统调用,属于man
手册的第 2 章节。 - 库函数类:其他
exec
函数均是 C 标准库对execve
的封装,属于man
手册第 3 章节。
exec
函数族成员一览(记熟)
函数名 | 参数类型 | 使用说明 |
---|---|---|
execl | 列出参数(arg0, arg1, …, NULL) | 最常用,适合参数个数固定 ✅ |
execv | 参数数组(char * argv []) | 参数可变,用数组表示(* *程序参数动态构造(任务调度器)**) |
execle | 列出参数 + 环境变量(envp) | 适合需要指定环境变量 |
execve | 参数数组 + 环境变量 | 最底层函数,系统调用接口 ✅ |
execlp | 自动查 $PATH + 列出参数 |
不指定绝对路径,依赖环境 PATH ✅ |
execvp | 自动查 $PATH + 参数数组 |
最常见 Shell 结构 ✅(支持 PATH 查找 + 动态参数) |
execvpe | 自动查 $PATH + 参数数组 + envp |
非标准,GNU 扩展 |
记忆:
- l(list):表示参数采用列表。
- v(vector):参数用数组。
- p(path):有 p 自动搜索环境变量 PATH。
- e(env):表示自己维护环境变量。
1. man
手册查询
一般来说使用 man 3 exec
(6 个)和 man 2 execve
(1 个)就能进行查询,但是不妨有朋友像我一样被提示“No manual entry for execl
”,如何解决?根据搜索,使用命令 sudo yum install man-pages man-pages-posix
即可(如果是其他情况还请自行搜索)。
2. 核心函数详解(实战场景)
① execl
(列表传参 + 绝对路径)
1 | execl("/bin/ls", "ls", "-l", NULL); // 执行 /bin/ls -l |
- 重点:必须指定完整路径,参数以列表形式传递,末尾必须加
NULL
。
② execlp
(列表传参 + 自动搜索 PATH
)
1 | execlp("ls", "ls", "-l", NULL); // 执行系统命令 ls(自动搜索 PATH 环境变量) |
- 优势:直接使用命令名(如
ls
),无需写绝对路径。
③ execle
(列表传参 + 自定义环境变量)
1 | // 自定义环境变量并执行程序 |
- 应用场景:需要为子进程指定独立环境变量(如容器化任务)。
④ execv
(数组传参 + 绝对路径)
1 | // 参数以数组形式传递 |
- 适用场景:参数动态生成(如从用户输入或配置文件读取)。
⑤ execvp
(数组传参 + 自动搜索 PATH
)
1 | // 动态参数数组 + 自动搜索 PATH |
- 高频用法:实现类似 Shell 的功能(动态解析命令参数)。
⑥ execvpe
(数组传参 + 搜索 PATH
+ 自定义环境变量)
1 | // GNU 扩展,非所有系统支持 |
- 注意:
execvpe
是GNU
扩展函数,需确认系统支持(如 Linux 可用)。
⑦ execve
(系统调用 + 完全控制)
1 | // 系统调用级函数,直接控制环境变量和参数 |
- 本质:其他
exec
函数最终调用execve
实现功能。
[!NOTE]
所有
exec
函数 成功时不返回,失败时返回-1
! 检查返回值并处理错误:
1
2
3
4
5 if (execl(...) == -1)
{
perror("执行失败!");
exit(1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int main()
{
char* const argv[] = { "ps", "-ef", NULL };
char* const envp[] = { "PATH=/bin:/usr/bin", "TERM=console", NULL };
// execl: 需要完整路径
if (execl("/bin/ps", "ps", "-ef", NULL) == -1)
{
perror("execl");
exit(1);
}
// execlp: 使用 PATH 环境变量
if (execlp("ps", "ps", "-ef", NULL) == -1)
{
perror("execlp");
exit(1);
}
// execle: 需要自己设置环境变量
if (execle("ps", "ps", "-ef", NULL, envp) == -1)
{
perror("execle");
exit(1);
}
// execv: 参数通过数组传递
if (execv("/bin/ps", argv) == -1)
{
perror("execv");
exit(1);
}
// execvp: 使用 PATH 环境变量
if (execvp("ps", argv) == -1)
{
perror("execvp");
exit(1);
}
// execve: 需要自己设置环境变量
if (execve("/bin/ps", argv, envp) == -1)
{
perror("execve");
exit(1);
}
return 0;
}
5. 部分代码实战
1. 使用 execvp
执行动态命令
1 |
|
- 输出:执行
ls -l -a
,参数通过数组动态传递。
2. 使用 execle
自定义环境变量
1 |
|
- 作用:子进程的环境变量被替换为
env
数组中的内容。
6. 代码验证 exec
执行系统命令和自定义命令
1. 执行系统命令(如 ls
)
1 |
|
执行结果:输出当前目录的文件列表(与终端直接运行 ls -l
效果一致)。
2. 执行自定义程序(如编译后的 my_program
)
1 |
|
关键点:
- 路径必须正确:需指定自定义程序的绝对路径或确保其在
PATH
环境变量中。 - 参数自由传递:可传递任意参数给自定义程序(
my_program
的main
函数接收这些参数)。
7. 向子进程传递环境变量
1. 使用 execle
或 execvpe
自定义环境变量
1 |
|
验证方法:在 ls
程序中无法直接看到环境变量(因为 ls
不读取这些变量),但可通过以下方式验证:
1 | // 编写一个测试程序 test_env.c |
8. 环境变量是覆盖还是追加?
1. 核心规则
- 若显式传递环境变量(如
execle
或execve
):完全覆盖 父进程的环境变量,子进程仅保留传递的环境变量。 - 若不传递环境变量(如
execlp
或execv
):继承父进程的所有环境变量。
2. 示例验证
1 |
|
test_env
程序输出:
1 | CHILD_ENV=child_value |
9. 总结
1. 核心原理
- 不创建新进程:复用现有进程的
PID
和资源(文件描述符等),仅替换代码和数据段。 - 执行即替换:成功后原进程代码立即终止,从新程序的
main
开始执行。 - 资源处理:默认关闭未标记的文件描述符,避免资源泄漏。
2. 关键函数实战场景
函数名 | 参数形式 | 路径搜索 | 环境变量 | 适用场景 |
---|---|---|---|---|
execl | 列表传参 | 否 | 继承父进程 | 参数固定 + 绝对路径(如 /bin/ls ) |
execlp | 列表传参 | 是 | 继承父进程 | 执行系统命令(如 ls ,依赖 PATH ) |
execle | 列表传参 | 否 | 自定义覆盖 | 需要独立环境变量(如容器化任务) |
execv | 数组传参 | 否 | 继承父进程 | 动态生成参数(如用户输入解析) |
execvp | 数组传参 | 是 | 继承父进程 | Shell 类动态命令执行(如 ls -l -a ) |
execve | 数组传参 | 否 | 自定义覆盖 | 系统级控制(底层实现) |
3. 环境变量规则
- 覆盖规则:使用
execle
或execve
时,子进程环境变量完全替换为传入的数组。 - 继承规则:默认继承父进程环境变量,适用于
execl
、execv
、execlp
、execvp
。
4. 实战口诀
- 列表传参用
l
,数组传参用v
- 自动搜索加
p
,环境变量加e
- 路径要对、参数要全、NULL 收尾、错误要检!
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果