1、Ftrace初探:什么是Ftrace?内核追踪的“瑞士军刀”,我的第一个trace命令。

说实话,我第一次接触Ftrace的时候,心里是有点抵触的。那时候我还在做嵌入式系统优化,遇到一个诡异的调度延迟问题——进程明明该醒了,却要等好几毫秒才被调度上CPU。我翻遍了/proc下的各种统计文件,看了半天也没找到根因。后来一位老同事跟我说:“你试试Ftrace吧,这玩意儿能告诉你内核里到底发生了什么。”

一试,就回不去了。

1.1 Ftrace到底是什么?

Ftrace,全称是Function Tracer。它是Linux内核内置的一个追踪框架。说白了,它就像内核里的“黑匣子记录仪”——能记录函数调用、中断触发、进程调度、系统调用等几乎所有的内核活动。

为什么叫它“瑞士军刀”?因为它的功能太全了:

  • 函数追踪:记录哪些函数被调用了,调用了多少次,花了多长时间
  • 函数图追踪:展示函数调用的完整调用链,像火焰图一样直观
  • 事件追踪:追踪特定内核事件,比如调度切换、中断上下文的进入和退出
  • 延迟追踪:测量中断关闭时间、抢占关闭时间、调度延迟等关键指标
  • 动态探针:可以在任意函数入口或出口动态插入追踪点,无需重新编译内核

核心要点:Ftrace最大的优势是——它就在内核里,不需要安装额外的工具,不需要加载内核模块(大部分功能),性能开销极低。你只需要挂载一个debugfs文件系统,就能开始追踪。

1.2 我的第一个trace命令

好,咱们直接上手。别怕,第一个命令很简单。

首先,确认你的内核支持Ftrace:

# 检查debugfs是否挂载
mount | grep debugfs

# 如果没有挂载,手动挂载
mount -t debugfs none /sys/kernel/debug

# 进入Ftrace目录
cd /sys/kernel/debug/tracing

嗯,这里要注意:有些发行版默认没挂载debugfs。我遇到过好几次,一上来就报错“目录不存在”,然后才想起来忘了挂载。所以第一步先检查挂载,养成习惯。

接下来,我们试试最简单的追踪——看看当前系统里有哪些可用的追踪器:

cat available_tracers

你会看到类似这样的输出:

hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

这些就是Ftrace内置的追踪器。每个追踪器干不同的事:

追踪器名称 作用
function 追踪所有内核函数的调用
function_graph 以调用图形式展示函数调用关系
wakeup 测量进程从被唤醒到被调度执行的延迟
wakeup_rt 专门测量实时进程的调度延迟
wakeup_dl 测量Deadline调度类的延迟
hwlat 检测硬件延迟(比如SMI、中断风暴)
blk 追踪块设备I/O
nop 空追踪器,关闭追踪时使用

我个人习惯,第一次接触一个新系统时,先用function_graph看看内核在忙什么。咱们来试试:

# 选择追踪器
echo function_graph > current_tracer

# 开始追踪(默认已经开始了)
# 查看追踪结果
cat trace

你会看到类似这样的输出:

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 0)   0.125 us    |  __fdget();
 0)               |  __fget_light() {
 0)   0.083 us    |    __fget_files();
 0)   0.375 us    |  }
 0)               |  __fget_light() {
 0)   0.083 us    |    __fget_files();
 0)   0.333 us    |  }
 0)   0.625 us    |  __fdget();

看到没?每一行都记录了:哪个CPU执行的、函数执行耗时、函数调用关系。缩进表示调用深度,花括号表示函数的进入和退出。

小技巧:如果输出太多,你可以先清空缓冲区再开始追踪:echo > trace。然后只追踪你关心的那几秒钟。

1.3 为什么Ftrace比printk强?

你可能会想:“我加printk也能看函数调用啊,干嘛用Ftrace?”

这个问题我当年也问过。直到有一次,我在一个生产环境的内核模块里加了几个printk,结果系统负载直接飙升了30%——printk的串行化输出太慢了,而且会触发控制台锁,影响实时性。

Ftrace不一样:

  • 零开销开关:不启用时,Ftrace的hook点只多了一条条件判断指令(nop),几乎不影响性能
  • 环形缓冲区:追踪数据写入内核内存中的环形缓冲区,不会触发磁盘I/O或控制台输出
  • 动态过滤:可以只追踪你关心的函数,而不是全量追踪
  • 无需重新编译:动态插桩技术让你可以在运行中的内核上随时开启/关闭追踪

说白了,printk是“大炮打蚊子”,而Ftrace是“手术刀”。

1.4 一个真实案例:用Ftrace揪出调度延迟

我记得有一次,一个音频处理进程在播放时出现卡顿。用户说“声音断断续续的”,但系统负载看起来很正常,CPU使用率也不高。

我用了wakeup追踪器:

# 切换到wakeup追踪器
echo wakeup > current_tracer

# 设置要追踪的目标进程PID(假设是1234)
echo 1234 > set_ftrace_pid

# 开始追踪
cat trace

结果发现,这个进程被唤醒后,平均要等2.3毫秒才被调度上CPU。而正常值应该在100微秒以内。进一步追踪发现,是另一个内核线程在持有某个锁时被中断打断了,导致唤醒延迟被拉长。

如果没有Ftrace,这种问题几乎不可能定位——你看到的只是“进程没跑”,但不知道“为什么没跑”。Ftrace把整个调度链完整地展示在你面前。

注意:Ftrace的环形缓冲区默认大小是1MB左右。如果追踪的数据量太大,旧数据会被覆盖。你可以通过buffer_size_kb文件调整缓冲区大小。我曾经因为没调大缓冲区,结果关键数据被冲掉了,白忙活半天。

1.5 小结:Ftrace的定位

Ftrace不是万能的。它擅长的是:

  • 函数级别的调用追踪
  • 调度延迟、中断延迟的精确测量
  • 内核事件的时序分析

但它不擅长:

  • 用户空间程序的性能分析(那是perf的事)
  • 内存泄漏检测(那是kmemleak的事)
  • 长时间、大范围的性能监控(那是eBPF的事)

不过,对于内核调度和性能瓶颈的初步诊断,Ftrace绝对是你工具箱里最趁手的那把刀。接下来的章节,我会带你深入每个追踪器的使用技巧,以及如何用它们解决真实世界中的性能问题。

准备好了吗?咱们继续往下走。