### 介绍下父子进程

**父子进程的创建**

父子进程的关系是通过一个特殊的系统调用——**`fork()`** 来创建的

1. 当一个进程（我们称之为**父进程**）调用 `fork()` 时，操作系统会复制父进程的几乎所有信息，包括代码、数据、堆、栈等，来创建一个新的进程，也就是**子进程**
2. `fork()` 系统调用有一个关键的特点：在父进程中，它会返回新创建的子进程的 **PID（进程ID）**；而在子进程中，它会返回 **0**。这使得程序可以根据返回值来判断自己是父进程还是子进程，并执行不同的代码路径

**父子进程的资源关系**

尽管子进程是父进程的副本，但在资源管理上，它们之间存在一些重要的区别：

- **独立的内存空间**：在创建时，子进程会获得一个与父进程几乎相同的独立内存副本。这意味着，父进程和子进程对各自内存的修改不会互相影响。这遵循了 **写时复制（Copy-on-Write, CoW）** 的机制，即最初它们共享相同的内存页，只有当任一进程试图修改该内存页时，才会真正创建一个新的副本
- **独立的 PID**：每个进程都有一个唯一的 PID，子进程会获得一个新的、独立的 PID
- **共享资源**：父子进程会共享一些只读资源，例如程序代码段。此外，它们还会共享打开的文件描述符。这意味着，如果在 `fork()` 前父进程打开了一个文件，那么子进程也会继承这个文件描述符，可以继续读写这个文件

**父子进程的生命周期**

- **正常终止**：如果父进程或子进程正常执行完毕，它们会退出。当子进程终止时，它会变成一个**僵尸进程（Zombie Process）**，直到它的父进程通过 `wait()` 或 `waitpid()` 系统调用来“收尸”，获取它的退出状态，然后这个僵尸进程才会被操作系统彻底清理
- **孤儿进程**：如果父进程在子进程之前终止，子进程就会成为一个**孤儿进程（Orphan Process）**。在这种情况下，操作系统的 **`init` 进程（PID 1）** 会收养这个孤儿进程，成为它的新父进程。`init` 进程会负责清理这些孤儿进程，防止它们变成永久的僵尸进程