内核探测 (Kprobes)¶
- 作者:
Jim Keniston <jkenisto@us.ibm.com>
- 作者:
Prasanna S Panchamukhi <prasanna.panchamukhi@gmail.com>
- 作者:
Masami Hiramatsu <mhiramat@kernel.org>
概念:Kprobes 和返回探测¶
Kprobes 允许你无干扰地动态中断任何内核例程,并收集调试和性能信息。你几乎可以在任何内核代码地址 [1] 处设置陷阱,并指定在命中断点时调用的处理程序例程。
目前有两种类型的探测:kprobes 和 kretprobes(也称为返回探测)。kprobe 可以插入到内核中的几乎任何指令上。返回探测在指定函数返回时触发。
在典型情况下,基于 Kprobes 的插桩被打包为内核模块。该模块的初始化函数安装(“注册”)一个或多个探测,而退出函数注销它们。注册函数(例如 register_kprobe())指定了探测要插入的位置以及命中探测时要调用的处理程序。
还有用于批量注册/注销一组 *probes 的 register_/unregister_*probes() 函数。当你必须同时注销大量探测时,这些函数可以加速注销过程。
接下来四个小节解释了不同类型的探测是如何工作的,以及跳转优化是如何工作的。它们解释了为了最好地利用 Kprobes 所需要了解的某些事情——例如,pre_handler 和 post_handler 之间的区别,以及如何使用 kretprobe 的 maxactive 和 nmissed 字段。但如果你急于开始使用 Kprobes,可以直接跳到 支持的架构。
Kprobe 是如何工作的?¶
当注册一个 kprobe 时,Kprobes 会复制被探测的指令,并用断点指令(例如 i386 和 x86_64 上的 int3)替换被探测指令的前几个字节。
当 CPU 命中该断点指令时,会发生一个陷阱(trap),保存 CPU 的寄存器,并通过 notifier_call_chain 机制将控制权传递给 Kprobes。Kprobes 执行与 kprobe 关联的 “pre_handler”,向该处理程序传递 kprobe struct 的地址以及保存的寄存器。
接下来,Kprobes 对被探测指令的副本进行单步执行。(原地单步执行实际指令会更简单,但那样 Kprobes 就必须临时移除断点指令。这会打开一个很小的时间窗口,在此期间其他 CPU 可能会直接滑过探测点。)
在指令单步执行之后,Kprobes 执行与 kprobe 关联的 “post_handler”(如果有的话)。随后,执行从探测点之后的指令继续。
改变执行路径¶
由于 kprobes 可以探测正在运行的内核代码,它可以更改寄存器集,包括指令指针。此操作需要极其小心,例如保持栈帧、恢复执行路径等。由于它是在运行中的内核上操作,并且需要深入了解计算机体系结构和并发计算,因此你很容易“搬起石头砸自己的脚”。
如果你在 pre_handler 中更改了指令指针(并设置了其他相关寄存器),你必须返回非零值(!0),以便 kprobes 停止单步执行并直接返回到给定的地址。这也意味着不再调用 post_handler。
请注意,在某些使用 TOC(目录表,Table of Contents)进行函数调用的架构上,此操作可能更难,因为你必须在模块中为你的函数设置一个新的 TOC,并在从它返回后恢复旧的 TOC。
返回探测¶
返回探测是如何工作的?¶
当你调用 register_kretprobe() 时,Kprobes 在函数入口处建立一个 kprobe。当被探测的函数被调用并且命中此探测时,Kprobes 会保存返回地址的副本,并用“中继代码(trampoline)”的地址替换返回地址。中继代码是一段任意的代码——通常只是一个 nop 指令。在启动时,Kprobes 在中继代码处注册了一个 kprobe。
当被探测的函数执行其返回指令时,控制权传递给中继代码并命中该探测。Kprobes 的中继处理程序调用与 kretprobe 关联的用户指定的返回处理程序,然后将保存的指令指针设置为保存的返回地址,执行就在从陷阱返回时从那里恢复。
在被探测函数执行期间,其返回地址存储在类型为 kretprobe_instance 的对象中。在调用 register_kretprobe() 之前,用户设置 kretprobe struct 的 maxactive 字段,以指定可以同时探测指定函数的多少个实例。register_kretprobe() 会预分配指定数量的 kretprobe_instance 对象。
例如,如果该函数是非递归的,并且在持有自旋锁的情况下被调用,maxactive = 1 就足够了。如果该函数是非递归的且绝不会放弃 CPU(例如通过信号量或抢占),NR_CPUS 应该就足够了。如果 maxactive <= 0,它将被设置为默认值:max(10, 2*NR_CPUS)。
如果将 maxactive 设置得太低也不是什么灾难;你只会错过一些探测。在 kretprobe 结构体中,nmissed 字段在注册返回探测时被设为零,并且每当进入被探测函数但没有可用于建立返回探测的 kretprobe_instance 对象时,该字段就会递增。
Kretprobe 入口处理程序¶
Kretprobes 还提供了一个可选的用户指定处理程序,它在函数入口处运行。该处理程序通过设置 kretprobe 结构体的 entry_handler 字段来指定。每当命中 kretprobe 在函数入口处放置的 kprobe 时,就会调用用户定义的 entry_handler(如果有的话)。如果 entry_handler 返回 0(成功),则保证在函数返回时调用相应的返回处理程序。如果 entry_handler 返回非零错误,则 Kprobes 保持返回地址不变,并且 kretprobe 对该特定函数实例不再产生其他影响。
多个入口和返回处理程序的调用是通过与它们关联的唯一的 kretprobe_instance 对象进行匹配的。此外,用户还可以指定每个返回实例的私有数据,使其成为每个 kretprobe_instance 对象的一部分。这在相应的用户入口和返回处理程序之间共享私有数据时特别有用。每个私有数据对象的大小可以在 kretprobe 注册时通过设置 kretprobe 结构体的 data_size 字段来指定。可以通过每个 kretprobe_instance 对象的 data 字段访问此数据。
如果进入了被探测函数但没有可用的 kretprobe_instance 对象,那么除了增加 nmissed 计数外,还会跳过用户 entry_handler 的调用。
跳转优化是如何工作的?¶
如果你的内核构建时启用了 CONFIG_OPTPROBES=y(目前该标志在 x86/x86-64 的非抢占式内核上会自动设置为 ‘y’),并且内核参数 “debug.kprobes_optimization” 设置为 1(参见 sysctl(8)),Kprobes 就会尝试通过在每个探测点使用跳转指令而不是断点指令来减少探测命中开销。
初始化 Kprobe¶
当注册探测时,在尝试此优化之前,Kprobes 会在指定地址插入一个普通的、基于断点的 kprobe。因此,即使无法优化此特定探测点,那里也会有一个探测。
安全检查¶
在优化探测之前,Kprobes 会执行以下安全检查
Kprobes 验证将被跳转指令替换的区域(“优化区域”)完全位于一个函数之内。(跳转指令占多个字节,因此可能会覆盖多条指令。)
Kprobes 分析整个函数并验证没有跳转到优化区域内部的跳转。具体来说
该函数不包含间接跳转;
该函数不包含会导致异常的指令(因为异常触发的修复代码可能会跳回优化区域内——Kprobes 会检查异常表以对此进行验证);
没有指向优化区域的近距离跳转(除了跳转到第一个字节)。
对于优化区域中的每条指令,Kprobes 验证该指令可以离线执行。
准备绕过缓冲区¶
接下来,Kprobes 准备一个“绕过(detour)”缓冲区,其中包含以下指令序列
用于压入 CPU 寄存器的代码(模拟断点陷阱)
对调用用户探测处理程序的中继代码的调用。
恢复寄存器的代码
来自优化区域的指令
跳回原始执行路径的跳转。
预优化¶
准备好绕过缓冲区后,Kprobes 验证以下情况均不存在
该探测具有 post_handler。
优化区域中的其他指令被探测了。
该探测被禁用了。
在上述任何情况下,Kprobes 都不会开始优化该探测。由于这些是临时情况,如果情况发生变化,Kprobes 会尝试再次开始优化它。
如果该 kprobe 可以被优化,Kprobes 会将该 kprobe 加入优化队列,并触发 kprobe-optimizer 工作队列来优化它。如果在被优化之前命中了待优化的探测点,Kprobes 会通过将 CPU 的指令指针设置为绕过缓冲区中复制的代码,将控制权返回给原始指令路径——从而至少避免了单步执行。
优化¶
Kprobe 优化器不会立即插入跳转指令;相反,为了安全起见,它首先调用 synchronize_rcu(),因为 CPU 可能会在执行优化区域的中途被中断 [3]。如你所知,synchronize_rcu() 可以确保在调用 synchronize_rcu() 时处于活动状态的所有中断都已完成,但前提是 CONFIG_PREEMPT=n。因此,此版本的 kprobe 优化仅支持 CONFIG_PREEMPT=n 的内核 [4]。
此后,Kprobe 优化器调用 stop_machine(),使用 text_poke_smp() 将优化区域替换为指向绕过缓冲区的跳转指令。
取消优化¶
当优化的 kprobe 被注销、禁用或被另一个 kprobe 阻止时,它将被取消优化。如果在优化完成之前发生这种情况,kprobe 只是从优化列表中移出。如果优化已经完成,则使用 text_poke_smp() 将跳转替换为原始代码(第一个字节中的 int3 断点除外)。
请想象一下,第二条指令被中断,然后在中断处理程序运行的同时,优化器将第二条指令替换为了跳转 地址。当中断返回到原始地址时,没有有效的指令,这会导致意外的结果。
这种优化安全性检查未来可能会被 ksplice 用于支持 CONFIG_PREEMPT=y 内核的 stop-machine 方法所取代。
极客注意:跳转优化改变了 kprobe 的 pre_handler 行为。在没有优化的情况下,pre_handler 可以通过更改 regs->ip 并返回 1 来改变内核的执行路径。但是,当探测被优化时,该修改会被忽略。因此,如果你想调整内核的执行路径,你需要使用以下技术之一来抑制优化
为 kprobe 的 post_handler 指定一个空函数。
或者
执行 ‘sysctl -w debug.kprobes_optimization=n’
黑名单¶
Kprobes 可以探测除了自身以外的大多数内核。这意味着有些函数是 kprobes 无法探测的。探测(捕获)此类函数可能会导致递归陷阱(例如双重错误)或者嵌套的探测处理程序可能永远不会被调用。Kprobes 将此类函数作为黑名单进行管理。如果你想将一个函数添加到黑名单中,你只需要 (1) 包含 linux/kprobes.h 并且 (2) 使用 NOKPROBE_SYMBOL() 宏来指定一个黑名单函数。Kprobes 会根据黑名单检查给定的探测地址,如果给定的地址在黑名单中,则拒绝注册。
支持的架构¶
Kprobes 和返回探测已在以下架构上实现
i386(支持跳转优化)
x86_64 (AMD-64, EM64T)(支持跳转优化)
ppc64
sparc64(返回探测尚未实现。)
arm
ppc
mips
s390
parisc
loongarch
riscv
配置 Kprobes¶
使用 make menuconfig/xconfig/oldconfig 配置内核时,确保将 CONFIG_KPROBES 设置为 “y”,在“通用架构相关选项”下查找“Kprobes”。
为了能够加载和卸载基于 Kprobes 的插桩模块,请确保将“可加载模块支持” (CONFIG_MODULES) 和“模块卸载” (CONFIG_MODULE_UNLOAD) 设置为 “y”。
还要确保将 CONFIG_KALLSYMS 甚至可能将 CONFIG_KALLSYMS_ALL 设置为 “y”,因为内核内的 kprobe 地址解析代码使用了 kallsyms_lookup_name()。
如果你需要在函数中间插入一个探测,你可能会发现“使用调试信息编译内核” (CONFIG_DEBUG_INFO) 很有用,这样你就可以使用 “objdump -d -l vmlinux” 来查看源码到目标代码的映射。
API 参考¶
Kprobes API 为每种类型的探测包含一个“注册”函数和一个“注销”函数。该 API 还包含用于(取消)注册探测数组的 “register_*probes” 和 “unregister_*probes” 函数。以下是这些函数以及你要编写的相关探测处理程序的简短手册页风格规范。有关示例,请参见 samples/kprobes/ 子目录中的文件。
register_kprobe¶
#include <linux/kprobes.h>
int register_kprobe(struct kprobe *kp);
在地址 kp->addr 处设置断点。当命中该断点时,Kprobes 调用 kp->pre_handler。在对被探测指令进行单步执行后,Kprobe 调用 kp->post_handler。任何或所有处理程序都可以为 NULL。如果 kp->flags 设置了 KPROBE_FLAG_DISABLED,则该 kp 将被注册但处于禁用状态,因此在调用 enable_kprobe(kp) 之前,其处理程序不会被命中。
注意
随着在
struct kprobe中引入 “symbol_name” 字段,探测点地址解析现在将由内核负责。以下代码现在可以正常工作kp.symbol_name = "symbol_name";
(64 位 powerpc 的复杂性(例如函数描述符)会被透明地处理)
如果已知要安装探测点的符号偏移量,请使用
struct kprobe的 “offset” 字段。该字段用于计算探测点。指定 kprobe 的 “symbol_name” 或 “addr” 之一。如果两者都指定了,kprobe 注册将失败并返回 -EINVAL。
对于 CISC 架构(例如 i386 和 x86_64),kprobes 代码不会验证 kprobe.addr 是否位于指令边界上。请谨慎使用 “offset”。
register_kprobe() 成功时返回 0,否则返回负的 errno。
用户的 pre-handler (kp->pre_handler)
#include <linux/kprobes.h>
#include <linux/ptrace.h>
int pre_handler(struct kprobe *p, struct pt_regs *regs);
调用时,p 指向与断点关联的 kprobe,regs 指向包含命中断点时保存的寄存器的 struct。此处返回 0,除非你是个 Kprobes 极客。
用户的 post-handler (kp->post_handler)
#include <linux/kprobes.h>
#include <linux/ptrace.h>
void post_handler(struct kprobe *p, struct pt_regs *regs,
unsigned long flags);
p 和 regs 如 pre_handler 中所述。flags 似乎总是为零。
register_kretprobe¶
#include <linux/kprobes.h>
int register_kretprobe(struct kretprobe *rp);
为地址为 rp->kp.addr 的函数建立返回探测。当该函数返回时,Kprobes 调用 rp->handler。在调用 register_kretprobe() 之前,你必须适当地设置 rp->maxactive;有关详细信息,请参阅“返回探测是如何工作的?”。
register_kretprobe() 成功时返回 0,否则返回负的 errno。
用户的返回探测处理程序 (rp->handler)
#include <linux/kprobes.h>
#include <linux/ptrace.h>
int kretprobe_handler(struct kretprobe_instance *ri,
struct pt_regs *regs);
regs 如 kprobe.pre_handler 中所述。ri 指向 kretprobe_instance 对象,其中以下字段可能会引起关注
ret_addr:返回地址
rp:指向对应的 kretprobe 对象
task:指向对应的任务结构体
- data:指向每个返回实例的私有数据;详见“Kretprobe
入口处理程序”了解详情。
regs_return_value(regs) 宏提供了一个简单的抽象,用于根据体系结构 ABI 定义的从适当寄存器中提取返回值。
处理程序的返回值目前被忽略。
unregister_*probe¶
#include <linux/kprobes.h>
void unregister_kprobe(struct kprobe *kp);
void unregister_kretprobe(struct kretprobe *rp);
移除指定的探测。注销函数可以在探测注册后的任何时间调用。
注意
如果函数发现不正确的探测(例如未注册的探测),它们会清除该探测的 addr 字段。
register_*probes¶
#include <linux/kprobes.h>
int register_kprobes(struct kprobe **kps, int num);
int register_kretprobes(struct kretprobe **rps, int num);
注册指定数组中的每一个 num 探测。如果在注册过程中发生任何错误,在 register_*probes 函数返回之前,数组中直到出错探测之前的所有探测都会被安全地注销。
kps/rps:指向
*probe数据结构的指针数组num:数组条目的数量。
注意
在使用这些函数之前,你必须分配(或定义)一个指针数组并设置所有的数组条目。
unregister_*probes¶
#include <linux/kprobes.h>
void unregister_kprobes(struct kprobe **kps, int num);
void unregister_kretprobes(struct kretprobe **rps, int num);
一次性移除指定数组中的每个 num 探测。
注意
如果函数在指定数组中发现一些不正确的探测(例如未注册的探测),它们会清除这些不正确探测的 addr 字段。但是,数组中的其他探测会被正确注销。
disable_*probe¶
#include <linux/kprobes.h>
int disable_kprobe(struct kprobe *kp);
int disable_kretprobe(struct kretprobe *rp);
临时禁用指定的 *probe。你可以使用 enable_*probe() 再次启用它。你必须指定已经注册的探测。
enable_*probe¶
#include <linux/kprobes.h>
int enable_kprobe(struct kprobe *kp);
int enable_kretprobe(struct kretprobe *rp);
启用已被 disable_*probe() 禁用的 *probe。你必须指定已经注册的探测。
Kprobes 特性与限制¶
Kprobes 允许在同一地址设置多个探测。此外,具有 post_handler 的探测点无法被优化。因此,如果你在优化的探测点安装带有 post_handler 的 kprobe,该探测点将自动被取消优化。
通常,你可以在内核中的任何地方安装探测。特别是,你可以探测中断处理程序。本节讨论了已知的例外情况。
如果你试图在实现 Kprobes 的代码中安装探测(主要是 kernel/kprobes.c 和 arch/*/kernel/kprobes.c,但也包括诸如 do_page_fault 和 notifier_call_chain 之类的函数),register_*probe 函数将返回 -EINVAL。
如果你你在可内联函数中安装探测,Kprobes 不会试图追溯该函数的所有内联实例并在那里安装探测。gcc 可能会在未被要求的情况下内联函数,因此如果你没有看到预期的探测命中,请记住这一点。
探测处理程序可以修改被探测函数的环境——例如,通过修改内核数据结构,或者通过修改 pt_regs 结构体的内容(在从断点返回时,这些内容会被恢复到寄存器中)。因此,Kprobes 可用于例如安装漏洞修复程序或注入用于测试的故障。当然,Kprobes 无法区分故意注入的故障和意外发生的故障。切勿酒后探测。
Kprobes 不会试图阻止探测处理程序互相冲突——例如,探测 printk() 然后从探测处理程序中调用 printk()。如果探测处理程序命中了另一个探测,第二个探测的处理程序在该实例中将不会运行,并且第二个探测的 kprobe.nmissed 成员将递增。
从 Linux v2.6.15-rc1 开始,多个处理程序(或同一处理程序的多个实例)可以在不同的 CPU 上并发运行。
除了在注册和注销期间外,Kprobes 不使用互斥锁或分配内存。
探测处理程序运行时带有禁用抢占或禁用中断,这取决于体系结构和优化状态。(例如,在 x86/x86-64 上,kretprobe 处理程序和优化的 kprobe 处理程序运行时不禁用中断)。无论如何,你的处理程序都不应该放弃 CPU(例如,通过尝试获取信号量或等待 I/O)。
由于返回探测是通过用中继代码的地址替换返回地址来实现的,因此对于被 kretprobe 探测的函数,栈回溯和对 __builtin_return_address() 的调用通常会产生中继代码的地址,而不是真正的返回地址。(据我们所知,__builtin_return_address() 仅用于插桩和错误报告。)
如果函数被调用的次数与它返回的次数不匹配,在该函数上注册返回探测可能会产生不希望的结果。在这种情况下,会打印出这样一行:kretprobe BUG!: Processing kretprobe d000000000041aa8 @ c00000000004f48c。有了这些信息,人们就能将导致问题的 kretprobe 的确切实例关联起来。我们已经处理了 do_exit() 的情况。do_execve() 和 do_fork() 不是问题。我们不知道还有哪些其他特定情况可能会导致此问题。
如果在进入或退出函数时,CPU 运行在当前任务栈以外的其他栈上,在该函数上注册返回探测可能会产生不希望的结果。因此,Kprobes 不支持在 x86_64 版本的 __switch_to() 上使用返回探测(或 kprobes);注册函数会返回 -EINVAL。
在 x86/x86-64 上,由于 Kprobes 的跳转优化广泛地修改了指令,因此优化有一些限制。为了解释这一点,我们引入一些术语。想象一个由两个 2 字节指令和一个 3 字节指令组成的 3 指令序列。
IA
|
[-2][-1][0][1][2][3][4][5][6][7]
[ins1][ins2][ ins3 ]
[<- DCR ->]
[<- JTPR ->]
ins1: 1st Instruction
ins2: 2nd Instruction
ins3: 3rd Instruction
IA: Insertion Address
JTPR: Jump Target Prohibition Region
DCR: Detoured Code Region
DCR 中的指令被复制到 kprobe 的离线缓冲区中,因为 DCR 中的字节被一个 5 字节的跳转指令替换了。所以有几个限制。
DCR 中的指令必须可重定位。
DCR 中的指令不能包含 call 指令。
JTPR 不能成为任何跳转或 call 指令的目标。
DCR 不能跨越函数之间的边界。
无论如何,这些限制由内核内的指令解码器进行检查,因此你无需担心。
探测开销¶
在 2005 年使用的一台典型 CPU 上,处理一次 kprobe 命中需要 0.5 到 1.0 微秒。具体来说,一个重复命中同一探测点并每次触发一个简单处理程序的基准测试报告称,每秒有 100-200 万次命中,具体取决于架构。返回探测命中通常比 kprobe 命中多花费 50-75% 的时间。当你在一个函数上设置了返回探测时,在该函数入口处添加一个 kprobe 基本上不会增加任何开销。
以下是不同架构的开销示例数据(单位:微秒)
k = kprobe; r = return probe; kr = kprobe + return probe
on same function
i386: Intel Pentium M, 1495 MHz, 2957.31 bogomips
k = 0.57 usec; r = 0.92; kr = 0.99
x86_64: AMD Opteron 246, 1994 MHz, 3971.48 bogomips
k = 0.49 usec; r = 0.80; kr = 0.82
ppc64: POWER5 (gr), 1656 MHz (SMT disabled, 1 virtual CPU per physical CPU)
k = 0.77 usec; r = 1.26; kr = 1.45
优化探测开销¶
通常,处理一次优化的 kprobe 命中需要 0.07 到 0.1 微秒。以下是 x86 架构的开销示例数据(单位:微秒)
k = unoptimized kprobe, b = boosted (single-step skipped), o = optimized kprobe,
r = unoptimized kretprobe, rb = boosted kretprobe, ro = optimized kretprobe.
i386: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
k = 0.80 usec; b = 0.33; o = 0.05; r = 1.10; rb = 0.61; ro = 0.33
x86-64: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
k = 0.99 usec; b = 0.43; o = 0.06; r = 1.24; rb = 0.68; ro = 0.30
待办¶
SystemTap (http://sourceware.org/systemtap):为基于探测的插桩提供了一个简化的编程接口。快来试试吧。
sparc64 的内核返回探测。
对其他架构的支持。
用户空间探测。
监视点探测(在数据引用时触发)。
Kprobes 示例¶
参见 samples/kprobes/kprobe_example.c
Kretprobes 示例¶
参见 samples/kprobes/kretprobe_example.c
废弃的特性¶
Jprobes 现在是一个废弃的特性。依赖它的用户应该迁移到其他跟踪特性或使用较旧的内核。请考虑将你的工具迁移到以下选项之一
使用 trace-event 跟踪带参数的目标函数。
trace-event 是一个低开销(关闭时几乎没有可见开销)的静态定义事件接口。你可以定义新事件并通过 ftrace 或任何其他跟踪工具对其进行跟踪。
请参阅以下网址
将 ftrace 动态事件(kprobe 事件)与 perf-probe 结合使用。
如果你使用调试信息构建内核 (CONFIG_DEBUG_INFO=y),你可以通过使用 perf-probe 找出哪个寄存器/栈被分配给了哪个局部变量或参数,并设置新事件来对其进行跟踪。
参见以下文档
tools/perf/Documentation/perf-probe.txt
kprobes debugfs 接口¶
在较新的内核(> 2.6.20)中,已注册的 kprobes 列表可以在 /sys/kernel/debug/kprobes/ 目录下查看(假设 debugfs 挂载在 /sys/kernel/debug)。
/sys/kernel/debug/kprobes/list:列出系统上所有已注册的探测
c015d71a k vfs_read+0x0
c03dedc5 r tcp_v4_rcv+0x0
第一列提供插入探测的内核地址。第二列标识探测类型(k - kprobe,r - kretprobe),而第三列指定探测的符号+偏移量。如果被探测的函数属于一个模块,还会指定模块名称。接下来的列显示探测状态。如果探测位于不再有效的虚拟地址上(模块初始化段、对应于已卸载模块的模块虚拟地址),则此类探测标记为 [GONE]。如果探测被临时禁用,则标记为 [DISABLED]。如果探测被优化,则标记为 [OPTIMIZED]。如果探测基于 ftrace,则标记为 [FTRACE]。
/sys/kernel/debug/kprobes/enabled:强制开启/关闭 kprobes。
提供了一个开关,用于全局且强制地开启或关闭已注册的 kprobes。默认情况下,所有 kprobes 都是启用的。通过向该文件回显“0”,所有已注册的探测都将被解除武装,直到向该文件回显“1”。请注意,此开关只是解除武装和武装所有 kprobes,并不会改变每个探测自身的禁用状态。这意味着如果你通过此开关开启所有 kprobes,原本禁用的 kprobes(标记为 [DISABLED])也不会被启用。
kprobes sysctl 接口¶
/proc/sys/debug/kprobes-optimization:开启/关闭 kprobes 优化。
当 CONFIG_OPTPROBES=y 时,此 sysctl 接口出现,它提供了一个开关,用于全局且强制地开启或关闭跳转优化(参见 跳转优化是如何工作的? 一节)。默认情况下,允许跳转优化(开启)。如果你向该文件回显“0”或者通过 sysctl 将 “debug.kprobes_optimization” 设置为 0,则所有优化的探测都将被取消优化,并且在此之后注册的任何新探测都不会被优化。
请注意,此开关会 改变 优化状态。这意味着优化的探测(标记为 [OPTIMIZED])将被取消优化([OPTIMIZED] 标签将被移除)。如果开启此开关,它们将再次被优化。
参考资料¶
有关 Kprobes 的其他信息,请参考以下网址