内核探针是一组用于收集 Linux 内核调试和性能信息的工具。开发人员和系统管理员使用它们来调试内核,或查找系统性能瓶颈。报告的数据然后可以用于调整系统以获得更好的性能。
您可以将这些探针插入到任何内核例程中,并指定在命中特定断点后调用的处理程序。 内核探针的主要优点是,您在探针中进行更改后,无需重建内核和重新启动系统。
要使用内核探针,通常需要编写或获取特定的内核模块。 这些模块包括 init 和 exit 函数。 init 函数(例如 register_kprobe())注册一个或多个探针,而 exit 函数则取消注册它们。 注册函数定义 探针插入的位置 和 命中探针后调用的处理程序。 要一次性注册或取消注册一组探针,可以使用相关的 register_<PROBE_TYPE>probes() 或 unregister_<PROBE_TYPE>probes() 函数。
调试和状态消息通常使用 printk 内核例程报告。 printk 是用户空间 printf 例程的内核空间等效项。 有关 printk 的更多信息,请参阅 记录内核消息。 通常,您可以可以通过检查 systemd 日志的输出来查看这些消息(请参阅 “参考”手册,第 11 章“journalctl:查询 systemd 日志”)。 有关日志文件的更多信息,请参阅 第 3 章,系统日志文件。
内核探针在以下架构上完全实现
x86
AMD64/Intel 64
Arm
POWER
内核探针在以下架构上部分实现
IA64(不支持指令 slot1 上的探针)
sparc64(尚未实现返回探针)
有三种类型的内核探针:Kprobes、Jprobes 和 Kretprobes。 Kretprobes 有时称为 返回探针。 您可以在 Linux 内核中找到所有三种类型探针的源代码。 请参阅目录 /usr/src/linux/samples/kprobes/(软件包 kernel-source)。
Kprobes 可以附加到 Linux 内核中的任何指令。 注册 Kprobes 时,它会将断点插入到被探测指令的第一个字节处。 当处理器命中此断点时,处理器寄存器将被保存,并且处理将传递给 Kprobes。 首先,将执行 预处理程序,然后将执行被探测的指令,最后将执行 后处理程序。 然后将控制权传递给探针点后的指令。
Jprobes 通过 Kprobes 机制实现。 它插入到函数的入口点,并允许直接访问被探测函数的参数。 其处理程序例程必须具有与被探测函数相同的参数列表和返回值。 要结束它,请调用 jprobe_return() 函数。
当命中 jprobe 时,处理器寄存器将被保存,并且指令指针将定向到 jprobe 处理程序例程。 然后将控制权传递给处理程序,其寄存器内容与被探测的函数相同。 最后,处理程序调用 jprobe_return() 函数,并将控制权切换回控制函数。
通常,您可以将多个探针插入到一个函数中。 但是,Jprobe 仅限于每个函数一个实例。
Kprobes 的编程接口由用于注册和取消注册所有使用的内核探针及其关联探针处理程序的函数组成。 有关这些函数及其参数的更详细描述,请参阅 第 5.5 节,“更多信息” 中的信息来源。
register_kprobe()
在指定的地址插入断点。 命中断点时,将调用 pre_handler 和 post_handler。
register_jprobe()
在指定的地址插入断点。 该地址需要是被探测函数的第一个指令的地址。 命中断点时,将运行指定的处理程序。 处理程序应具有与被探测函数相同的参数列表和返回类型。
register_kretprobe()
为指定的函数插入返回探针。 当被探测的函数返回时,将运行指定的处理程序。 此函数在成功时返回 0,或在失败时返回负错误编号。
unregister_kprobe()、unregister_jprobe()、unregister_kretprobe()删除指定的探针。 可以在探针注册后随时使用它。
register_kprobes()、register_jprobes()、register_kretprobes()插入指定数组中的每个探针。
unregister_kprobes()、unregister_jprobes()、unregister_kretprobes()删除指定数组中的每个探针。
disable_kprobe()、disable_jprobe()、disable_kretprobe()暂时禁用指定的探针。
enable_kprobe()、enable_jprobe()、enable_kretprobe()暂时启用禁用的探针。
在最近的 Linux 内核中,Kprobes 仪器化使用内核的 debugfs 接口。 它可以列出所有已注册的探针并全局打开或关闭所有探针。
所有当前已注册探针的列表位于 /sys/kernel/debug/kprobes/list 文件中。
saturn.example.com:~ # cat /sys/kernel/debug/kprobes/list c015d71a k vfs_read+0x0 [DISABLED] c011a316 j do_fork+0x0 c03dedc5 r tcp_v4_rcv+0x0
第一列列出内核中插入探针的地址。 第二列打印探针的类型:k 表示 kprobe,j 表示 jprobe,r 表示返回探针。 第三列指定探针的符号、偏移量和可选的模块名称。 以下可选列包括探针的状态信息。 如果探针插入到不再有效的虚拟地址上,则标记为 [GONE]。 如果探针被暂时禁用,则标记为 [DISABLED]。
要了解有关内核探针的更多信息,请查看以下信息来源
关于内核探针的详尽但更具技术性的信息位于 /usr/src/linux/Documentation/trace/kprobes.txt(软件包 kernel-source)。
所有三种类型探针的示例(以及相关的 Makefile)位于 /usr/src/linux/samples/kprobes/ 目录中(软件包 kernel-source)。
有关 Linux 内核模块和 printk 内核例程的深入信息可以在 Linux 内核模块编程指南 中找到