第3章 操作系统:同一次慢会在多层显影,先画路径再谈根因

操作系统章先把系统层次图立住:应用、系统调用、内核子系统、文件系统和设备层是同一条路径上的不同显影位点。

本页目录

同一个慢,会穿过用户态、内核态和设备层一起显影

一次请求变慢,现场通常不会只在一个地方露面。

  • 应用日志开始超时
  • 运行时、系统库或系统调用开始变慢
  • 内核子系统里出现等待
  • 文件系统、块层或网络栈开始堆积
  • 底层设备或外部依赖开始拉长服务时间

这些不是天然独立的问题,更常见的情况是同一条路径上的不同显影。

第 3 章的作用,就是先把这张系统层次图立住。图没立住,后面的 CPU、内存、磁盘、网络章都会被读歪,因为你会一直搞不清自己现在看的到底是哪一层。

先把慢放回一条路径,不要先拆成很多组件问题

现场最常见的误判,不是证据太少,而是层次错位。应用层、文件系统层、设备层都在报慢,于是大家默认有三个根因,各自开始查各自的图。

更稳的起手动作只有一个:先回放同一条路径。

你先确认这次慢的是哪类操作,再确认这类操作从哪里进入内核、经过哪些子系统、最后落到哪类设备或外部依赖。路径没接起来前,不要宣布“这里也有问题,那里也有问题”。

路径图至少先分开这六个位置

一张能拿来排障的路径图,不用把所有细节一次画全,但至少要把下面这些位置分开:

  1. 应用本身在做工,还是在等待
  2. 运行时、系统库和系统调用边界
  3. 用户态和内核态的分界
  4. 内核里真正承接这次操作的子系统
  5. 文件系统、块层或网络栈这类中间层
  6. 最终设备、远端服务或外部依赖

少了这几个分界,后面很多词都会被你看成一团。尤其是 系统调用边界用户态 / 内核态 这两个位置,不分开就很难判断等待到底是在应用里形成的,还是已经交给内核以后才开始拉长。

别把“画路径”理解成开会时靠脑补连线。操作系统自己就保留了更硬的路径证据: 用户栈和内核栈。要确认同一次慢到底是怎么从应用走进内核、又在什么位置卡住,真正能把路径钉死的,是调用栈和栈回溯,而不是一张会后补的白板图。

跨过边界本身就要付出代价

用户态和内核态不只是地图上的分层,它们之间的来回穿越本身就有成本。

  • 一次系统调用会触发模式切换
  • 阻塞型系统调用还可能继续触发上下文切换
  • 调用很碎、很频繁时,慢的不一定是底层设备,而是“过路费”本身已经很贵

这会直接改变现场动作。遇到大量细碎 I/O、频繁 syscall 或高并发短请求时,不要只盯着下游有没有排队,还要先问一句: CPU 周期是不是已经被模式切换和上下文切换吃掉了。

多层同时报警时,固定先做这三步

只要同一个问题在多层同时冒出来,就按这个顺序走:

  1. 先并时间窗和操作类型 同一段时间、同一类请求、同一类读写,才配放进同一条路径里。时间窗没并上,后面全是误会。

  2. 再标最早出现等待的位点 不要只记“哪些层也慢了”,而要标出“最早在哪一层开始等”。后面层的异常,有些只是继续传导,有些只是放大显影。

  3. 最后才判断谁最先形成约束 真正值得继续下钻的,不是“现在也异常的那一层”,而是最先出现排队、等待或饱和的那一层。

这个顺序能拦住一种很常见的直觉误判:哪里都有红灯,所以哪里都有根因。

应用超时、文件系统抖动、设备繁忙,经常还是同一件事

最典型的现场,是一条读写链路被看成三个问题。

表面上看:

  • 应用层在报超时
  • 文件系统层在报写回、缓存回收或元数据压力
  • 块层或设备层也看到等待上升

如果直接按层认领根因,会议会很快分裂成三拨解释。

更稳的看法是把它们重新放回一条路径里:

  • 应用层更像在报告结果
  • 文件系统层更像在暴露路径中段的语义、缓存和写回行为
  • 块层和设备层更像在暴露更下游的服务时间与排队状态

这里的转折点,不是你又看到了一个新图,而是你开始按同一条操作路径重排先后顺序。顺序一变,很多“并发根因”会自动塌成一条主线。

还有一种经常被漏掉的显影,不是自上而下的请求链,而是自下而上的硬件打断。底层设备很忙时,会通过异步中断和后续软中断直接抢走 CPU 时间片。表面上看,某个应用“明明没调那个盘,也突然慢了”,实际可能只是它的时间被底层中断服务偷走了。

还要再防一种更隐蔽的断链: 异步队列。画路径时别默认请求会沿着同一个线程同步走完。内核常把中断下半部、软中断或后续处理放进异步工作队列里执行,结果是应用触发和底层 I/O 耗时在时间上、线程上都被拆开了。只盯业务线程自己的同步阻塞,因果链很容易少掉半截。

先留下一张能反复复用的路径图

这张图至少要能让你以后每次都标这四件事:

  • 这次慢的入口是什么
  • 它穿过了哪些边界和子系统
  • 最早开始等待的位置在哪里
  • 后面哪些异常只是继续显影

最小画法可以很简单:

应用 -> 库 / 运行时 -> 系统调用 -> 内核子系统 -> 文件系统 / 网络栈 -> 块层 / 设备 / 远端

每次出问题,只往这条线上补三种标记:

  • 症状最先出现的位置
  • 队列或等待开始拉长的位置
  • 只是被动显影的位置

图上能分出这三类位点,它就是排障图;分不出来,它还只是背景示意图。

再配一张误判卡,拦住最常见的层次错位

这张卡至少要写下三条:

  • 应用层超时,不等于磁盘、网络或 CPU 已经是根因。
  • 文件系统层异常,不等于底层设备一定慢;中间还可能隔着缓存、写回和语义路径。
  • 设备层繁忙,也不等于上层语义可以不看;有些“设备忙”只是上游访问模式把它推到了极限。

写这三条不是为了提醒自己“保持谦虚”,而是为了强制自己每次都回到同一条路径上重排证据。

多层一起响时,就按这份检查动作走

  1. 先固定时间窗、请求类型和操作类型。
  2. 画出这次操作穿过的最短路径,至少标出应用、系统调用、内核子系统、中间层和终点。
  3. 把所有症状按先后标回路径图。
  4. 区分最早等待位点、后续传导位点和被动显影位点。
  5. 只对最早形成约束的那一层继续下钻。

做到这一步,第 3 章的任务就完成了。它解决的是“证据落在哪一层、这些层之间怎么接”。第 4 章才继续回答“既然知道缺哪段证据,该拿哪类工具”。