Linux进程编程(8)exce族函数的使用
前言
exec
系列函数用于在当前进程上下文中加载并执行一个新的程序。换句话说,exec
会替换当前进程的代码、数据段和堆栈等信息为新程序的相应部分,然后执行新程序。这个过程中,进程的 PID 不会改变,但是几乎所有其他的内容都会被新程序替换。
一、exce族函数是什么?
exec
系列函数有多个变体,如 execl()
, execp()
, execle()
, execv()
, execvp()
和 execve()
等。这些变体主要是在传递命令行参数和环境变量方面有所不同。
常见的 exec
函数:
-
execl(const char *path, const char *arg0, ..., const char *argN, NULL)
使用逗号分隔的参数列表,并且以NULL
终止。 -
execv(const char *path, char *const argv[])
使用字符串数组来传递参数。 -
execle(const char *path, const char *arg0, ..., const char *argN, NULL, char *const envp[])
和execl
类似,但允许传递一个环境变量数组。 -
execve(const char *path, char *const argv[], char *const envp[])
允许传递一个参数数组和一个环境变量数组。 -
execp
和execvp
这两个函数会在系统PATH
环境变量所指定的目录中查找可执行文件。
二、实现代码
示例:
1.下面的代码使用 execl()
在子进程中执行 ls -l
命令:
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork(); // 创建子进程
// fork 失败
if (pid < 0) {
perror("fork failed");
return 1;
}
// 子进程
if (pid == 0) {
// 使用 execl 执行 ls -l 命令
// 注意:这里的第二个和第三个参数是命令的名字和参数,最后一个参数必须是 NULL
execl("/bin/ls", "ls", "-l", NULL);
// 如果 execl 调用失败,则会执行以下 perror
perror("execl failed");
return 1;
}
// 父进程
// 父进程可以继续执行其他任务,或等待子进程完成
return 0;
}
2.使用 execvp() 执行 ls -l
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork(); // 创建子进程
// fork 失败
if (pid < 0) {
perror("fork failed");
return 1;
}
// 子进程
if (pid == 0) {
// 参数列表,第一个是命令,最后一个必须是 NULL
char *args[] = {"ls", "-l", NULL};
// 使用 execvp 执行 ls -l 命令
// 这里使用的是 PATH 环境变量中的 ls 命令
execvp("ls", args);
// 如果 execvp 调用失败,则会执行以下 perror
perror("execvp failed");
return 1;
}
// 父进程
// 父进程可以继续执行其他任务,或等待子进程完成
return 0;
}
3.使用 execle() 执行 env 命令
execle() 允许你传递一个新的环境变量数组给新程序。下面的代码在子进程中执行 env 命令,这个命令会打印出环境变量。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid < 0) {
perror("fork failed");
return 1;
}
if (pid == 0) {
// 新的环境变量列表
char *newenv[] = {"MY_VAR=hello", NULL};
// 使用 execle 执行 env 命令
execle("/usr/bin/env", "env", NULL, newenv);
// 如果 execle 调用失败,则会执行以下 perror
perror("execle failed");
return 1;
}
// 父进程可以继续执行其他任务,或等待子进程完成
return 0;
}
4.使用 execv() 执行 ls -l
这个例子与使用 execvp() 类似,但它不会搜索 PATH 环境变量来查找 ls 命令。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid < 0) {
perror("fork failed");
return 1;
}
if (pid == 0) {
// 参数列表,第一个是命令,最后一个必须是 NULL
char *args[] = {"ls", "-l", NULL};
// 使用 execv 执行 ls -l 命令
execv("/bin/ls", args);
// 如果 execv 调用失败,则会执行以下 perror
perror("execv failed");
return 1;
}
// 父进程可以继续执行其他任务,或等待子进程完成
return 0;
}
总结
这些示例覆盖了 exec 系列函数中的主要变体,每种变体都有其特定用途和优势。例如,execl 和 execle 允许你直接在函数调用中指定参数,而 execv 和 execvp 允许你通过数组传递参数,这在参数数量未知时非常有用。而 execle 和 execve 允许你指定新程序的环境变量。希望这些示例和注释能够帮助你理解这些函数的不同用途。