在类 Unix 系统中,exec 函数族用于在当前进程的上下文中执行一个新的程序。调用 exec 函数会用新程序替换当前进程的代码段、数据段、堆和栈等,从而开始执行新程序。以下是 exec 函数族的常见函数及其特点:

在进入exec0后子进程原来的代码和数据区就会被清掉(释放)。待该子进程开始运行新程序时,由于此时内核还没有从块设备上加载该程序的代码,CPU 就会立刻产生代码页面不存在的异常(Fault),此时内存管理程序就会从块设备上加载相应的代码页面,然后 CPU 又重新执行引起异常的指令。到此时新程序的代码才真正开始被执行。

函数列表

1. execl

int execl(const char *path, const char *arg, ...);
  • 参数说明
    • path:要执行的程序的路径名。
    • arg:传递给新程序的第一个参数,通常是程序名本身。后续可以跟可变数量的参数,以 NULL 结尾。
  • 示例
#include <unistd.h>

int main() {
    execl("/bin/ls", "ls", "-l", NULL);
    return 0;
}

2. execv

int execv(const char *path, char *const argv[]);
  • 参数说明
    • path:要执行的程序的路径名。
    • argv:一个以 NULL 结尾的字符串数组,包含传递给新程序的参数。
  • 示例
#include <unistd.h>

int main() {
    char *args[] = {"ls", "-l", NULL};
    execv("/bin/ls", args);
    return 0;
}

3. execle

int execle(const char *path, const char *arg, ..., char * const envp[]);
  • 参数说明
    • path:要执行的程序的路径名。
    • arg:传递给新程序的第一个参数,通常是程序名本身。后续可以跟可变数量的参数,以 NULL 结尾。
    • envp:一个以 NULL 结尾的字符串数组,包含传递给新程序的环境变量。
  • 示例
#include <unistd.h>

int main() {
    char *env[] = {"MY_VAR=value", NULL};
    execle("/bin/ls", "ls", "-l", NULL, env);
    return 0;
}

4. execve

int execve(const char *path, char *const argv[], char *const envp[]);
  • 参数说明
    • path:要执行的程序的路径名。
    • argv:一个以 NULL 结尾的字符串数组,包含传递给新程序的参数。
    • envp:一个以 NULL 结尾的字符串数组,包含传递给新程序的环境变量。
  • 示例
#include <unistd.h>

int main() {
    char *args[] = {"ls", "-l", NULL};
    char *env[] = {"MY_VAR=value", NULL};
    execve("/bin/ls", args, env);
    return 0;
}

5. execlp

int execlp(const char *file, const char *arg, ...);
  • 参数说明
    • file:要执行的程序的文件名。系统会在 PATH 环境变量指定的目录中搜索该文件。
    • arg:传递给新程序的第一个参数,通常是程序名本身。后续可以跟可变数量的参数,以 NULL 结尾。
  • 示例
#include <unistd.h>

int main() {
    execlp("ls", "ls", "-l", NULL);
    return 0;
}

6. execvp

int execvp(const char *file, char *const argv[]);
  • 参数说明
    • file:要执行的程序的文件名。系统会在 PATH 环境变量指定的目录中搜索该文件。
    • argv:一个以 NULL 结尾的字符串数组,包含传递给新程序的参数。
  • 示例
#include <unistd.h>

int main() {
    char *args[] = {"ls", "-l", NULL};
    execvp("ls", args);
    return 0;
}

函数命名规律

  • 函数名中包含 l 表示参数以列表形式传递(list)。
  • 函数名中包含 v 表示参数以数组形式传递(vector)。
  • 函数名中包含 e 表示可以指定环境变量(environment)。
  • 函数名中包含 p 表示会在 PATH 环境变量指定的目录中搜索程序(path)。

返回值

如果 exec 函数调用成功,不会返回,因为当前进程的代码段已经被新程序替换。如果调用失败,返回 -1,并设置 errno 来指示错误原因。

system 函数

system 函数是 C 标准库中一个常用的函数,它的主要功能是在程序中调用系统命令。下面从函数原型、头文件、功能、返回值、使用示例以及注意事项几个方面详细介绍 system 函数。

函数原型与头文件
  • 头文件:要使用 system 函数,需要包含 <stdlib.h> 头文件。
  • 函数原型
int system(const char *command);

这里的 command 是一个指向字符串的指针,该字符串就是要执行的系统命令。

功能

system 函数会调用 /bin/sh -c 来执行传入的 command 参数所指定的命令。也就是说,它会启动一个新的 shell 进程,然后在这个 shell 中执行指定的命令。

返回值

system 函数的返回值有以下几种情况:

  • 如果 system 函数调用失败:例如无法启动新的 shell 进程,返回 -1,并且会设置全局变量 errno 来指示具体的错误原因。
  • 如果 shell 不能正常执行:也就是 /bin/sh 执行失败,返回值就如同 shell 以状态 127 退出一样。
  • 如果 shell 正常执行system 函数会返回命令的退出状态码。在 Unix/Linux 系统中,一般程序正常退出时返回 0,出现错误时返回非零值。
使用示例

下面是一个简单的 C 语言程序示例,展示了如何使用 system 函数执行 ls -l 命令来列出当前目录下的文件和文件夹详细信息:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int status;
    // 调用 system 函数执行 ls -l 命令
    status = system("ls -l");
    if (status == -1) {
        perror("system");
    } else {
        printf("命令执行完毕,退出状态码: %d\n", status);
    }
    return 0;
}
代码解释
  1. 包含头文件:包含 <stdio.h> 用于输入输出操作,包含 <stdlib.h> 以使用 system 函数。
  2. 定义变量:定义 status 变量来存储 system 函数的返回值。
  3. 调用 system 函数:执行 ls -l 命令,并将返回值赋给 status
  4. 检查返回值:如果返回值为 -1,说明调用失败,使用 perror 输出错误信息;否则,输出命令的退出状态码。
注意事项
  • 安全性问题:当 command 参数包含用户输入时,可能会存在安全风险,比如遭受命令注入攻击。攻击者可能通过构造特殊的输入来执行恶意命令。所以在使用 system 函数处理用户输入时,一定要进行严格的输入验证和过滤。
  • 性能开销system 函数会启动一个新的 shell 进程,这会带来一定的性能开销。如果只是简单的系统调用,使用更底层的函数(如 exec 系列函数)可能会更高效。 ****