操作系统-实例详解fork()的工作原理(超详细版)

讲解这一主题的帖子很多,但是对于初学者来说依然很难理解。所以本文会先贴出完整的实例代码,然后逐句讲解。重点在于理解函数的工作逻辑。读者可以直接从代码段后开始阅读。

#include 
#include 
#include 
#include 

int main() {
    pid_t pid = fork();  // 创建一个子进程

    if (pid < 0) {
        // 如果 fork() 失败
        perror("fork failed");
        exit(1);
    } else if (pid == 0) {
        // 子进程逻辑
        printf("Child process (PID: %d) is running.\n", getpid());
        exit(42);  // 子进程以状态码 42 退出
    } else {
        // 父进程逻辑
        int status;
        pid_t child_pid = wait(&status);  // 等待子进程完成
        printf("Parent process: Child (PID: %d) terminated.\n", child_pid);

        if (WIFEXITED(status)) {
            // 检查子进程是否正常退出
            printf("Child exited with status: %d\n", WEXITSTATUS(status));
        } else {
            printf("Child did not exit normally.\n");
        }
    }
    return 0;
}

首先要理解代码,第一行:pid_t pid = fork(); 这里pid_t 是一个数据类型,在 POSIX 标准中定义,用于表示进程的 ID。际实现中通常是一个整数类型。pid_t pid 定义了一个变量 pid,用于存储 fork() 的返回值。pid 将用来标识当前进程或子进程。

fork() 是一个系统调用,用于创建一个新的子进程。调用 fork() 后,系统会复制当前进程,形成两个几乎完全相同的进程(父进程和子进程)。这两个进程从 fork() 返回点开始,分别继续执行,但它们的返回值不同。在 父进程 中,fork() 返回 子进程的 PID。在 子进程 中,fork() 返回 0。

重点来了:父进程就是你写的这个代码在执行时,操作系统分配给它的初始进程。当程序运行时,操作系统会启动一个进程来执行它的代码。从程序的起点main()开始执行。运行你的程序最初只有一个进程,从第一行代码开始执行。当程序运行到 fork() 之前,整个程序的所有逻辑都是父进程在执行。

fork() 系统调用会创建一个新的子进程,此时,操作系统中有两个进程在运行这段代码:父进程:原来的进程。子进程:新创建的进程,它从 fork() 调用返回的位置 开始执行代码,返回点是指代码中调用 fork() 的那一行,而不是重新从程序的起点main()运行。

讲的更具体一些就是,当 fork() 被调用时,操作系统会:复制父进程的内存空间(包括代码段、数据段、栈等)。复制父进程的寄存器状态,包括程序计数器(PC)、栈指针(SP)、通用寄存器的值等。因为此时PC 保存的是下一条指令地址,PC 的值指向 fork() 系统调用的下一条指令。子进程从父进程那里复制了 PC,因此它从 fork() 调用返回的位置开始执行。

此时父子进程从 fork() 的返回点开始执行,pid 被赋值为子进程的 PID,例如 12345。执行 if (pid > 0) 块。子进程复制了父进程的 PC,所以从 fork() 的返回点开始执行。操作系统在子进程中将 fork() 的返回值设置为 0,pid 被赋值为 0,执行 else if (pid == 0) 块。

理解了这些部分,wait()的作用就很容易看懂了。 wait()的功能是让父进程等待子进程的状态变化(如子进程退出),然后返回已退出的子进程的 PID。如果出错(如没有子进程),wait() 返回 -1。子进程在运行过程中,任何状态变化(退出、被信号终止、暂停等)都会向操作系统内核报告。内核会记录这些状态变化,并将子进程的状态保存在数据结构中(如进程控制块,PCB)。当父进程调用 wait() 时:内核检查是否有子进程的状态发生变化。如果有变化,wait() 将子进程的相关信息返回给父进程。

int status;
pid_t child_pid = wait(&status);

这两行意思就是获取退出子进程的 PID(存储在 child_pid 中)。同时将子进程的退出状态保存到变量 statuswait() 的参数是用来接收子进程的退出状态(如退出码或信号信息)。如果你不关心退出状态,可以传 NULL

大家可以试着去编译执行示例代码。我自己运行的结果是:

This is the child process. My PID: 1237
This is the parent process. Child PID: 1237
Child exited with status: 42

你可能感兴趣的:(linux,系统架构)