Linux系统编程——exec族函数
我们来完整、系统、通俗地讲解 Linux 系统编程中非常重要的一类函数:exec
族函数(也叫 exec family)。
一、什么是 exec
?
exec
系列函数的作用是:
用一个新的程序,替换当前进程的内容。
也就是说:
-
你用
fork()
创建了一个子进程, -
然后在子进程里调用
exec()
, -
这时子进程会“变身”,从原来的程序变成你指定的新程序。
这个函数不会创建新进程,而是当前进程的替换与重启。
举个例子:
你写的程序中包含:
execl("/bin/ls", "ls", "-l", NULL);
这行代码会让当前进程(如一个子进程)变成 ls -l
命令的进程,后续你原来的代码都不执行了!
二、exec 函数族成员有哪些?
exec
系列函数一共有 6 个函数,按名字分为 3 种组合:
函数 | 路径是否自动查找? | 参数形式 | 描述 |
---|---|---|---|
execl() | 否 | 列表 | 使用绝对路径 + 参数列表 |
execv() | 否 | 数组 | 使用绝对路径 + 参数数组 |
execle() | 否 | 列表 | 和 execl() 类似,但可传环境变量 |
execve() | 否 | 数组 | 最原始的形式,底层系统调用 |
execlp() | 是 | 列表 | 像 execl() ,但会查 PATH |
execvp() | 是 | 数组 | 像 execv() ,但会查 PATH |
三、参数结构说明(重要)
int execv(const char *path, char *const argv[]);
-
path
:新程序的路径(如/bin/ls
) -
argv[]
:参数数组:-
argv[0]
:程序名(通常写成命令本身,如"ls"
) -
argv[1], argv[2], ..., NULL
:其他参数 -
最后一个必须是 NULL
,表示参数结束
-
四、分类讲解:每个函数的使用方法和例子
1. execl()
— 使用参数“列表”,不自动查找路径
原型:
int execl(const char *path, const char *arg0, ..., NULL);
示例:
execl("/bin/ls", "ls", "-l", "-a", NULL);
说明:
-
用
/bin/ls
程序替换当前进程; -
参数就像
ls -l -a
; -
最后一定要是
NULL
。
2. execv()
— 参数用“数组”,不查路径
原型:
int execv(const char *path, char *const argv[]);
示例:
char *args[] = {"ls", "-l", "-a", NULL};
execv("/bin/ls", args);
说明:
-
路径仍然是
/bin/ls
; -
参数用数组传入;
-
NULL
表示参数结束。
3. execlp()
— 参数列表 + 会查找 PATH 环境变量
原型:
int execlp(const char *file, const char *arg0, ..., NULL);
示例:
execlp("ls", "ls", "-l", NULL);
说明:
-
不用写全路径,系统会在
/bin/
/usr/bin/
等目录下找ls
; -
更加灵活,推荐!
4. execvp()
— 参数数组 + 会查 PATH(最常用!)
原型:
int execvp(const char *file, char *const argv[]);
示例:
char *args[] = {"ls", "-l", NULL};
execvp("ls", args);
说明:
-
会查找
ls
程序的位置; -
使用参数数组,适合处理用户输入;
-
实际中非常常用!
5. execle()
和 execve()
— 带自定义环境变量
-
一般情况用不到,仅当你要给新程序指定新的环境变量时才用。
五、使用 exec 的注意事项
1. exec
成功后,不会返回
因为当前进程已经“变身”为新程序,原来的代码后面就不执行了。
如果它返回了,说明执行失败了。
if (execvp("ls", args) == -1) {perror("exec failed"); // 只有失败才会执行这里
}
2. 参数列表/数组最后必须以 NULL 结尾
否则系统不知道参数在哪里结束,可能导致崩溃。
3. 建议用 execvp()
或 execlp()
因为它们会根据系统环境变量 PATH
查找程序,更灵活,用户体验更好。
4. 配合 fork()
使用
通常我们不会在主进程里直接 exec()
,而是先用 fork()
创建子进程,让子进程执行新的程序:
示例:
pid_t pid = fork();if (pid == 0) {// 子进程:执行 lsexeclp("ls", "ls", "-l", NULL);_exit(1); // exec失败才执行
} else {// 父进程:等子进程wait(NULL);
}
六、exec调用失败常见原因
原因 | 解决方法 |
---|---|
路径写错了 | 检查路径或用 execvp |
没有执行权限 | 用 chmod +x 赋权 |
参数忘记写 NULL | 确保结尾是 NULL |
系统找不到程序 | 确保程序存在于 $PATH 所列目录中 |
七、总结一句话
exec
系列函数是“把自己变成另一个程序”的魔法。
-
不创建新进程,而是“换皮重生”;
-
常用
execlp()
和execvp()
; -
一定配合
fork()
使用,否则你原来的程序会消失。