3. ftrace 基础框架:tracefs 挂载与核心操作

ftrace 是 Linux 内核内置的跟踪器,用于分析内核行为、函数调用和调度延迟。在实时性测试中,ftrace 是测量调度延迟、抢占延迟和中断响应时间的关键工具。本章将系统讲解 ftrace 的基础框架,包括 tracefs 文件系统的挂载、跟踪器的查看与选择、跟踪数据的读取以及跟踪启停控制。

3.1 tracefs 文件系统挂载

ftrace 通过 tracefs 虚拟文件系统暴露控制接口和跟踪数据。在大多数现代 Linux 发行版中,tracefs 默认挂载在 /sys/kernel/tracing 目录下。如果未自动挂载,需要手动执行挂载操作。

挂载命令:
# 挂载 tracefs 到标准路径
mount -t tracefs tracefs /sys/kernel/tracing

# 或使用 debugfs(旧方式,兼容性考虑)
mount -t debugfs debugfs /sys/kernel/debug
# 此时 ftrace 接口位于 /sys/kernel/debug/tracing
推荐做法: 建议始终使用 /sys/kernel/tracing 路径,这是内核主线推荐的标准化接口。在实时性测试脚本中,建议在脚本开头检查挂载状态并自动挂载。

挂载成功后,/sys/kernel/tracing 目录下会出现一系列控制文件和跟踪数据文件。下表列出了最核心的文件及其用途:

文件/目录 用途 读写属性
available_tracers 列出当前内核支持的所有跟踪器 只读
current_tracer 设置或查看当前激活的跟踪器 读写
tracing_on 全局跟踪开关(1=开启,0=关闭) 读写
trace 读取跟踪数据(人类可读格式) 只读
trace_pipe 流式读取跟踪数据(持续输出) 只读
buffer_size_kb 每个 CPU 的跟踪缓冲区大小(KB) 读写
tracing_cpumask 限制跟踪只在指定 CPU 上运行 读写
set_ftrace_filter 函数跟踪过滤器(白名单) 读写
set_ftrace_notrace 函数跟踪排除列表(黑名单) 读写
注意事项: 在实时性测试环境中,buffer_size_kb 的默认值通常较小(约 1-4 MB)。对于长时间调度延迟跟踪,建议增大到 16-64 MB,避免跟踪数据被覆盖丢失。

3.2 available_tracers:查看可用跟踪器

available_tracers 文件列出了当前内核编译时启用的所有跟踪器。不同内核配置支持的跟踪器种类不同,实时性测试中常用的跟踪器包括:

  • nop —— 空跟踪器,不记录任何数据,用于关闭跟踪。
  • function —— 函数调用跟踪,记录内核函数的进入和退出。
  • function_graph —— 函数调用图跟踪,显示函数调用层级和耗时。
  • preemptirqsoff —— 跟踪抢占和中断关闭的最大时间(实时性关键指标)。
  • preemptoff —— 仅跟踪抢占关闭时间。
  • irqsoff —— 仅跟踪中断关闭时间。
  • wakeup —— 跟踪最高优先级任务的调度唤醒延迟。
  • wakeup_rt —— 跟踪实时(RT)任务的调度唤醒延迟。
  • wakeup_dl —— 跟踪 deadline 调度类的任务唤醒延迟。
查看命令:
cat /sys/kernel/tracing/available_tracers
示例输出:
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

输出结果中,跟踪器名称之间用空格分隔。当前激活的跟踪器会用方括号 [] 标记。例如 [function] 表示当前正在使用 function 跟踪器。

3.3 current_tracer:设置当前跟踪器

current_tracer 文件用于激活指定的跟踪器。写入跟踪器名称即可切换,写入 nop 则关闭跟踪。

设置跟踪器命令:
# 切换到 function_graph 跟踪器
echo function_graph > /sys/kernel/tracing/current_tracer

# 关闭跟踪(切换到 nop)
echo nop > /sys/kernel/tracing/current_tracer

# 查看当前跟踪器
cat /sys/kernel/tracing/current_tracer
实时性测试建议: 测量调度延迟时,优先使用 wakeup_rt 或 wakeup_dl 跟踪器。它们专门记录高优先级任务从唤醒到开始执行的时间差,直接反映调度延迟。

切换跟踪器时,内核会自动重置跟踪缓冲区。因此,先设置跟踪器,再开启跟踪是标准操作流程。

3.4 tracing_on:跟踪启停控制

tracing_on 是全局跟踪开关,控制是否向缓冲区写入跟踪数据。写入 1 开启跟踪,写入 0 暂停跟踪。注意:暂停跟踪不会清空缓冲区,已记录的数据仍然可以读取。

启停命令:
# 开启跟踪
echo 1 > /sys/kernel/tracing/tracing_on

# 暂停跟踪
echo 0 > /sys/kernel/tracing/tracing_on

# 查看当前状态
cat /sys/kernel/tracing/tracing_on
常见误区: tracing_on 设置为 0 只是暂停写入,不会清空缓冲区。如果需要清空历史数据,需要向 trace 文件写入空内容或重置跟踪器。标准清空操作为:echo > /sys/kernel/tracing/trace。

3.5 trace 文件读取与数据解析

trace 文件以人类可读的文本格式输出跟踪数据。每行记录包含时间戳、进程信息、CPU 编号和跟踪事件详情。

读取跟踪数据:
# 读取全部数据
cat /sys/kernel/tracing/trace

