通过
ftrace
来了解 Linux 内核内部工作方式是一个好方法。
操作系统的内核是最难以理解的软件之一。自从你的系统启动后,它会一直在后台运行。尽管每个用户都不与内核直接交互,但他们在内核的帮助下完成自己的计算任务。与内核的交互发生在调用系统调用或者用户日常使用的各种库或应用间接调用了系统调用。
在之前的文章里我介绍了如何使用 strace 来追踪系统调用。然而,使用 strace
时你的视野是有限的。它允许你查看特定参数的系统调用。并在工作完成后,看到其返回值或状态,以表明是成功还是失败。但是你无法知道内核在这段时间内发生了什么。除了系统调用外,还有很多其他活动内核中发生,而你却视而不见。
ftrace 介绍
本文的旨在通过使用一个名为 ftrace
的机制来阐明追踪内核函数的一些情况。它使得任何 Linux 用户可以轻松地追踪内核,并且了解更多关于 Linux 内核内部如何工作。
ftrace
默认产生的输出往往是巨大的,因为内核总是忙碌的。为了节省空间,很多情况下我会通过截断来给出尽量小的输出。
我使用 Fedora 来演示下面的例子,但是它们应该在其他最新的 Linux 发行版上同样可以运行。
启用 ftrace
ftrace
现在已经是内核中的一部分了,你不再需要事先安装它了。也就是说,如果你在使用最近的 Linux 系统,那么 ftrace
是已经启用了的。为了验证 ftrace
是否可用,运行 mount
命令并查找 tracefs
。如果你看到类似下面的输出,表示 ftrace
已经启用,你可以轻松地尝试本文中下面的例子。下面有些命令需要在 root 用户下使用(用 sudo
执行是不够的)。
# mount | grep tracefs
none on /sys/kernel/tracing type tracefs (rw,relatime,seclabel)
要想使用 ftrace
,你首先需要进入上面 mount
命令中找到的特定目录中,在那个目录下运行文章中的其他命令。
# cd /sys/kernel/tracing
一般的工作流程
首先,你需要理解捕捉踪迹和获取输出的一般流程。如果你直接运行 ftrace
,不会运行任何特定的 ftrace
命令。相反的,基本操作是通过标准 Linux 命令来写入或读取一些文件。
一般的步骤如下:
可用的追踪器类型
有多种不同的追踪器可供使用。之前提到,在运行任何命令前,你需要进入一个特定的目录下,因为需要的文件在这些目录下。我在我的例子中使用了相对路径(而不是绝对路径)。
你可以查看 available_tracers
文件内容来查看所有可用的追踪器类型。你可以看下面列出了几个。不需要担心这些:
$ pwd
/sys/kernel/tracing
$ sudo cat available_tracers
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop
在所有输出的追踪器中,我会聚焦于下面三个特殊的:启用追踪的 function
和 function_graph
,以及停止追踪的 nop
。
确认当前的追踪器
通常情况默认的追踪器设定为 nop
。即在特殊文件中 current_tracer
中的 “无操作”,这意味着追踪目前是关闭的:
$ pwd
/sys/kernel/tracing
$ sudo cat current_tracer
nop
查看追踪输出
在启用任何追踪功能之前,请你看一下保存追踪输出的文件。你可以用 cat 命令查看名为 trace
的文件的内容:
# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 0/0 #P:8
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
启用 function 追踪器
你可以通过向 current_tracer
文件写入 function
来启用第一个追踪器 function
(文件原本内容为 nop
,意味着追踪是关闭的)。把这个操作看成是启用追踪的一种方式:
$ pwd
/sys/kernel/tracing
$ sudo cat current_tracer
nop
$ echo function > current_tracer
$
$ cat current_tracer
function
查看 function 追踪器的更新追踪输出
现在你已启用追踪,是时候查看输出了。如果你查看 trace
文件内容,你将会看到许多被连续写入的内容。我通过管道只展示了文件内容的前 20 行。根据左边输出的标题,你可以看到在某个 CPU 上运行的任务和进程 ID。根据右边输出的内容,你可以看到具体的内核函数和其父函数。中间显示了时间戳信息:
# sudo cat trace | head -20
# tracer: function
#
# entries-in-buffer/entries-written: 409936/4276216 #P:8
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
-0 [000] d... 2088.841739: tsc_verify_tsc_adjust preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
cupsd-1066 [004] .... 3308.989545: ext4_file_getattr