📜  fork 在 c 中是如何工作的 (1)

📅  最后修改于: 2023-12-03 15:15:10.799000             🧑  作者: Mango

Fork 在 C 中是如何工作的

Fork 是 C 语言中一个重要的系统调用,它用于创建一个新的进程。新进程和原进程在执行过程中是完全独立的,拥有自己的内存空间与 CPU 时间分配。本篇文章将介绍 Fork 在 C 中的工作原理。

fork() 函数

Fork 函数的原型如下:

#include <unistd.h>

pid_t fork(void);

fork() 函数会调用一次,但会返回两次不同的返回值。对于新创建的子进程,fork() 返回 0;而对于原进程,fork() 返回新进程的进程 ID(PID)。

Fork 的工作原理

在调用 Fork 函数创建新进程时,操作系统会复制原进程的所有地址空间、代码、数据与内存将它们复制到新进程的地址空间中,同时给新进程分配一个新的 PID。此时,新进程和原进程所执行的代码和数据虽然完全独立,但是在代码和数据的物理存储空间却是共享的。以下是具体的流程:

  1. 首先,原进程调用 fork() 函数并创建一个新的进程。
  2. 然后,新进程被赋予了一个新的PID号,并拥有了一份和原进程相同的代码和数据的虚拟空间。
  3. 接着,在新进程中,堆和栈中的数据被进行了复制(即数据快照)并被设置成原进程的副本。但是,这些数据在物理存储空间中是不共享的,这个新的进程只是原始进程的一个准确的镜像。
  4. 最后,新的进程获取了一份原始进程文件描述符表的拷贝,这样它就可以操作文件。

由于 fork () 返回两次,一次返回新进程的 PID,一次返回 0,因此可以通过返回值来判断当前代码运行在哪个进程中。

示例代码

以下是一个简单的示例代码,演示了父子进程间的通信和 fork() 返回两次的原理:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main () {
    pid_t pid;
    int status;

    printf("I'm the parent process with PID = %d\n", getpid());
    pid = fork();

    if (pid == 0) {
        printf("I'm the child process with PID = %d\n", getpid());
        exit(0);
    }
    else if (pid > 0) {
        printf("I'm the parent process with PID = %d and my child is PID = %d\n", getpid(), pid);
        waitpid(pid, &status, 0);
        printf("My child process %d terminated with status %d\n", pid, WEXITSTATUS(status));
    }
    else {
        printf("Fork failed!\n");
        return 1;
    }

    printf("Will this line be printed twice by parent and child?\n");
    return 0;
}

该程序打印的输出如下:

I'm the parent process with PID = 1234
I'm the child process with PID = 1235
I'm the parent process with PID = 1234 and my child is PID = 1235
My child process 1235 terminated with status 0
Will this line be printed twice by parent and child?
I'm the parent process with PID = 1234
Will this line be printed twice by parent and child?

从上述输出可以看出,程序首先打印 "I'm the parent process with PID = 1234",随后通过 fork() 又创建了一个新的进程,并在其内部打印了 "I'm the child process with PID = 1235"。父进程根据 fork() 返回值区分子进程和父进程,并在两种情况下输出不同的信息。其后,父进程等待了子进程的结束,并打印了相应的进程状态。最后,原始父进程打印了一条语句,并返回到操作系统。

结论

Fork() 是 Linux 操作系统中最常用的系统调用之一。它基于『复制』技术,在创建子进程的同时,将原始进程的数据和代码复制,创建一个几乎完全相同的新进程。新进程和原进程是完全独立的,但又共享一部分物理内存。因此,子进程可以操作父进程内的数据和代码,反之亦然。在程序执行期间,可以使用 getpid () 函数来获取当前进程的 PID,判断代码运行的是哪个进程。