# 实时流式读取(类似 tail -f)
cat /sys/kernel/tracing/trace_pipe

以下是一个典型的 function_graph 跟踪输出示例:

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 0)   0.125 us    |  _raw_spin_lock();
 0)   0.062 us    |  _raw_spin_unlock();
 0)               |  try_to_wake_up() {
 0)   0.250 us    |    _raw_spin_lock_irqsave();
 0)   0.125 us    |    ttwu_queue();
 0)   0.125 us    |    _raw_spin_unlock_irqrestore();
 0)   1.000 us    |  }
 0)   0.125 us    |  _raw_spin_lock();
 0)   0.062 us    |  _raw_spin_unlock();

各字段含义:

  • CPU —— 事件发生的 CPU 编号。
  • DURATION —— 函数执行耗时(微秒),仅 function_graph 模式有此列。
  • FUNCTION CALLS —— 函数调用层级和名称,缩进表示调用深度。

对于调度延迟分析,重点关注 wakeup_rt 跟踪器的输出,它直接给出从唤醒到调度的时间差:

# tracer: wakeup_rt
#
# Task: PID: 1234 (cyclictest)
# Duration: 45.125 us
# CPU: 2
#
#           <idle>-0     [002] d..3  1234.567890: sched_wakeup: comm=cyclictest pid=1234 prio=95 target_cpu=002
#           <idle>-0     [002] d..3  1234.567935: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 next_comm=cyclictest next_pid=1234 next_prio=95

其中 Duration: 45.125 us 就是调度延迟值,即从 sched_wakeup 事件到 sched_switch 事件的时间差。

3.6 ftrace 基础操作流程 SVG 图

下图展示了 ftrace 基础操作的标准流程,从挂载 tracefs 到读取调度延迟数据的完整步骤:

ftrace 基础操作流程 步骤 1:挂载 tracefs mount -t tracefs tracefs /sys/kernel/tracing 步骤 2:查看可用跟踪器 cat available_tracers 步骤 3:设置跟踪器 echo wakeup_rt > current_tracer 步骤 4:开启跟踪 echo 1 > tracing_on 步骤 5:运行测试负载 cyclictest -p 95 -t 1 -i 1000 步骤 6:读取跟踪数据 cat trace | grep "Duration" 关键文件路径 /sys/kernel/tracing/ ├── available_tracers ├── current_tracer ├── tracing_on 实时性测试跟踪器 wakeup_rt:RT 任务延迟 wakeup_dl:Deadline 延迟 preemptirqsoff:关抢占/中断 function_graph:函数耗时

3.7 实时性测试中的典型操作序列

在实际的实时性测试中,ftrace 操作通常遵循以下标准序列。建议将以下命令封装为脚本,便于重复执行:

完整操作序列(脚本化):
#!/bin/bash
TRACING=/sys/kernel/tracing

# 1. 确保 tracefs 已挂载
if ! mountpoint -q $TRACING; then
    mount -t tracefs tracefs $TRACING
fi

# 2. 清空历史数据
echo nop > $TRACING/current_tracer
echo > $TRACING/trace

# 3. 设置跟踪器(以 wakeup_rt 为例)
echo wakeup_rt > $TRACING/current_tracer

# 4. 增大缓冲区(避免丢失数据)
echo 16384 > $TRACING/buffer_size_kb

# 5. 开启跟踪
echo 1 > $TRACING/tracing_on

# 6. 运行测试程序(示例)
cyclictest -p 95 -t 1 -i 1000 -l 10000

# 7. 暂停跟踪
echo 0 > $TRACING/tracing_on

# 8. 提取调度延迟数据
grep "Duration" $TRACING/trace
性能影响提示: ftrace 本身会引入一定的性能开销,尤其是在 function 和 function_graph 模式下。对于调度延迟测量,wakeup_rt 和 wakeup_dl 跟踪器的开销极小(仅记录特定调度事件),适合在生产环境或接近生产环境的测试中使用。

3.8 常见问题与排查

问题现象 可能原因 解决方案
cat trace 输出为空 跟踪未开启或缓冲区被清空 检查 tracing_on 是否为 1;确认 current_tracer 不是 nop
available_tracers 列表很短 内核编译时未启用相关跟踪器 重新编译内核,开启 CONFIG_FUNCTION_TRACER、CONFIG_PREEMPTIRQ_TRACEPOINTS 等选项
跟踪数据丢失或截断 缓冲区太小,数据被覆盖 增大 buffer_size_kb,或使用 trace_pipe 实时读取
设置跟踪器时报错 Device or resource busy 其他进程正在使用 ftrace 检查是否有其他跟踪工具(如 perf、trace-cmd)占用,先关闭它们
调度延迟数据不准确 ftrace 自身开销干扰了调度 使用 wakeup_rt 而非 function 跟踪器;减小缓冲区大小;限制跟踪 CPU 范围

3.9 本章小结

ftrace 的基础框架由 tracefs 文件系统提供,核心操作包括:

  • 挂载 tracefs —— 确保 /sys/kernel/tracing 可用。
  • 查看 available_tracers —— 确认内核支持的跟踪器类型。
  • 设置 current_tracer —— 选择适合实时性测试的跟踪器(如 wakeup_rt)。
  • 控制 tracing_on —— 精确控制跟踪的启停时机。
  • 读取 trace 文件 —— 解析调度延迟数据,提取 Duration 字段。