exec函数族是把当前进程映像替换成新的程序文件

exec 函数族会根据指定的文件名或目录名查询可执行文件,找到后会使用它去取代原调用进程的数据段、代码段、堆栈段。在执行完毕后,原调用进程的内容除进程号 pid 外全部被新的进程所替换。另外,这里的可执行文件可以是二进制文件,也可以是Linux系统下任何可执行的脚本程序。

实际上,Linux中并没有 exec 函数,而是有6个以 exec 开头的函数组成 exec 函数族。

#include <unistd.h>
int execl(const char *path, const char *arg, ...)
int execlp(const char *path, const char *arg, ...)
int execle(const char *path, const char *arg, ..., char *const envp[])
int execv(const char *path, char *const argv[])
int execvp(const char *file, char *const argv[])
int execve(const char *path, char *const argv[], char *const envp[])

简单来说,exec函数族都是以exec开头,后面跟的不同字母表示不同的涵义:

l 表示 list,指代的是命令行参数列表。 p 表示 path,指定搜索文件file时所使用的path变量。 v 表示vector,指代的是命令行参数数组。 e 表示 environment,指代的是环境变量数组。 path 表示要执行的程序路径,可以是绝对路径或是相对路径。 file 表示要执行的程序名称,如果该参数中包含/字符则视为路径名并直接执行,否则则视为单独的文件名,系统将会根据环境变量PATH中设置的路径顺序去搜索指定的文件。 argv 表示命令行参数的矢量数组 envp 表示带有该参数的exec函数可以在调用时指定一个环境变量数组,其他不带该参数的exec函数则使用调用进程的环境变量。 arg 表示程序的第0个参数,也就是程序名本身,相当于argv[0]... 表示命令行参数列表,调用相应程序时有多少个命令行参数就需要有多少个输入参数项。

函数执行成功不会返回,若执行失败则返回-1,失败原因会记录在error中。

事实上,这6个函数中真正的系统调用只有execve函数,其它5个都是库函数,它们最终都会调用execve这个系统调用。

int execl(const char *path, const char *arg, ...);

execl中的l表示list即命令行参数列表,它的功能是通过“路径+文件名”的方式来加载一个进程,参数path字符指针指向要执行的文件路径,参数arg表示文件名称,参数...表示可变参数,可变参数必须使用NULL结尾。

exec("/bin/ls", "ls", "-a", "-l", NULL)
$ vim execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
    printf("parent process begin\n");
    pid_t pid = vfork();
    if(pid == -1) 
        perror("vfork fail");
        exit(1);
    else if(pid == 0)
        printf("child process begin\n");
        if(execl("/bin/ls", "ls", "-l", NULL) == -1) 
            perror("execl fail");
            _exit(1);
    sleep(1);
    printf("parent process end\n");
    return 0;
$ gcc -o execl.out execl.c
$ ./execl.out
parent process begin
child process begin
总用量 16
-rw-r--r-- 1 jc jc  400 3月   6 00:32 execl.c
-rwxr-xr-x 1 jc jc 8544 3月   6 00:33 execl.out
parent process end

注意:vfork函数所创建的子进程里直接调用exec函数,则立即启动另一个进程取代其自身,这比调用fork函数完成同样的工作要快的多。

execl("/bin/ls", "ls", "-l", NULL) == -1
execl("/bin/ls", "ls", "-l", (char *)0) == -1

这两种写法中如果使用常量0来表示一个空指针,则必须将它强制转换为一个字符指针,否则它将解释为整形参数。如果一个整形参数的长度与char *的长度不同,那么exec函数的实际参数将会出错。如果函数调用成功,进程自己的执行代码会变成加载程序的代码,execl()后面的代码也就不会再执行。