从来没有一个技术领域像 Linux 内核这样,既让人趋之若鹜,又让绝大多数人铩羽而归。
我见过太多人兴冲冲地 clone 一份内核源码,从 start_kernel 开始逐行读,读了两天之后脑子里只剩一堆函数名,最后放弃。也见过不少准备 Linux 面试题的同学,把“进程和线程的区别”“用户态和内核态的区别”背得滚瓜烂熟,可一被追问“fork 之后父子进程的虚拟内存到底怎么隔离的”,立刻卡壳。
问题出在哪?缺一个东西——心智模型。说白了,就是你脑子里有没有一张关于“内核这台机器到底怎么转”的地图。有了这张地图,再去看代码,每一行都有位置感;没有这张地图,你只是在背单词,永远学不会一门语言。
这篇文章是 Linux 内核专栏的第一篇,我不会上来就讲某个子系统怎么实现,而是先把最底层的认知框架搭起来:Linux 内核的整体架构是什么样,设计哲学有哪几条,六个核心子系统各自的心智模型是什么。适合三类人看:准备 Linux 面试题但感觉知识散成一团的人;做嵌入式或运维、工作中要碰内核问题和内核裁剪的人;以及零基础想系统进入 Linux 内核学习,但不知道从哪下手的自学者。
好,开始。
1. 为什么学内核先要建立心智模型,而不是急着读源码
1.1 三种最容易踩的入门姿势,我全试过
第一种:从头读源码。很多教程告诉你“内核学习没有捷径,读源码就完了”,结果你从 start_kernel 开始,跟着初始化流程走,走到调度器初始化的时候还能撑住,走到 RCU、走到 per-cpu 变量,人已经懵了。原因很简单:源码是“结果”,不是“过程”。它呈现的是最终状态,而不会告诉你每一步为什么存在。
第二种:刷面试题背答案。面试题是别人消化之后的产出,你背下来的只是只言片语。比如面试题里常考的“进程调度算法有哪些”,你背下了 CFS、O(1)、实时调度,但不知道这些算法解决的核心矛盾是什么,不知道怎么权衡吞吐和延迟,一遇到“那 CFS 为什么用红黑树而不是链表”就又不会了。
第三种:只看概念不看流程。知道什么是进程、什么是虚拟内存、什么是文件系统,但把三者之间的调用链串不起来。真到定位内核问题的时候,丢给你一条 dmesg 报错和一份 /proc 下的数据,你根本不知道该从哪儿下手。
这三种姿势的共同问题,是缺少一个“骨架”。心智模型就是那个骨架。
1.2 心智模型到底有什么用:面试、排障、裁剪全靠它
我常说一句话:学内核有点像学开飞机。你不需要先会拆发动机,但你得先知道仪表盘上每个指针代表什么,知道起飞、巡航、降落三个阶段各是什么状态。遇到发动机报警,你才能判断是油路问题还是电路问题,而不是拆开所有管路一根根查。
具体到内核,心智模型的价值体现在三个场景。
第一个场景是面试。真正高质量的内核面试题不是考记忆,而是考推断。比如考官问“用户态和内核态切换时,栈是怎么切换的”,如果你脑子里有“CPU 切换到内核态后用内核栈保存用户态现场,返回时再从内核栈恢复”这条完整链路,这个问题就是送分题。反之,只背结论的人当场就露馅。
第二个场景是排障。做运维或驱动开发的人都知道,内核问题最怕“两眼一抹黑”。panic 日志出来,到底是内存越界、调度死锁、还是驱动非法访问,先要有个大方向。你的心智模型越清晰,定位范围就缩得越快。我在后面专门有一节讲这套“按图索骥”的排障方法。
第三个场景是裁剪。嵌入式项目经常要裁内核,把不需要的驱动、文件系统、子系统都去掉,换来更小的镜像、更快的启动。但什么能裁、什么千万不能动,这不是靠背“内核裁剪八股”能解决的,得真懂每个子系统之间的依赖关系。比如你可以去掉某个不用的驱动,但你不能把 RCU 关掉,因为整个内核都在用它做同步。
所以,这篇专栏第一篇不讲代码,先把骨架立起来。
2. 画出内核全景图:先搞懂六个子系统怎么分工
2.1 宏内核加模块:Linux 用一条中庸之道搞定性能与扩展性
内核架构设计上有一个经典争论:到底是把所有核心服务都塞进一个大内核里(宏内核),还是把核心做到最小、把服务全部放进用户态进程(微内核)。
Linux 选了宏内核,而且靠“内核模块”机制给宏内核补上了扩展性这个短板。宏内核的好处是性能好、内部通信开销小,进程调度、内存管理、文件系统、网络协议栈都在同一个地址空间里运行,互相调用就是一次普通的函数调用,不需要走进程间通信,也不会有频繁的模式切换。代价是一旦某个子系统出 bug,比如一个驱动越界写了内存,整个内核都可能崩。
模块机制把这个代价降下来了一点。驱动、文件系统、网络协议这些组件可以编译成 .ko 文件,运行时用 insmod/modprobe 动态加载,用 rmmod 卸载。注意这里有个前提:代码必须写得“可卸载”,比如正确地注册、正确地释放资源、保证引用计数归零,否则 rmmod 的时候很容易把自己搞崩。这也是很多内核模块开发新手踩坑的地方——加载没事,一卸载就 panic。
用一句话总结:Linux 用宏内核换性能,用模块制衡宏内核的脆弱性,用社区协作守住代码质量。这个架构选择是 90 年代到现在经过大量实战验证的中庸之道。
2.2 六个核心子系统:每个人各管一摊,但业务深度耦合
可以把内核想象成一家公司,六个核心子系统就是六个部门。它们各管一摊,但业务上深度耦合。我用一张表把它们列清楚:
| 子系统 | 核心职责 | 关键数据结构/机制 | 你会在哪些场景遇到它 |
|---|---|---|---|
| 进程管理 | 管理任务的创建、调度、销毁 | task_struct、调度器(CFS/RT)、fork/exec | 程序起慢、CPU 飙高、top/ps 异常、面试高频 |
| 内存管理 | 管理物理内存和虚拟地址空间 | mm_struct、页表、slab 分配器、伙伴系统 | OOM、内存泄漏、页错误异常、mmap 性能 |
| 文件系统 | 统一管理各种存储上的数据组织 | VFS、inode、dentry、super_block、page cache | 磁盘满了、IO 慢、文件丢失、挂载失败 |
| 网络协议栈 | 实现 TCP/IP 及各种网络协议 | socket、sk_buff、协议分层、netfilter | 网络延迟、丢包、防火墙、连接数异常 |
| 设备驱动 | 驱动各类硬件设备 | 设备模型、file_operations、中断处理 | 驱动加载失败、设备不识别、中断风暴 |
| 系统调用接口 | 内核态与用户态的边界 | syscall 表、pt_regs、copy_from/to_user | 应用卡在 syscall、strace 分析、性能剖析 |
这张表建议你保存下来。以后不管是面试还是排障,先问自己一句:我遇到的问题属于哪个部门?这比一头扎进代码里高效得多。
你还需要注意一件事:这六个部门不是独立的。举个例子,你执行 cat /proc/cpuinfo,这条命令先触发一个系统调用(系统调用接口),然后由进程管理子系统找到当前进程上下文,接着文件系统子系统把它映射到 procfs 这种虚拟文件系统上,最终由 procfs 从内核维护的 CPU 信息里读取数据。一条命令,至少经过三个子系统。所以心智模型讲的不是六个孤岛,而是六张互相连接的网。
2.3 用户态与内核态:边界到底是怎么划出来的
在进入各子系统细节之前,有一个总纲必须先讲清楚,就是用户态与内核态的划分。这是 Linux 内核最重要的心智模型之一。
按 CPU 特权级别,通常区分 Ring0(内核态)和 Ring3(用户态)。用户态程序不能直接访问硬件、不能直接操作内核数据结构,所有关键操作都必须通过系统调用进入内核态完成。这样的隔离不是闲着没事干,它保证了一个普通程序再怎么作妖,也最多把自己搞挂,不至于把整个系统拖下水。
系统调用的完整路径可以这样记:应用层调用 glibc 封装函数(比如 read)→ glibc 把参数放入寄存器并执行 syscall 指令 → CPU 切换到内核态,跳到内核预先设置好的入口 → 内核根据系统调用号查 syscall 表,找到对应的内核函数 → 执行 → 把结果返回用户态。中间涉及栈切换和 CPU 上下文保存,现场保存在当前进程的内核栈的 pt_regs 结构里。
理解这条路径以后,“用户态和内核态切换”就不只是一个面试答案,它会变成你分析性能问题的工具。比如 strace 显示某个程序频繁陷入内核态,你就知道它大概率在做大量系统调用,可以从批处理、缓存、改用 io_uring 这些方向去优化,而不是瞎调参数。
还有一类进入内核态的入口是中断和异常,它们是异步的,用户态程序感知不到。中断上下文不是进程上下文,这也是后面讲内核运行节奏的关键。
3. 设计哲学:看懂这几条,源码里的很多怪癖就解释通了
3.1 一切皆文件:这个抽象为什么能统治 Unix 世界五十年
Linux 继承自 Unix 的核心哲学的第一条,就是“一切皆文件”。普通文件是文件,目录是文件,设备是文件,管道是文件,socket 是文件,甚至进程信息(/proc)和内核参数(/sys)也被模拟成文件。
这套抽象的精髓在于统一操作接口。任何东西,你都可以用 open/read/write/close/ioctl 这一套系统调用来操作。操作一个设备驱动,和你读写一个磁盘文件,在用户态看起来几乎没区别。设备驱动暴露给内核的就是一组 file_operations 回调函数,正好对应文件操作。
你可能会问,为什么不干脆把设备接口做得更“专用”一点,而是非要套文件的壳?因为泛化接口有巨大的好处:它让应用层的代码无需感知底层硬件差异。写应用的人只需要面对文件描述符,不用管底层是 SSD、网卡还是串口。这就是“抽象层”的威力——上层不用关心下层的多样性。
但我必须提醒你,“一切皆文件”并不是说文件系统、设备、网络这三者在实现上是同一套代码。它们只是在“接口形态”上统一了,底层数据结构和处理逻辑完全不同。很多人在这里想当然,以为懂了 VFS 就懂了网卡驱动,结果排障时到处碰壁。接口统一是好事,但别把“长得像”当成“本质一样”。
3.2 机制与策略分离:内核只提供“怎么做”,不替你决定“做什么”
Unix 设计哲学里有一条很容易被忽略但极其重要的原则:把机制(mechanism)和策略(policy)分开。
机制指的是内核提供的通用能力,策略指的是在具体场景下如何选择和配置。Linux 的调度器是典型例子。内核只提供调度机制——一组可选的调度器类、一个可插拔的调度框架;而具体采用什么调度策略,是 CFS 还是实时调度、nice 值调多少、cgroup 怎么分权重,这些都尽量放到用户态配置,或者以模块形式实现。
这样做的好处是灵活。同一个内核,既可以在服务器上按吞吐优先配置,也可以在移动设备上按延迟优先配置,还能在实时系统上切到 PREEMPT_RT 补丁。内核不需要为了每一种策略改代码,策略层通过配置、模块、用户态工具去实现。
对你学习内核也有一个启发:读源码的时候,先分清哪部分是机制、哪部分是策略。机制往往是稳定不变的主干,策略是频繁调整的分支。先抓住机制,就抓住了内核的主心骨;策略细节可以需要时再深入。这个“抓主干”的学习方法,后面每个子系统都会反复用到。
3.3 简单之美与组合思维:内核代码为什么普遍短小精悍
你如果翻开内核源码,会发现一个有趣的现象:很多核心函数写得非常短小,逻辑清晰。内核社区的编码约定甚至要求函数尽量短、每个函数只做一件事。这不是矫情,而是因为内核是全世界并发度最高、被审查最严格的代码库之一,任何花哨的写法都会成为 bug 的温床。
侵入式链表(list_head)是一个教科书级的例子。它在内核里无处不在,实现却只有几十行,把链表节点直接嵌入到宿主结构体里,通过 container_of 宏从节点反推宿主结构。这种做法避免了单独分配链表节点内存的开销,也保证了缓存亲和性。你看,复杂问题用简单的数据结构就能解决,关键是找到正确的抽象。
“组合优于继承”这条面向对象设计原则,在内核里也体现得淋漓尽致。内核用 C 实现,没有 class,但它用结构体里的函数指针模拟了多态——file_operations、inode_operations、vm_operations_struct 都是这样。一个结构体包含若干函数指针,不同的实现往里填不同的函数,上层调用时不需要关心具体是什么实现。这就是“面向接口编程”,在 C 里靠的是约定和纪律。
总结一下这几条哲学之间的关系:一切皆文件是“统一接口”,机制与策略分离是“内核克制”,简单与组合是“实现风格”。三者合起来,就是为了让一个巨型系统保持可维护性。你带着这套哲学去看源码,很多“为什么这么写”的疑问都能自己找到答案。
4. 关键心智模型:从抽象到具象,四个必懂的底层视角
4.1 进程不是程序,是“执行流”加“资源容器”
很多人把进程理解成“正在运行的程序”,这个说法没错,但从内核视角看,更准确地说,进程是一个“执行流”,是内核调度器分配 CPU 时间的基本单位,同时它也是一个“资源容器”,装着地址空间、文件表、信号处理等一堆状态。
内核为每个进程维护一个 task_struct,里面装满了调度信息、内存映射、打开的文件、信号处理、内核栈等全部状态。进程是资源容器,线程是进程内部更轻量的执行单元,同一进程的多个线程共享大部分资源,但各自有独立的栈和寄存器现场。理解“进程 = 资源容器 + 执行流”这个组合,面试里那些“进程和线程的区别”就能从资源与调度的角度讲清楚,而不是背几条对比列表。
调度器的视角也值得单独建立。CPU 在任何时刻只能跑一个执行流,调度器的工作就是决定下一个该跑谁。CFS 的核心理念是“让每个任务获得公平的 CPU 时间份额”,它用虚拟运行时间来排序,用红黑树维护一个按 vruntime 有序的任务队列。你不需要把红黑树的旋转逻辑背下来,但你要理解:它解决的问题是如何在大量任务之间高效地维持公平排队。
另外,fork、exec、clone 这三兄弟的语义差异很重要。fork 复制整个进程(其实利用写时复制偷了懒),exec 是替换进程当前执行的程序镜像,clone 则是更精细地控制共享哪些资源,是线程实现的底层机制。把它们串成一条线:“先 fork 出一个新进程,再 exec 加载新程序,也可以 clone 出线程”——这个认知比单独背每个系统调用的行为要牢固得多。
4.2 虚拟内存是“沙盘”,页表是“翻译官”
第二个必须建立的核心心智模型是虚拟内存。每个用户态进程都以为自己独占一个巨大的地址空间(64 位下理论上 128T),但物理内存是所有人共享的。中间的桥梁就是页表。
页表把虚拟地址翻译成物理地址。以前课上学过二级页表、四级页表,其实本质都一样:一层层往下查,最终找到物理页。这个翻译过程由 CPU 的 MMU 硬件完成,内核只负责维护页表结构和内容。翻译结果还会被缓存进 TLB,所以上下文切换时 TLB 的失效和重建是性能热点,这也是为什么内核里有很多“避免频繁切换进程”的优化。
虚拟内存机制带来的杀手级特性是隔离与共享并存。每个进程的地址空间是隔离的——你写自己的虚拟地址,不可能直接踩到别的进程的内存;但通过 mmap 的 MAP_SHARED、文件映射、共享内存这些机制,又能精确地共享指定的区域。这就是“沙盘游戏”:每个人看着都是自己的世界,但管理员可以悄悄把两块沙盘拼在一起。
还有一个容易被忽略的点:用户空间和内核空间的地址布局。在 x86-64 Linux 里,用户空间占低地址区,内核空间在高地址区,进程的内核栈位于内核地址空间,每个进程都有自己的内核栈。这也是理解“用户态切换到内核态时栈怎么切换”的基础——切到内核态后,CPU 用的是当前进程对应的内核栈,不是用户栈。搞混这个概念,很多排障会走弯路。
4.3 把 VFS 当“翻译官”,一切文件系统都在这层被统一
文件系统可能是六个子系统里最抽象、也最适合建立分层心智模型的一个。想象一下:同样是“读文件”这个操作,磁盘上的 ext4、网络上的 NFS、内存里的 tmpfs、内核信息伪装的 procfs,它们的实现天差地别。但在用户态,你拿到的永远是同一个 read 系统调用。谁在做翻译?VFS(虚拟文件系统层)。
VFS 定义了四个核心对象:super_block(整个文件系统的元信息)、inode(文件本身的元信息,包括权限、大小、数据块位置)、dentry(目录项缓存,负责路径解析)、file(打开的文件实例,保存读写位置)。这四者的关系可以这样记:路径解析靠 dentry,文件身份靠 inode,打开文件靠 file,超级块是文件系统这个“国家”的户口本。
把 VFS 当成“翻译官”,你就能明白为什么挂载点、文件系统类型、inode 这些概念会混在一起出现。排障时遇到“磁盘满了”但 df 显示空间还有很多这种经典问题,你得知道可能是 inode 耗尽而不是数据块耗尽——这时候脑子里有 inode 这个概念,比临时翻文档要快得多。
再补一个和性能密切相关的心智模型:page cache。内核把读过的磁盘页缓存到内存里,写操作也先写缓存、后台再回写磁盘。这套机制让文件读写快了好几个数量级,但也带来一个问题——同时读写同一个文件时,缓存和磁盘数据的一致性需要内核用一套复杂的回写与同步机制来保证。理解“数据先到缓存,落盘是异步的”,对定位“明明 wrote 了,断电后数据丢了”这类场景非常有用。
4.4 内核的运行节奏:中断上下文与进程上下文是两条赛道
看内核代码时,新手最容易犯的一个认知错误,是用“写普通多线程程序”的思维去理解内核。普通程序里,线程之间无非是加锁、排队、同步,但在内核里,除了常规的进程上下文(比如你在系统调用里运行),还有一类完全不同的上下文——中断上下文。
中断上下文的特点是:它随时可能抢占 CPU,和当前正在运行的进程毫无关系;它不隶属于任何进程,也不参与正常的调度;它里面不能睡眠(不能调用会阻塞的函数),因为睡眠需要调度器,而调度器在中断上下文里是没法正常工作的。这也是为什么中断处理程序里不能随便用 kmalloc(GFP_KERNEL),因为它可能睡眠。
为了解决“中断处理不能久留”这个矛盾,内核把中断处理拆成两半:上半部(hardirq)只处理最紧急的硬件响应,尽快结束;下半部(softirq、tasklet、工作队列)在更安全的时机完成剩余工作。工作队列甚至会在内核线程的上下文里执行,可以睡眠。理解“紧急的赶紧做,不紧急的排队做”这条节奏,你就看懂了内核里大量机制存在的原因。
中断风暴是一个很实际的风险。硬件故障或驱动写得不好,可能导致中断疯狂触发,CPU 一直在处理中断,用户进程根本得不到调度,系统表现为“假死”。排障时如果 top 看到各进程 CPU 使用率都很低但系统卡顿,可以先用 cat /proc/interrupts 看看中断计数是否异常暴涨,这是定位中断风暴的第一步。
5. 心智模型怎么用:面试答题、定位问题、内核裁剪三场景实录
5.1 面试题背后的模型:从“背答案”到“推答案”
这部分我在前面反复提到,现在用两个高频面试题做示范,你看同样的知识点,有了心智模型之后怎么答。
第一题:“用户态和内核态有什么区别?”没有模型的人会背:内核态有高权限,用户态没权限。有模型的人会从边界、路径、风险三层展开:硬件层面特权级不同,关键资源只允许内核态访问;用户态要触发内核功能,只能走系统调用这条“正规通道”;隔离用户态还有一个目的,是隔离故障,让单个程序崩溃不至于毁掉整个系统。然后再补一句“切换涉及栈切换和现场保存,所以频繁模式切换是有性能开销的”,整个答案的层次立刻不一样。
第二题:“进程和线程有什么区别?”有模型的人会讲:进程是资源容器,线程是调度单位。fork 出一个新的进程,等于新开了容器加新调度实体;pthread_create 创建线程,是在已有容器里加一个新的调度实体,共享地址空间、文件表等资源。再展开一点,线程有自己的内核栈和用户栈,所以线程是独立执行流;但如果一个线程崩了,整个进程地址空间可能跟着完蛋。这个答案比“进程开销大、线程开销小”这种套话有说服力得多。
我给你的建议是:把每一道面试题都还原到它背后的心智模型。你不用刷几百道题,你只要把“进程管理、内存管理、文件系统、网络、设备、系统调用”这六张地图刻在脑子里,面试题大概率逃不出这些模型的范围。
5.2 定位内核问题:先看是哪个子系统,再看是哪条路径
做内核相关排障,最忌讳的是一上来就翻源代码。我的习惯是“按图索骥”五步走。
第一步,看日志。dmesg 里通常有最直接的线索,panic 时的调用栈会告诉你崩在哪个函数、属于哪个子系统。别急着读源码,先把调用栈里出现的内核函数和你心智模型里的子系统对应上。比如看到 do_page_fault、handle_mm_fault,那是内存管理;看到 __schedule、pick_next_task_fair,那是调度器。
第二步,量化。用 top/pidstat 看 CPU 和内存,用 iostat 看磁盘 IO,用 sar 看网络。这个阶段的目标是把“系统出问题”这个大现象拆成“哪个资源是瓶颈”的小线索。top 里看到大量时间花在 sys 上,说明瓶颈在内核态;要缩小到具体系统调用,可以用 strace -c 做系统调用频率统计。
第三步,追踪。定位到具体路径后,再决定用什么工具深入。ftrace 可以用来跟踪函数调用,perf 可以做采样和热点分析,bpftrace 可以写动态探针。这些工具的用法不是这一篇能讲完的,但方向要选对:你想知道“内核函数被谁调用了”,用 ftrace 的 function_graph 最快;你想知道“CPU 时间花在哪个内核函数”,perf top 最直接。
第四步,复现和验证。改代码前先想清楚怎么验证。比如怀疑驱动导致的中断风暴,你可以先 rmmod 该驱动观察 /proc/interrupts 的计数变化,再决定是否值得加 printk 或 ftrace 探针。注意生产环境上不要随便加载不稳定的内核模块,最好先在测试机复现。
第五步,查社区和源码。带着明确的问题去查 LKML、内核文档和源码,效率会高很多。很多人排障失败不是因为没有工具,而是因为没有问题域——在心智模型里定位好“哪个子系统、哪条路径”后,再去查资料,你会发现搜什么关键词都非常清晰。
5.3 内核裁剪:什么能剪,什么不能动
嵌入式 Linux 项目几乎都要做内核裁剪。裁剪的大方向是从配置项出发,删掉不用的功能来缩小镜像体积、加快编译和启动时间。但裁剪不等于瞎删,我给你的第一个建议是:拿一套可用的 defconfig 作为基线,在你的硬件平台上先跑通,再逐步关功能,每关一个配置就编译、启动、做冒烟测试一次,切忌一次性删一大批配置。
先说哪些一般可以关:不需要的驱动(网卡、声卡、蓝牙、触控都按硬件选)、不需要的文件系统(用不到 ext4 就关、用不到 NFS 就关)、不需要的网络协议子模块(IPv6 用不到可以关)、调试相关配置(CONFIG_DEBUG_INFO 能不开就不开,能省大量空间和编译时间)。
再说哪些千万不能动:进程调度、内存管理这些基础子系统的核心配置,必须保持默认;RCU、per-cpu、内核锁机制这些底层基础设施,绝对不能乱关;initramfs 或内嵌根文件系统的支持,要根据你的启动方案保留。我见过有人为了裁剪,把 CONFIG_PREEMPT 关闭导致实时性崩了,也见过把 RCU 关闭直接起不来,这些都属于把“支持系统运转的地基”给拆了。
裁剪完之后怎么验证?除了启动测试,别忘了看 /proc/config.gz 或 make menuconfig 导出的 .config,跟你的裁剪清单逐项核对;镜像大小用 ls -lh 看;启动时间可以用内核的 printk.time 和 systemd-analyze(如果用了 systemd)协助分析。把这套流程固化下来,你会少踩很多“编译能过但板子起不来”的坑。
最后说点实际的
这篇文章写到这里,技术内容已经讲完了,最后我想用自己踩过的坑做个收尾。我最早学内核的时候,也是从 start_kernel 开始硬读源码,结果三天就放弃了。后来换了一个思路:每学一个子系统,先花半小时在白纸上画一页流程——谁调用谁、谁在什么上下文运行、谁和谁共享什么数据、出错从哪里报。画完这页纸,再去读源码,每一个函数的出现都像老朋友,再也不觉得陌生。
这个习惯我一直用到现在。说实话,没有人能一次把整个内核装进脑子里,但只要你手里有一张地图,知道自己当前站在哪个子系统、面前是哪条调用路径,你就永远知道下一步该往哪儿走。这比记住一百个函数名都管用。后续的 Linux 内核专栏里,我会按这张地图一个一个子系统深入地写,下一篇大概率会先聊进程与调度这个“内核的心脏”,到时候咱们再接着掰扯。