在 Linux 中,exec 指的是一组函数,一共有 6 个。其中只有 execve() 是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。当进程调用一种 exec 函数时,该进程完全由新程序替换,而新程序则从其 main 函数开始执行。因为调用 exec 并不创建新进程,所以前后的进程 ID (当然还有父进程号、进程组号、当前工作目录……)并未改变。exec 只是用另一个新程序替换了当前进程的正文、数据、堆和栈段(进程替换)。
区别
/* 完整的路径加文件名,
* 使用列表传参的方式 */
int execl(const char *path, const char *arg, ...);
/* 完整的路径加文件名,
* 使用字符串数组传参(argv) */
int execv(const char *path, char *const argv[]);
/* 可以直接使用可执行文件的文件名,不用带路径,程序会在环境变量 PATH 里查找该执行程序。
* 使用列表传参 */
int execlp(const char *file, const char *arg, ...);
/* 可以直接使用可执行文件的文件名,不用带路径,程序会在环境变量 PATH 里查找该执行程序。
* 使用字符串数组传参(argv) */
int execvp(const char *file, char *const argv[]);
/* 完整的路径加文件名,
* 使用列表传参的方式。
* 可使用字符串数组 envp 改变替换程序的环境变量,正确来说,让替换程序只保留 envp 的环境变量 */
int execle(const char *path, const char *arg, ..., char * const envp[]);
/* 完整的路径加文件名,
* 使用字符串数组传参(argv)
* 可使用字符串数组 envp 改变替换程序的环境变量,正确来说,让替换程序只保留 envp 的环境变量 */
int execve(const char *path, char *const argv[], char *const envp[]);
execl、execv、execlp和execvp代码
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("进程替换之前.......\n");
sleep(5);
/* 可执行程序的全路径,参数列表传参,以 NULL 结束传参 */
//execl("/bin/ls","ls","-al",NULL);
/* 可执行程序的全路径,字符串数组传参,以 NULL 结束传参 */
//char* argv[]={"ls","-al",NULL};
//execv("/bin/ls",argv);
/* 只需可执行程序的文件名,参数列表传参,以 NULL 结束传参 */
//execlp("ls","ls","-al",NULL);
/* 只需可执行程序的文件名,字符串数组传参,以 NULL 结束传参 */
char* argv[]={"ls","-al",NULL};
execvp("ls",argv);
/* exec 函数族与一般的函数不同,exec 函数族中的函数执行成功后不会返回,
* 而且,exec 函数族下面的代码执行不到。
* 只有调用失败了,它们才会返回 -1,失败后从原程序的调用点接着往下执行。 */
perror("execl");
return 0;
}
四种方式的执行结果均如下,可见程序确实替换为 ls ,并按照指定参数执行。
[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study
进程替换之前.......
总用量 64
drwxr-xr-x 6 lingyun lingyun 4096 8月 3 11:20 .
drwxr-xr-x 6 lingyun lingyun 4096 8月 1 20:16 ..
drwxr-xr-x 2 lingyun lingyun 4096 4月 20 09:55 include
-rw-r--r-- 1 lingyun lingyun 9509 6月 17 21:38 main.c
drwxr-xr-x 2 lingyun lingyun 4096 4月 20 09:53 res
drwxr-xr-x 2 lingyun lingyun 4096 4月 20 09:55 src
-rwxr-xr-x 1 lingyun lingyun 16696 8月 3 11:20 study
-rw-r--r-- 1 lingyun lingyun 4179 8月 3 11:19 study.cpp
drwxr-xr-x 2 lingyun lingyun 4096 7月 6 12:06 .vscode
execle 和 execve 代码
execle() 和 execve() 改变的是 exec 启动的程序的环境变量(只会改变进程的环境变量,不会影响系统的环境变量),其他四个函数启动的程序则使用默认系统环境变量。
/* study.cpp */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
/* getenv() 获取指定的环境变量的值 */
printf("进程替换之前:USER=%s, HOME=%s\n", getenv("USER"), getenv("HOME"));
char *envp[]={"USER=LINGYUN", "HOME=/tmp", NULL};
/* ./main是外部程序,有另外的代码编译得到
列表传参,NULL表示给 ./main 程序传参结束
env:改变 ./main 程序的环境变量,准确一点,是让 ./main 程序只保留 envp 的环境变量
*/
//execle("./main", "main", NULL, envp);
/* ./main是外部程序,有另外的代码编译得到
字符串数组传参,NULL表示给 ./main 程序传参结束
env:改变 ./main 程序的环境变量,准确一点,是让 ./main 程序只保留 envp 的环境变量
*/
char* argv[] = {"main",NULL};
execve("./main",argv,envp);
perror("execle");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("进程替换之后: USER=%s,HOME=%s\n",getenv("USER"),getenv("HOME"));
return 0;
}
执行可见,环境变量确实发生了改变。
[lingyun@manjaro study]$ gcc main.c -o main
[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study
进程替换之前:USER=lingyun, HOME=/home/lingyun
进程替换之后: USER=LINGYUN,HOME=/tmp
exec 并没有创建新的进程
/* study.cpp */
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("进程替换之前的进程 ID: %d, 父进程 ID: %d\n",getpid(),getppid());
sleep(5);
execl("./main", "main", NULL);
perror("execl");
return 0;
}
/* main.c */
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("进程替换之前的进程 ID: %d, 父进程 ID: %d\n",getpid(),getppid());
return 0;
}
执行可见,进程替换前后的进程 ID 和父进程 ID 都没有发生改变。
[lingyun@manjaro study]$ gcc main.c -o main
[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study
进程替换之前的进程 ID: 6499, 父进程 ID: 2411
进程替换之前的进程 ID: 6499, 父进程 ID: 2411