在 Linux 系统编程中,exec
函数族 用于在一个进程中替换当前运行的程序为另一个新的程序。它与 fork()
配合使用,是实现多进程编程、启动子进程执行外部命令的核心机制。
目录
一、exec 函数族概述
二、exec 函数族成员
三、函数原型详解
1. execl()
示例:
2. execlp()
示例:
3. execv()
示例:
4. execvp()
示例:
5. execle()
示例:
四、exec 执行流程图解(知识树状图)
五、exec 的使用场景
1. 在子进程中执行新程序
示例代码:
编译运行:
六、exec 返回值说明
七、exec 与 fork 的经典组合模式
八、exec 的高级用法
1. 使用 exec 替换当前 shell 进程
2. 重定向标准输入输出后执行
九、总结知识点图解(知识树状图)
十、课后练习建议
exec
并不是一个单独的函数,而是一组具有相似功能的系统调用,它们的功能都是将当前进程映像替换为一个新的可执行文件(如 /bin/ls
, /usr/bin/python
等)。
注意:
exec
不会创建新进程,而是替换当前进程的代码段、数据段、堆栈等,因此调用成功后不会返回原程序。
以下是常见的六个 exec
函数(定义在
中):
函数名 | 参数形式 | 是否使用环境变量 PATH |
---|---|---|
execl() |
列表(参数逐个传入) | 否 |
execlp() |
列表 | 是(自动查找 PATH) |
execv() |
数组(char *argv[]) | 否 |
execvp() |
数组 | 是 |
execle() |
列表 + 指定环境变量 | 否 |
execvpe() (非标准) |
数组 + 指定环境变量 | 是 |
execl()
int execl(const char *path, const char *arg0, ..., /* (char *) NULL */);
path
:要执行的程序路径(绝对或相对)arg0, arg1, ...
:程序的命令行参数,以 NULL
结束execl("/bin/ls", "ls", "-l", NULL);
execlp()
int execlp(const char *file, const char *arg0, ..., /* (char *) NULL */);
file
:程序名(不带路径也可,会搜索 PATH)execlp("ls", "ls", "-a", NULL);
execv()
int execv(const char *path, char *const argv[]);
argv[]
:一个指向参数字符串数组的指针,最后一个元素必须为 NULL
char *args[] = {"ls", "-l", "/home", NULL};
execv("/bin/ls", args);
execvp()
int execvp(const char *file, char *const argv[]);
execv()
,但支持 PATH 环境变量查找char *args[] = {"python3", "script.py", NULL};
execvp(args[0], args);
execle()
int execle(const char *path, const char *arg0, ..., /* (char *) NULL, char * const envp[] */);
char *env[] = {"PATH=/bin:/usr/bin", "USER=test", NULL};
execle("/bin/echo", "echo", "Hello World", NULL, env);
+----------------------------+
| 当前进程 |
+----------------------------+
|
v
+----------------------------+
| 调用 exec 函数 |
| 如:execl(), execv() 等 |
+----------------------------+
|
v
+----------------------------+
| 内核加载并执行新程序 |
| (替换当前进程的代码和数据)|
+----------------------------+
|
v
+----------------------------+
| 新程序开始运行 |
| 原进程上下文被完全替换 |
+----------------------------+
这是最常见的使用方式:先 fork()
创建子进程,然后在子进程中调用 exec
执行新程序。
#include
#include
#include
#include
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("Fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process is about to exec ls -l\n");
execl("/bin/ls", "ls", "-l", NULL);
// 如果 exec 成功,下面这句不会执行
perror("exec failed");
return 1;
} else {
// 父进程等待子进程结束
wait(NULL);
printf("Parent: Child process finished.\n");
}
return 0;
}
gcc exec_example.c -o exec_example
./exec_example
输出示例:
Child process is about to exec ls -l
total 8
-rwxr-xr-x 1 user user 8672 Jul 4 10:00 exec_example
Parent: Child process finished.
-1
,并设置 errno
错误码常见错误包括:
errno 值 | 含义 |
---|---|
ENOENT | 文件不存在 |
EACCES | 权限不足 |
ENOMEM | 内存不足 |
ELOOP | 解析路径时遇到循环符号链接 |
ETXTBSY | 程序正在被执行,无法修改 |
父进程
|
v
fork()
├─→ 父进程继续执行(通常调用 wait 等待)
└─→ 子进程调用 exec 执行新程序
这种模式是构建 shell、守护进程、服务管理器等程序的基础。
exec
替换当前 shell 进程在 Shell 脚本中也可以使用 exec
来替换当前 shell:
#!/bin/bash
exec python3 myscript.py
脚本运行后,bash 进程会被 Python 替换。
exec > output.txt
exec /bin/ls -l
这样 ls
的输出会被写入到 output.txt
。
exec 函数族
│
├── 功能:替换当前进程为新程序
│
├── 常见函数
│ ├── execl(), execlp()
│ ├── execv(), execvp()
│ ├── execle(), execvpe()
│
├── 参数传递方式
│ ├── 列表(execl 系列)
│ └── 数组(execv 系列)
│
├── 环境变量控制
│ ├── 默认继承当前环境变量
│ └── 可通过 execle 等指定新环境
│
├── 成功执行后不会返回
│
├── 最佳实践
│ ├── fork() 创建子进程
│ └── exec() 替换子进程执行新程序
│
└── 错误处理
├── 必须检查返回值
└── 失败时返回 -1,并设置 errno
exec
执行 ps aux
,观察输出。execv()
实现一个简单的 shell 命令解析器(输入命令后执行)。exec
替换当前进程为 Python 或其他语言解释器。exec
失败(如执行不存在的程序),并打印对应的 errno
和错误信息。strace
跟踪 exec 的系统调用行为:strace -f ./your_exec_program