文章目录
-
- [一、`fork()` 函数](#一、
fork()
函数) - [二、`exec` 系列函数](#二、
exec
系列函数) - [三、`fork()` 与 `exec` 的关系](#三、
fork()
与exec
的关系)
- [一、`fork()` 函数](#一、
在 Linux 和其他 Unix-like 操作系统中,exec
系列函数和 fork
函数是进程管理中的两个重要组成部分。它们在创建和执行进程时扮演着关键角色,理解它们的工作原理及相互关系对于系统编程至关重要。
一、fork()
函数
fork()
是用于创建新进程的系统调用。调用 fork()
后,当前进程(父进程)会被复制一份,生成一个新的进程(子进程)。以下是 fork()
的一些重要特性:
- 进程复制:子进程是父进程的一个副本,拥有相同的内存空间、打开的文件描述符等。
- 返回值 :
fork()
在父进程中返回新创建的子进程的 PID(进程 ID),而在子进程中返回 0。在出错时返回 -1。 - 并行执行:父进程和子进程会并行执行,操作系统调度它们的执行。
示例代码:
c
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("This is the child process with PID: %d\n", getpid());
} else {
// 父进程
printf("This is the parent process with PID: %d, created child with PID: %d\n", getpid(), pid);
}
return 0;
}
二、exec
系列函数
exec
系列函数用于在当前进程的上下文中执行新的程序。这意味着调用 exec
后,当前进程的代码和数据将被新程序替换。exec
函数有多种变体,包括:
execl()
:使用参数列表传递命令行参数。execv()
:使用参数数组传递命令行参数。execle()
:使用环境变量传递命令行参数。execve()
:接收命令、参数和环境变量的数组,是最底层的实现。execvp()
:使用文件名搜索路径,传递参数数组。
特性:
- 替换进程 :执行
exec
后,当前进程的映像被替换为新程序,进程 ID 保持不变。 - 返回值 :如果
exec
调用成功,它不会返回;如果失败,它返回 -1,并设置 errno。 - 环境变量:可以传递环境变量供新程序使用。
示例代码:
c
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
char *args[] = {"/bin/ls", "-l", NULL}; // 参数列表
execvp(args[0], args); // 执行 ls 命令
perror("exec failed"); // 如果 exec 失败,输出错误
} else {
// 父进程
wait(NULL); // 等待子进程结束
printf("Child process completed.\n");
}
return 0;
}
三、fork()
与 exec
的关系
在大多数情况下,fork()
和 exec
一起使用,以便创建新的进程并在其中执行新程序。通常的操作步骤如下:
- **调用 **
fork()
:创建一个新的子进程。 - **在子进程中调用 **
exec
:用exec
系列函数替换子进程的映像为新程序。 - 父进程继续执行:父进程可以继续执行,通常会等待子进程完成。
工作流程:
- 创建子进程 :
fork()
生成一个新进程,该进程是当前进程的副本。 - 执行新程序 :子进程调用
exec
系列函数,将自己的执行映像替换为新程序。 - 并行处理:父进程可以并行处理其他任务。
示例代码(结合使用):
c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
char *args[] = {"./my_program", NULL}; // 假设执行的程序为 my_program
execvp(args[0], args);
perror("exec failed");
} else {
// 父进程
wait(NULL); // 等待子进程结束
printf("Child process completed.\n");
}
return 0;
}