Linux进程是如何创建出来的?

💡 原文中文,约13300字,阅读约需32分钟。
📝

内容提要

本文通过以Nginx创建worker进程为例,深入分析进程的创建过程,介绍了进程的数据结构task_struct和fork过程的执行。学习本文可更深入了解进程的关键要素,如进程地址空间、当前目录、父子进程关系、进程打开的文件fd表、进程命名空间等。同时,还了解了内核在保存已使用的pid号时如何优化内存占用。

🔎

延伸解读

fork 的写时复制与资源隔离

文章指出,fork 创建进程时,copy_process 会复制 task_struct,但内部指针如 mm_struct 初始时仍指向父进程。随后通过 copy_mm、copy_files、copy_fs 等函数,在未传递 CLONE_VM、CLONE_FILES、CLONE_FS 标志时,为子进程申请独立资源。这体现了进程隔离的核心:地址空间、文件描述符表和文件系统信息默认独立,而命名空间在未指定标志时则共享。

pid 管理的 bitmap 优化

内核使用 bitmap 管理 pid 号,每个 bit 表示一个 pid 是否被占用。相比用整数数组记录,bitmap 极大节约内存,且数据紧凑,遍历时缓存命中率高,访问速度快。这种思想在超大规模数据处理中常用于优化内存占用。文章通过 alloc_pidmap 的代码展示了如何遍历 bitmap 找到空闲 pid。

内核线程与用户进程的地址空间差异

mm_struct 是用户进程虚拟地址空间的核心数据结构,包含代码段、数据段、堆栈等区域的起止地址。内核线程没有用户态虚拟地址空间,因此其 mm 成员为 null。所有进程的内核态内存是共享的,且内核内存区域可直接计算物理地址,无需复杂页表。这解释了进程与内核线程在内存管理上的根本区别。

❓

Q&A

Nginx是如何创建worker进程的?

Nginx通过调用fork系统调用来创建worker进程,具体实现是在ngx_spawn_process函数中。

fork系统调用的核心逻辑是什么?

fork系统调用的核心逻辑在于copy_process函数,它负责生成新的task_struct并复制父进程的相关信息。

task_struct数据结构包含哪些关键信息?

task_struct包含进程状态、pid、父子进程关系、调度优先级、地址空间、文件系统信息和打开的文件信息等。

进程的地址空间是如何管理的?

进程的地址空间由mm_struct表示,负责管理进程的虚拟地址空间和内存查找。

Linux如何优化已使用的pid号的内存占用?

Linux使用bitmap来管理已使用的pid号,通过位图的方式节约内存开销。

Nginx的worker进程在创建后会执行什么?

创建成功后,worker进程会进入自己的入口函数开始处理用户请求。

🏷️

标签

➡️

继续阅读