QIAOPANG.COM · 陈天(TYR CHEN)2013–2026 精选技术与随笔文集 · 共 260 篇按时间顺序浏览(2013 → 2026)↗
程序人生 · 陈天文集QiaoPang · Systems, Craft & Agentic Era
时间轴 ↗

黑匣子

黑匣子

很多人看到这个标题的第一印象可能是 —— 这是要讲飞机上的黑匣子么?有点关系,但主要讲软件中的flight data recorder。不过我们先从黑匣子讲起。

Flight Data Recorder(FDR)用于记录飞机行驶过程中的各种关键数据,其规格如下:

  • 电源:115V AC 或 28V DC
  • 电源能力:30天不间断
  • 使用期限:至多6年
  • 包含水下定位装置
  • 能记录25小时不间断数据
  • 能经受1100度高温

FDR的硬件架构:

  • CPU card
  • Ana日志 card
  • Discrete card
  • Frequency card
  • Audio card
  • Power supply card

水下定位装置能在深达12000ft的水域正常工作,发送37.5khz的声音信号。

FDR会记录些什么?

  • time
  • pressure
  • altitude
  • airspeed
  • vertical acceleration
  • magnetic heading
  • control-column position
  • rudder-pedal position
  • control-wheel position
  • horizontal stablizer
  • fuel flow

FDR(以及CVR, Cockpit Voice Recorder,记录机组人员及空乘的对话)对于定位航班失事的原因非常重要,因为它是进行RCA(Root Cause Analysis)的第一手资料。没有FDR,或者找不到FDR,基本上客机失事就会成为一个永远的谜团。

下图展示了FDR在飞机上的位置,以及数据的收集:

fdr location

而越来越复杂的软件系统也有这样的问题。运行在客户端的软件,运行的过程中突然崩溃,很多时候单凭崩溃时产生的core很难找到问题的原因(请类比飞机碎片)。这时候,我们就要求助于软件系统中的FDR了。

最典型,也是最原始的FDR是日志。几乎所有的软件都有日志。通过分析系统崩溃前的日志,我们可以找到一些问题的原因,但很多问题依旧无法靠日志来进行找到根本原因,这是因为,日志的定位是记录系统的关键路径,关键路径有时候并不一定等同于关键数据。

打开调试开关,提供调试版本?很多问题是特定情况的突发事件,需要很隐晦条件才能触发,调试开关,调试版本这样的事后诸葛亮,只要问题不能复现,就没有用武之地。

这就是软件系统中的黑匣子FDR存在的价值和意义了。

网上关于软件系统中的FDR的信息还很少,除了我了解的systemtap的flight record mode(FRM)外,就是Java中对FDR的支持了。我们先看Java FDR。

Java FDR是建立在JVM中的,所以只要JVM使能了FDR,所有运行在其上的application都将受益(这又是indirection好处的一大佐证)。它提供几大功能:

(1) Tracer:tracer捕捉JVM和application中的runtime数据,包括GC,synchronization,compiler (JIT),CPU usage,exceptions,I/O等等信息。

(2) Profiler:定期对系统采样(追踪系统的性能),或者on-demand profiling,当且仅当需要时可以打开进行全数据采集(对性能有较大影响,但仅在特定事件下或者特定配置下触发)。

(3) after-the-fact analysis:即事后分析。捕获的运行时数据会放在一片循环的缓存中(kernel memory或文件)。当意外事件发生时(比如崩溃),这片缓存中的数据被输出,用于定位问题。

我对Java和JVM几乎没有涉猎,所以以上理解都是皮毛,希望Java大牛来拍砖。

systemtap的飞行模式(FRM)是另外一种FDR。

如果你在linux下进行系统开发而没有涉猎systemtap,建议研究一下。systemtap给予你写段脚本runtime注入到目标软件中,从软件中获取信息的能力。这功能如此凶悍,所以你需要“sudo”的权限来运行systemtap。简单讲一下systemtap的原理:

(1) 首先用户可以撰写stap脚本,选择要注入的进程和函数

(2) stap脚本被编译成c文件,然后编译成kernel module,通过insmod加载进内存。

(3) kernel module初始化时会使用do_brk()为目标进程分配新的内存,把要注入的代码写入新的内存区域。

(4) 如果要probe的是某条指令,则将该指令替换成跳转到注入区执行;如果probe的是系统调用,则在系统调用中完成代码的注入。

(5) 当注入代码执行完毕后,恢复注入点的代码,并执行,对于目标进程而言就像正常执行一样。

下图是对系统调用 write 进行probe的一个示例:

Code inject

这个原理和病毒基本一样。只不过,它的目的是在不用修改目标程序源代码的情况下,从目标中获取统计信息,达到运行时调试 / 性能剖析的功效。

如果打开systemtap的飞行模式,则其会不断往1MB(可配置)的一个内核缓冲区中写监控数据,并且在需要的时候输出进行分析。

对于FDR而言,有了称手的工具是远远不够的。最关键的是:究竟什么样的数据才是关键数据,值得记录,用以崩溃后的分析?如果采样太多的系统数据,会给系统本身的性能带来冲击,而且过多的数据杂音很大,反而不便于分析;如果采样的数据不够,无法进行根本原因分析,那么FDR就失去了应有的作用。

此外,对数据合理的可视化也非常重要,因为它能帮助人更好地理解数据间的关系,从而为根本原因分析提供线索。

MORE IN 系统与架构

同专栏延伸阅读

查看「系统与架构」全部文章 →

日志的未来:必也正名乎

我们花了十年,把可观测性盖成一座漂亮的大厦,地基却是漂浮的字符串:字段名靠默契、基数靠运气、账单靠忍。这篇文章给出一个激进的方案,用 protobuf 给每一件被观测的事正名,把日志、指标、审计统一成一份带版本、带 owner、带保留策略的事件目录,旁边留一扇叫 ForensicEvent 的逃生舱兜住所有未知未知。AI 已经成为代码的主笔,schema-first 的最后一道阻力随之消失。是时候让 schema 重新成为可观测性大厦的

New Hope (2025–2026)10,772 字 · 24 分钟

彻底抛弃多环境:用 Rust 与 Pingora 重塑下一代 Serverless 架构

当我们退回原点重新思考"计算"的本质,会发现多环境是个幻觉,K8s 是个过度抽象,而 Serverless 的冷启动和连接池风暴是自找的。这篇文章拆解一套基于 Baggage Header 路由、Pingora 智能网关、Firecracker 微虚拟机和 Rust Data Bus 的无环境架构,试图回答:Post-Kubernetes 时代的计算该长什么样。

New Hope (2025–2026)23,788 字 · 53 分钟