扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard-进程的基本操作之创建执行进程exec系列

9小时前
152
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

那么使用fork()函数启动一个复制的子进程有什么意义呢,子进程跟父进程都是一样的,子进程能做的父进程也一样能,这不是多此一举嘛。所以,通常父进程在创建子进程后,子进程会调用exec()系列函数,这样子进程可以用新的程序替换自身的内存映像;这允许在同一进程中运行不同的程序,使得创建和管理进程变得灵活。例如,一个Shell在接收到用户命令后,通常会fork()出一个子进程,并在其中使用exec()来执行用户输入的命令。

exec 系列函数是一组用于在当前进程中执行新程序的系统调用。使用这些函数时,当前进程的内存空间会被新程序的内容替换。调用exec系列函数并不会创建进程,因此前后进程的PID并无改变。

1.头文件

#include <unistd.h>

2.常见的 exec 系列函数原型

int execl(const char *pathname, const char *arg, .../* (char  *) NULL */);

int execlp(const char *file, const char *arg, .../* (char  *) NULL */);

int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);

int execv(const char *pathname, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[], char *const envp[]);

3.相关参数大致说明

pathname:要执行的程序的路径。

arg:程序的第一个参数(通常是程序名)。

...:可变参数列表,表示后续的命令行参数,最后以 NULL 结束。

file:要执行的程序名,可以在 PATH 环境变量中查找。

envp:环境变量数组,最后一个参数为 NULL。

argv:参数数组,最后一个元素为 NULL。

4.返回值

exec 系列函数只有在出错的时候才会返回,如果成功,该函数无返回,否则返回-1。可以通过它们的后缀来区分他们的作用;

l 后缀:表示参数是一个可变长参数列表(list)。如:execl、execlp、execle;参数以 ... 形式传递,每个参数单独列出,最后以 NULL 结束。

v 后缀:表示参数是一个数组(vector)。如:execv、execvp、execvpe;参数以 argv[] 数组的形式传递,数组最后一项为 NULL。

p 后缀:表示程序名可以根据 PATH 环境变量查找(path)。如:execlp、execvp、execvpe;如果程序名不是绝对路径,系统会在 PATH 环境变量中查找可执行文件。

e 后缀:表示可以传递自定义的环境变量(environment)。如:execle、execvpe;允许传递自定义的环境变量,而不是使用当前进程的默认环境变量。

5.示例:使用fork()创建子进程调用exec()执行shell命令

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

int main() {

pid_t pid = fork();  // 创建子进程

if (pid < 0) {

// fork 失败

perror("fork failed");

exit(EXIT_FAILURE);

}

if (pid == 0) {

// 子进程

printf("Child process (PID: %d)\n", getpid());

// 在子进程中使用 exec 执行一个新程序

// 例如执行 /bin/ls 来列出当前目录的内容

char *argv[] = { "ls", "-l", NULL };  // 命令和参数

execvp("ls", argv);  // 执行 ls 命令

// 如果 exec 成功,下面的代码不会执行

perror("exec failed");  // exec 失败会返回

exit(EXIT_FAILURE);

} else {

// 父进程

printf("Parent process (PID: %d), waiting for child (PID: %d)\n", getpid(), pid);

// 父进程等待子进程结束

wait(NULL);

}

return 0;

}

6.运行结果

ls -l命令执行成功会列出当前目录下的文件和文件夹,显示详细的文件信息。

7.代码解析

父进程调用fork()创建子进程,然后两个进程并行执行;子进程调用execvp(),如果调用成功,当前进程的内存空间会被新程序的内容替换,后续的退出和打印错误就不执行;父进程等待子进程结束然后退出。

8.fork和exec共同使用的优势

⚫起到进程隔离和管理的作用:fork() 创建新进程,通过 fork(),可以生成一个子进程,这个子进程和父进程可以独立执行,从而实现任务的并发执行;父进程可以继续自己的任务,而子进程可以执行新的任务。exec() 替换子进程的内容,通过在子进程中调用 exec(),子进程可以执行一个完全不同的程序,而父进程继续保持不变。父进程可以监视子进程,等待它完成任务,或在子进程失败时执行某些恢复操作。

⚫可以执行不同的程序:fork() 和 exec() 的组合允许在子进程中执行一个完全不同的程序。例如,父进程可能是一个守护进程或主进程,负责处理调度任务,子进程则可以通过 exec() 来启动不同的程序(如系统命令、脚本、服务器进程等)。

⚫让父进程保持对子进程的控制权:在使用 fork() 时,父进程可以保持控制权,例如通过 wait() 等待子进程结束、获取子进程的退出状态,或者监控子进程的行为。这样父进程可以管理多个子进程,同时每个子进程可以独立执行不同的任务。

⚫避免阻塞主进程:如果直接在主进程中使用 exec(),主进程将被替换成新的程序,原本主进程的工作将无法继续。而通过 fork() 创建一个子进程,父进程依旧可以执行原任务,同时子进程执行新的程序,从而避免阻塞。

⚫有更多的环境和参数控制:在 exec() 系列函数中,可以精细控制传递给新程序的命令行参数和环境变量,例如 execle() 和 execvpe() 允许显式传递环境变量,而 system() 则只能使用当前进程的环境。

相关推荐