exec 函数族
1. 函数族的介绍
- 功能定义:根据指定⽂件名找到可执⾏⽂件,并⽤它取代调⽤进程的内容(代码段、数据段和堆栈等),实现"在调⽤进程内部执⾏可执⾏⽂件"的效果。
- 名称来源:源⾃英⽂单词 “execute” 的前四个字⺟缩写,表示执⾏程序的本质功能。
- 参数要求:需要传递⽂件名或⽂件路径作为参数,且该⽂件必须是可执⾏⽂件。
2. exec 函数族执行过程
- 替换机制:将当前进程的⽤户区数据(代码段、数据段、堆栈等)完全替换为新程序的内容,仅保留进程 ID 等内核区信息。
- 执⾏起点:新程序从 main 函数开始执⾏,完全替代原程序的执⾏流程。
3. exec 函数族与 fork 函数的区别
- 创建⽅式:
- fork 创建新进程(⽗⼦进程并存)
- exec 不创建新进程(原进程内容被替换)
- 组合使⽤:通常先 fork 创建⼦进程,再在⼦进程中调⽤ exec 执⾏新程序,这样既保留⽗进程⼜实现新程序执⾏。
4. exec 函数族原型
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...
/*, (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/*, (char *) NULL */);
int execle(const char *path, const char *arg, ...
/*, (char *) NULL, char *const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
execvpe():
_GNU_SOURCE
(1) 特殊说明:execvpe
属于 GNU 扩展函数,不是 POSIX 标准,使用前必须在文件最顶部定义宏:
#define _GNU_SOURCE
#include <unistd.h>
(2) 全局变量 environ
- 类型:字符串数组,存储当前进程全部环境变量,格式 key=value (环境变量名=环境变量对应的值) ,数组以 NULL 结尾,举几个系统自带的环境变量例子:
PATH=/usr/bin:/bin:/sbin
HOME=/home/chen
USER=root
LANG=en_US.UTF-8
- 无 e 后缀的 exec 函数,会默认复用该全局环境变量;带 e 后缀的会替换为自定义 envp
(3) 返回值
- 成功:调用成功后,当前进程代码段、数据段、堆栈全部被新程序替换,原有代码不再执行,因此无返回值。
- 失败:返回 -1,同时设置 errno(文件不存在、权限不足、参数错误等)
5. exec 函数族描述
exec 函数族会用新的进程映像替换当前进程映像。这些函数的第一个参数是要执行的文件名。
可以根据 “exec” 前缀后的字母将这些函数分为几组:
- l(list):参数地址列表,以空指针结尾 – execl(), execlp(), execle()
const char *arg 和后续省略号可以看作是 arg0 , arg1 , … , argn 。它们共同描述了一个或多个指向以空字符结尾字符串的指针列表,代表可供执行程序使用的参数列表。第一个参数,按照惯例,应该指向与要执行的文件关联的文件名。参数列表必须以空指针终止,由于这些是可变参数函数,因此这个指针必须强制转换为 (char *) NULL 。
与 ‘l’ 函数相比,‘v’ 函数(如下所述)通过向量形式指定执行程序的命令行参数。
-
v(vector):存有各参数地址的指针数组的地址 – execv(), execvp(), execvpe()
char *const argv[] 参数是一个指向以空字符结尾字符串的指针数组,代表新程序可用的参数列表。第一个参数,按照惯例,应该指向与要执行的文件关联的文件名。指针数组必须以空指针终止。 -
e(environment):存有环境变量字符串地址的指针数组的地址 – execle(), execvpe()
新进程映像的环境是通过 envp 参数指定的。 envp 参数是一个指向以空字符结尾字符串的指针数组,必须以空指针终止。
所有其他不含 ‘e’ 后缀的 exec() 函数都从调用进程的外部变量 environ 中获取新进程映像的环境。
- p(path):存有环境变量字符串地址的指针数组的地址 – execlp(), execvp(), execvpe()
如果指定的文件名不包含斜杠(/),则这些函数会模仿 shell 的行为来搜索可执行文件。文件将在 PATH 环境变量中指定的冒号分隔目录路径列表中查找。如果未定义此变量,默认情况下,路径列表包括由 confstr(_CS_PATH) 返回的值(通常是 /bin:/usr/bin )以及可能还包括当前工作目录;详情见版本说明。
6. exec 函数族样例
(1) 输出 environ 数组内容
创建 test.c 程序
#include <unistd.h>
#include <stdio.h>
#include <string.h>
extern char **environ; // 声明全局环境变量数组
int main()
{
char **p = environ;
while(*p != NULL)
{
printf("%s\\n",*p);
p++;
}
return 0;
}
编译并运行 test.c 程序
gcc test.c -o test
./test
可以看到 environ 数组的具体内容:

(2) execl 函数
参数:
- path(const char *):指定要执行的文件路径或名称。可以使用相对路径或绝对路径。
- arg(可变参数列表 const char *):提供执行程序所需的参数列表。
- 第一个参数通常无实际作用,惯例上写执行程序名称。
- 从第二个参数开始为程序真正需要的参数。
- 必须以 NULL 指针结尾 (作为参数结束的哨兵标志)
样例:
创建一个 hello.c 文件:
#include <stdio.h>
int main()
{
printf("Hello,Linux!\\n");
return 0;
}
创建一个 execl.c 文件:
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = fork();
if(pid > 0)
{
printf("我是父进程, pid: %d\\n",getpid());
}
else if(pid == 0)
{
printf("我是子进程, pid: %d\\n", getpid());
execl("./hello", "hello", NULL);
}
else
{
perror("fork");
return –1;
}
for(int i = 0; i < 3; i++)
{
printf("i = %d\\n", i);
}
return 0;
}
编译并运行以上文件:
gcc hello.c -o hello
gcc execl.c -o execl
./execl
可以看到短暂出现了孤儿进程的状况,原因是因为父进程先结束,所以shell 提示符提前出现。

(3) execlp 函数
函数特点:
- 自动搜索 PATH 环境变量。
- 只需提供文件名而非完整路径
参数:
- file (const char *):待执行程序的文件名(无需路径,依赖 PATH 查找)
- arg(可变参数列表 const char *):提供执行程序所需的参数列表。
- 第一个参数通常无实际作用,惯例上写执行程序名称。
- 从第二个参数开始为程序真正需要的参数。
- 必须以 NULL 指针结尾 (作为参数结束的哨兵标志)
样例:
查看 ps 命令是否在环境变量:
which ps
可以看到这个路径在 PATH 中:

创建一个 execlp.c 文件:
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = fork();
if(pid > 0)
{
printf("我是父进程, pid: %d\\n",getpid());
}
else if(pid == 0)
{
printf("我是子进程, pid: %d\\n", getpid());
execlp("ps", "ps", "aux", NULL);
}
else
{
perror("fork");
return –1;
}
for(int i = 0; i < 3; i++)
{
printf("i = %d\\n", i);
}
return 0;
}
编译并运行 execlp 程序:
gcc execlp.c -o execlp
./execlp

(4) execv 函数
参数:
- path (const char *):指定要执行的文件路径或名称。可以使用相对路径或绝对路径。
- argv[] (char *const):参数字符串数组,包含程序执行所需的参数列表。参数列表最后必须以 NULL 结束。
样例:
创建一个 execv.c 文件:
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = fork();
if(pid > 0)
{
printf("我是父进程, pid: %d\\n",getpid());
}
else if(pid == 0)
{
printf("我是子进程, pid: %d\\n", getpid());
char *p[] = {"ps", "aux", NULL};
execv("/usr/bin/ps", p);
}
else
{
perror("fork");
return –1;
}
for(int i = 0; i < 3; i++)
{
printf("i = %d\\n", i);
}
return 0;
}
编译并运行 execv 程序:
gcc execv.c -o execv

(5) execvpe 函数
execvpe 查找提供的第一个参数文件名,依然会在 PATH 中寻找,同时设置新的环境变量,但是设置的这个环境变量不能在此次寻找可执行文件的时候用上,只会在成功找到并执行新程序之后生效!!!
因此如果在其他目录放可执行程序,想通过 execvpe 函数设置的新环境变量,只提供文件名就找到并运行该程序,是无法做到的!!!
注意:
- 兼容性限制(GNU 扩展,非 POSIX 标准)
execvpe 不是标准 C/POSIX 函数,是 Linux glibc 拓展,使用前必须在代码最顶部定义宏:
#define _GNU_SOURCE
#include <unistd.h>
不定义宏会编译警告、找不到函数符号。
- 特殊规则:file 带斜杠时跳过 PATH 检索
如果 file 写 ./hello(包含/),不会遍历 PATH,直接按相对路径打开文件,和 execl 行为一致。
参数:
- file (const char *):待执行程序的文件名(无需路径,依赖 PATH 查找)。
- argv[] (char *const ):参数字符串数组,包含程序执行所需的参数列表。参数列表最后必须以 NULL 结束。
- envp[] (char *const):自定义环境变量字符串数组,每条格式 KEY=VALUE,数组末尾为 NULL。
样例:
在 ~/Linux 目录下创建一个 a.c 文件:
#include <stdio.h>
int main()
{
printf("666\\n");
return 0;
}
编译 a.c 文件:
gcc a.c

创建一个 execvpe.c 文件:
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = fork();
if(pid > 0)
{
printf("我是父进程, pid: %d\\n",getpid());
sleep(2);
}
else if(pid == 0)
{
printf("我是子进程, pid: %d\\n", getpid());
char *argv[] = {"env", NULL};
char *envir[] = {"PATH=/home/ubuntu/Linux/:/home/ubuntu/Linux/lesson19/", NULL};
execvpe("env", argv, envir);
perror("execvpe");
}
else
{
perror("fork");
return –1;
}
for(int i = 0; i < 3; i++)
{
printf("i = %d\\n", i);
}
return 0;
}
编译并运行 execvpe.c :
gcc execvpe.c -o execvpe
./execvpe
这里使用 sleep(2) 延缓父进程的结束,使终端显示工整些,输出的 i=… 属于父进程。






