译者序
这篇文章翻译自 Linux 内核源码树中的 kprobes.txt 文件,此文件描述了 Kprobes 的概念、工作原理、限制等内容。因为文件的最后一次提交是在 2019 年,所以文章标题中的年份也就是指这个意思的。
下面的表格是对关键词的一点解释,概念其实就这几个。
关键词 | 解释 |
---|---|
Kprobes | 指的是内核的探测机制、框架,依赖于硬件的特定功能实现的,比如 int3 指令 |
kprobe | 指的是 Kprobes 的对象或结构体,关联探测点和探针 |
probepoint | 探测点,一个可用于观察、监视目的的具体位置,比如:一个函数的入口、返回地址 |
probe | 探针,在探测点做具体事情的对象,比如:分析、追踪 |
如果用一句话解释 Kprobes 原理就是:Kprobes 在探测点上注册了一些探针,当 CPU 执行到探测点的时候 Kprobes 会调用所有相关探针的回调函数。CPU 是怎么从执行流转到 Kprobes 的呢?
注:因为水平有限,文中难免存在遗漏或者错误的地方。如有疑问,建议直接阅读原文。
概念: Kprobes 和 Return Probes
Kprobes 能够让你动态的介入内核的任意函数,且无中断的收集调试和性能信息。基本上,你可以捕获内核的任意地址1,指定一个在断点命中候调用回调函数。
目前,探针有两种类型:kprobes ,kretprobes(也被称为 return
探针)。基本上, kprobe 可以安插到任意指令上。当一个指定的函数返回时触发 return
探针。
通常,基于 Kprobes 的探测工具被打包成了一个内核模块。模块的初始化函数会安装(注册)一个或多个探针,而模块的卸载函数会注销它们。 register_kprobe()
注册函数指明探针要插入到什么位置,探针命中的时候要调用什么样的函数。
也有一些用来批量注销或注册一组探针的 register_/unregister_*probes()
函数。在必须一次性注销大量探针的时候,这些函数可以加快注销过程。
接下来的四个小节会解释不同类型的探针及跳转优化是如何工作的。这些内容讲了一些必须要知道的事项,以便充分利用 Kprobes ,例如: pre_handler
与 post_handler
之间的区别,以及如何使用 kretprobes 的 maxactive
、 nmissed
字段。不过,假如你想马上试试 Kprobes 的话,可以直接跳至[##支持的架构]章节。
Kprobe 如何工作?
Kprobes 在注册了一个 kprobe 后,复制被探测的指令,并且把被探测指令的第一个字节替换为断点指令(例如:在 i386、x86_64 平台上的 int3
)。
在 CPU 命中断点指令时,会发生一个 trap
,CPU 的寄存器会被保存,而控制通过 notifier_call_chain 机制转移到 Kprobes 。Kprobes 执行与 kprobe 关联的 pre_handler
,并且向它传递 kprobe 结构体和保存的寄存器地址。
接着,Kprobes 单步执行被探测指令的副本。(虽然单步执行原始指令会更简单,但过后 Kprobes 还必须移除断点指令。当另一个 CPU 执行过探测点的时候,将会打开一个小的时间窗口。)
在指令单步执行完之后,Kprobes 会执行与 kprobe 关联的 post_handler
,如果有的话。然后,继续执行探测点之后的指令。
改变执行路径
kprobes 能够探测一段正在运行的内核代码,因此它能改变寄存器,包括指令指针。类似保存栈帧、恢复执行路径,这类操作需要非常地小心,因为 kprobes 作用在正在运行的内核上面,需要深入的了解计算结构体系和并行计算才行。
假如你在 pre_handler
回调函数内改变指令指针(以及设置其它相关的寄存器),那你必须返回非零值,好让 kprobes 停止单步执行并立即返回到指定地址。这也表示 post_handler
不应该再被调用。
注意,在那些使用 TOC (目录)进行函数调用的架构上,这种操作可能会更困难,因为你必须在你的模块中为你的函数设置新的 TOC,并且在函数返回之后还要恢复先前的 TOC。
Return 探针
Return 探针如何工作的?
在你调用 register_kretprobe()
函数的时候, Kprobes 会在函数的入口处建立一个 kprobe。在调用被探测函数的时候命中这个探针, Kprobes 会保存 return 地址的一个副本,且用一个 “trampoline”(蹦床)的地址替换 return 地址。trampoline 是一段任意的代码 — 通常只是 nop 指令。在启动的时候, Kprobes 在蹦床注册了一个 kprobe。
在被探测的函数执行 return 指令时,控制转移到蹦床命中探针。 Kprobes 的蹦床处理函数调用与 kretprobe 关联的用户指定的回调函数,然后把保存的指令指针设置为保存的 return 地址,一旦从 trap 返回,执行会在这里恢复。
在被探测的函数执行期间,它的返回地址被保存在一个 kretprobe_instance
类型的对象中。在调用 register_kretprobe()
函数前,用户设置 kretprobe 结构体的 maxactive
字段表示可同时探测指定函数的实例数量。 register_kretprobe()
函数会预先分配规定数量的 kretprobe_instance
对象。
例如,假设该函数是非递归的,且在自旋锁锁住的情况下被调用,那 maxactive
的值为 1 应该足够。如果函数是非递归的,且永远不放弃 CPU (例如:通过信号量或抢占),那 NR_CPUS 应该足够。要是 maxactive
的值小于等于零的话,会设置成默认值。如果开启 CONFIG_PREEMPT 选项,默认值为 max(10, 2*NR_CPUS)
(在两倍的 CPU 数量与 10 之间取较大的值)。其他情况,默认值为 NR_CPUS(CPU 数量)。
如果你把 maxactive
设置的很小的话,也不会有什么问题,只是会漏掉一些探针而已。在 kretprobe 结构体中,return 探针注册后 nmissed
字段会设置为 0,之后在每次进入被探测函数且没有可用的 kretprobe_instance
对象关联 return 探测的时候累加。
Kretprobe 入口回调函数
Kretprobes 还提供一个可选的用户回调函数,它运行于函数入口。这个回调函数通过 kretprobe 结构体 entry_handler
字段指定。每当命中放置在函数入口处的 kprobe 时,就会调用用户自定义的 entry_handler
函数。如果 entry_handler
函数返回零(成功),那么对应的 return 回调函数保证会在函数返回的时候被调用。如果 entry_handler
返回一个非零错误, Kprobes 会保留返回地址,而 kretprobe 对特定的函数实例没有影响。
使用与它们关联的唯一对象 kretprobe_instance,可以匹配许多的 entry 和 return 回调函数调用。另外,用户也可以把每个 return-instace 的私有数据指定为每个 kretprobe_instance 对象的一部分。这在 entry 和 return 回调函数之间共享私有数据时尤其有用。每个私有数据对象的大小可以在 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 尝试在每个探测点用 jump 指令代替 breakpoint 指令来减少命中探针的开销。
初始化 Kprobe
在注册了一个探针试图优化之前,Kprobes 会在指定的地址插入一个普通的,基于断点的 kprobe。所以,即便不能优化这个特定的探测点,也会有一个探针在那儿。
安全检查
在进行优化探针之前,Kprobes 会做以下安全检查:
-
Kprobes 校验会被 jump 指令替换的区域(”已优化的区域“)完全处于一个函数内部。(jump 指令是多字节指令,因此可能会覆盖多个指令)
-
Kprobes 分析整个函数,并且确认不会跳入已优化的区域。特别是:
- 该函数不包含间接跳转
- 该函数不包含引起异常的指令(因为被异常触发的固定代码可能会跳回到已优化的区域 — Kprobes 会检查异常表来验证这一点)
- 该函数附近没有跳转到已优化的区域(除了第一个字节)
-
对于已优化区域中的每一个指令,Kprobes 会验证它们能否离线执行。
准备 detour 缓冲区
detour 意思是像交通节点(环岛)那样
接着,Kprobes 准备一个“环形”缓冲区,包含以下指令序列:
- 推进 CPU 寄存器的代码(模拟断点 trap)
- 调用蹦床代码,再间接调用用户的探针回调函数
- 恢复寄存器的代码
- 优化区域的指令
- 跳回原始执行路径的指令
优化前
在准备 detour 缓冲区后, Kprobes 会检查确保不出现以下情况:
- 探针有一个 post_handler 回调函数
- 在优化区域中的其他指令被探测了
- 已禁用的探针
在上述任何一种情况下,Kprobes 都不会优化探针。因为这都是临时情况,如果情况有变化,Kprobes 会再次进行优化。
如果 kprobe 可以被优化,Kprobes 会把 kprobe 列入优化队列中,然后启动工作队列 kprobe-optimizer 去优化它。如果被优化的 probepoint 在优化之前命中, Kprobes 通过把 CPU 的指令指针设置为 detour 缓冲区中被复制的代码,将控制权返回到原始指令路径 — 这样做至少避免了单步执行。
优化
Kprobe-optimizer 并不会立即插入 jump 指令,相反为了安全它会先调用 synchronize_rcu()
函数,因为在处理优化区域的过程中 CPU 可能会被中断 2。如你所知, synchronize_rcu()
函数可以确保所有活跃的中断在调用 synchronize_rcu()
的时候已经完成,但前提是 CONFIG_PREEMPT=n
的时候。所以,kprobe 的优化版本只支持 CONFIG_PREEMPT=n
的内核 3。
之后, Kprobe-optimizer 调用 stop_machine()
函数替换优化区域,用一个跳转到 detour 缓冲区指令,使用 text_poke_smp()
函数。
取消优化
当优化的 kprobe 被其他 kprobe 注销、禁用或阻塞的时候,它将不会被优化。如果这种情况在优化完成之前发生,则只是将 kprobe 从优化队列中移除。如果优化已经完成,会通过调用 text_poke_smp()
函数把 jump 指令替换为原始代码(除了第一个字节中的 int3 断点)。
geeks 注意:
跳转优化改变了 kprobe 的 pre_handler
的行为。优化前,pre_handler
通过改变 regs->ip
的同时返回 1 可以改变内核的执行路径。然而,在 probe 被优化的时候,修改会被忽略。因此,如果你想微调内核的执行路径,需要使用一个方法去抑制优化:
- 给 kprobe 的 post_handler 指定一个空函数
- 执行
sysctl -w debug.kprobes_optimization=n
命令
黑名单
Kprobes 可以探测除自身以外的大部分内核空间。这表示有一些函数 kprobes 无法探测。探测(trapping)这样的函数会导致递归 trap (比如:双重故障)或者嵌套的 probe 回调函数可能永远不会被调用。如果你想添加一个函数到黑名单中,只需要两个步骤:首先,引入 linux/kprobes.h
文件;其次,使用 NOKPROBE_SYMBOL()
宏指定一个要被列入黑名单的函数。 Kprobes 对照黑名单检查传入的 probe 地址,如果传入的地址在黑名单中会拒绝注册。
支持的架构
Kprobes 和 Kretprobes 已在下面的这些结构体系上实现:
- i386 (Supports jump optimization)(支持跳转优化)
- x86_64 (AMD-64, EM64T) (Supports jump optimization)(支持跳转优化)
- ppc64
- ia64 (Does not support probes on instruction slot1.)(在 slot1 指令上不支持 probes)
- sparc64 (Return probes not yet implemented.)(返回 probes 还没实现)
- arm
- ppc
- mips
- s390
- parisc
配置 Kprobes
在使用 make menuconfig/xconfig/oldconfig
配置内核时,确保 CONFIG_KPROBES
设置为 “y”。在 “General setup” 字符后搜索 “Kprobes”。
为了可以加载和卸载基于 Kprobes 的探测模块,请确保将“支持模块加载”(CONFIG_MODULES)和“模块卸载”(CONFIG_MODULE_UNLOAD)设置为 “y”。
还要确保将 CONFIG_KALLSYMS
甚至是 CONFIG_KALLSYMS_ALL
都设置为 “y”,因为 kallsyms_lookup_name()
函数被内核里的 kprobe 地址解析代码使用。
如果你需要在函数中间插入 probe,也许发现“使用 debug info 编译内核” (CONFIG_DEBUG_INFO
) 非常有用,因此可以使用 objdump -d -l vmlinux
命令去查看源码到目标代码的映射关系。
API 参考
Kprobes API 为每种探针类型提供了一个”注册“和“注销”函数。还包括批量注册、注销探针的 register_*probes
和 unregister_*probes
函数。这有些迷你手册以及将会用到的相关的探针回调函数的简短说明。相关例子,可查看在 samples/kprobes/
子目录内的文件。
register_kprobe
|
|
在 kp->addr
地址设置一个断点。命中断点时,Kprobes 调用 kp->pre_handler
。在探测的指令单步执行后,Kprobe 调用 kp->post_handler
。如果一个错误发生,在执行 kp->pre_handler
或 kp->post_handler
期间,又或者是在单步执行被探测指令期间,Kprobes 会调用 kp->fault_handler
。所有回调函数都可以是 NULL
。如果 kp->flags
设置为 KPROBE_FLAG_DISABLED
,kp
将会被注册且处于禁用状态。所以 kp
的回调函数在调用 enable_kprobe(kp)
之前不会被调用。
注意:
-
通过引入
symbol_name
字段来构造 kprobe,探测点地址解析将会由内核来处理。可以使用以下内容:kp.symbol_name = "symbol_name";
(64 位 powepc 错综复杂,例如透明地处理函数描述符)
-
如果在符号中用于安装探测点的偏移量是已知的,请使用 kprobes 结构体的
offset
字段。这个字段用于计算探测点。 -
kprobe 的
symbol_name
或者addr
字段都被指定,kprobe 注册会失败且返回EINVAL
。 -
使用 CISC 架构(如:i386,x86_64),kprobes 代码不会验证,如果
kprobe.addr
在指令边界。谨慎使用offset
。
register_kprobe()
函数成功返回 0,其他情况返回一个负的 errno
。
用户的 pre-handler(kp->pre_handler
)函数原型:
|
|
用指向与断点关联的 kprobe 指针 p
以及命中断点时保存的寄存器指针 regs
调用。
用户的 post-handler (kp->post_handler
)函数原型:
|
|
p
和 regs
同 pre_handler
所述。flags
看起来一直是 0。
用户的 fault-handler (kp->fault_handler
)函数原型:
|
|
p
和 regs
同 pre_handler
所述 。 trapnr
是故障相关的特定架构下的 trap 号(例如:在 i386 上, 13 为普通防护故障,14 为页面故障)。如果成功的处理了异常返回 1。
register_kretprobe
|
|
为 rp->kp.addr
地址的函数建立一个 return 探针。在函数返回时,kprobes 调用 rp->handler
。在调用 register_kretprobe()
之前必须设置合适的 rp->maxactive
,细节参考 “Return Probe 如何工作?” 。
register_kretprobe()
成功返回 0,其他情况返回一个负的 errno
。
用户的 return 探针回调函数(rp->handler
)原型:
|
|
regs
同 kprobe.pre_handler 描述那样。ri
指向 kretprobe_instance
对象,其中可能涉及以下字段:
- ret_addr:返回地址
- rp:指向相关的 kretprobe 对象
- task:指向相关的 task 结构体
- data:指向每个 return-instace 私有数据,细节参考 “kretprobe entry-handler”。
regs_return_value(regs)
宏提供一个简单的抽象方法,从架构的 ABI 定义的合适的寄存器中提取返回值。
目前,回调函数的返回值是被忽略的。
unregister_*probe
|
|
移除探针。注销函数可以在探针被注册后调用。
注意:
如果这些发现一个不正确的探针(不包括未注册的探针),它们会清除探针的 addr
字段。
register_*probes
|
|
注册数组中 num
个探针。如果在注册期间发生错误,在 register_*probes
函数返回之前会安全地注销数组中已注册的探针,直到发生错误的探针为止。
kps/rps
:指向*probe
数据结构的指针数组num
:数组的大小
注意:
必须分配(或定义)指针数组,且在使用这些函数之前设置数组的所有元素。
unregister_*probes
|
|
一次性移除指定数组中 num
个探针。
注意:
如果这些函数在数组中发现一些不正确的探针(比如:未注册的探针),会清除那些不正确探针的 addr
字段。数组中其他的探针会被注销掉。
disable_*probe
|
|
临时地禁用某个探针。调用 enable_*probe()
函数可再次启用。必须是已经注册的探针。
enable_*probe
|
|
Enables *probe
which has been disabled by disable_*probe(). You must specify the probe which has been registered.
通过 disable_*probe()
启用已经被禁用的 *probe
。必须指定已经注册的 probe。
Kprobes 特性与限制
kprobes 允许在同一个地址插入多个探针。此外,带有 post_handler
的探测点无法被优化。所以,如果在已优化的探测点插入带有 post_handler
回调函数的 kprobe 探针,探测点会自动地变成未优化的。
通常,可以在内核的任意位置插入探针。特别的是,它可以探测中断处理函数。本节讨论了已知的异常。
如果试图在实现 Kprobes 的代码中插入一个探针,register_*probe
函数将返回 -EINVAL
。(在 kernel/kprobes.c
和 arch/*/kernel/kprobes.c
文件中,还有像 do_page_fault
和 notifier_call_chain
这类的函数)。
如果在可内联的函数中插入探针,Kprobes 并不会给所有内联实例插入探针。如果没有命中期望的探针,记住一点, gcc 可能会自动内联一个函数。
探针回调函数可以修改被检测函数的环境 – 例如,改变内核数据结构或者 pt_regs
数据结构的内容(从断点返回时恢复到寄存器中)。因此,Kprobes 可用于安装 bug 修复或测试时注入错误。当然, Kprobes 是没有办法把故意地注入的错误与意外的错误区分开。不要喝大了搞事情。
Kprobes 不会阻止探针回调函数之间的相互作用 – 比如,先给 printk()
函数插入探针,接着又从另一个探针回调函数中调用 printk()
函数。如果探针回调函数命中一个探针,那么这第二个探针的回调函数不会执行,将只会累加探针的 kprobe.nmissed
值。
自 Linux v2.6.15-rc1 开始,多个回调函数(或者相同回调函数的多个实例)可以同时在不同的 CPU 上运行。
除了注册和注销探针之外,Kprobes 不会用互斥锁或分配内存。
探针回调函数在禁用抢占或者禁用中断的情况下运行,这取决于架构以及优化状态(例如,kretprobe 和优化的 kprobe 回调函数在 x86/x86-64 上运行时没有禁用中断)。不管如何,你的回调函数都不应该让出 CPU (比如,试图获取信号量或等待 I/O)。
因为 return 探针是通过把蹦床的地址替换为返回地址来实现的,所以堆栈回溯以及调用 __builtin_return_address()
函数得到的是蹦床的地址,而不是 kretprobed 函数实际 return 地址(就目前我们知道的而言,__builtin_return_address()
函数只用于测试工具和报告错误)。
如果一个函数的调用次数不能匹配返回的次数,在那个函数上注册的探针可能产生不想要的结果。这种情况,会输出一行 kretprobe BUG!: Processing kretprobe d000000000041aa8 @ c00000000004f48c
。有了这行信息,就可以关联导致问题的 kretprobe 实例。涵盖了 do_exit()
函数的情况。 do_execve()
和 do_fork()
函数都不是问题。我们不知道的其他特定的情况,可能会出现问题。
如果在进入或者退出某个函数时,CPU 在除当前 task 以外的堆栈上运行,那在这个函数上注册 return 探针可能会产生不想要的结果。因为这个原因,Kprobes 不支持 __switch_to()
函数 x86_64 版本的 return 探针(或 kprobes),注册函数会返回 -EINVAL
。
在 x86/x86-64 架构上,由于 Kprobes 跳转优化修改指令普遍存在,会对优化有一些限制。为解释这一点,我们引入些术语。想象一下,一个由 2 字节指令和 3 字节指令组成的 3 个指令序列。
|
|
|
|
DCR 内的指令被复制到 kprobe 的离线缓冲区中,因为 DCR 内的字节被 5 字节 jump 指令替代了。所以,这儿会有几个限制。
- DCR 内的指令一定是可重定位的
- DCR 内的指令一定不能包含
call
指令 - JTPR 不能作为
jump
或call
指令的目标 - DCR 不能跨越函数之间的边界
不过,这些限制由内核的指令解码器检查,所以不需要关心这些限制。
探针的开销
在 2005 年常见的 CPU 上,处理命中 kprobe 要花费 0.5 - 1.0 微秒。具体一点,基准测试反复命中同一个探测点,每一次触发简单的回调函数,每秒 1-2 百万次命中,具体数值取决于 CPU 架构。通常,命中 return 探针比命中 kprobe 多花费 50-75% 的时间。当你把一个 kretprobe 插入到一个函数的时候,实际是在函数入口处添加一个 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
TODO
1. [SystemTap](http://sourceware.org/systemtap):给基于探针的探测工具提供了一个简单的编程接口。可以试一下
2. sparc64 架构的 kretprobe
3. 支持其他架构
4. 用户空间的探针
5. 观察点探针(在数据引用时触发)
Kprobes 例子
见 samples/kprobes/kprobe_example.c 文件
Kretprobes 例子
见 samples/kprobes/kretprobe_example.c 文件
有关 Kprobes 的其他信息,请参考以下 URL 链接:
- http://www-106.ibm.com/developerworks/library/l-kprobes.html?ca=dgr-lnxw42Kprobe
- http://www.redhat.com/magazine/005mar05/features/kprobes/
- http://www-users.cs.umn.edu/~boutcher/kprobes/
- http://www.linuxsymposium.org/2006/linuxsymposium_procv2.pdf (pages 101-115)
已弃用的机制
现在 Jprobes 是一个不被推荐的机制。依赖它的应该迁移到其他追踪机制或使用旧的内核。请考虑把你的工具迁移到以下工具中:
- 使用 trace-event 追踪带参数的函数
trace-event 是个低开销的静态定义的事件接口(如果关闭,没有明显的开销)。你可以定义新事件,通过 ftrace 或者其他追踪工具追踪它。
参考以下 URL 链接:
- https://lwn.net/Articles/379903/
- https://lwn.net/Articles/381064/
- https://lwn.net/Articles/383362/
- 使用 ftrace 动态事件(kprobe 事件)和 perf-probe
如果你使用调试信息(
CONFIG_DEBUG_INFO=y
)编译你的内核,可以用 perf-probe 设置新事件去追踪它,能发现寄存器/栈被分配给了哪个本地变量或者参数。
参考以下文档:
- Documentation/trace/kprobetrace.rst
- Documentation/trace/events.rst
- 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
:列出在系统上所有已注册的 probes:
|
|
第一列,是已插入探针的内核地址。第二列,是表示探针的类型(k - kprobe,r - kretprobe)。第三列,是指定探针的符号+偏移量(symbol+offset)。如果被探测的函数属于一个模块,那么这个模块的名字也会被列出来。随后的几列显示探针的状态。如果探针在虚拟地址上,并且地址无效(模块初始化部分,模块虚拟地址,对应的模块已经卸载),这类探针会被标记为 [GONE]。如果探针临时被禁用,会被标记为 [DISABLED]。如果探针被优化了,会被标记为 [OPTIMIZED]。如果探针是基于 ftrace 的,会被标记为 [FTRACE]。
/sys/kernel/debug/kprobes/enabled
:强制开启/关闭 kprobes。
提供一个全局按钮,强制的开启或关闭已注册的 kprobes。默认情况下,所有 kprobes 是开启的。输出 “0” 到这个文件,所有已注册的探针会被卸载,输出 “1” 到这个文件,又重新加载。请注意,这个按钮只是卸载和加载所有 kprobes,并不会改变每个探针的禁用状态。意思是,已经禁用的探针(标记为 [DISABLED])是不会被激活的。
kprobes sysctl 接口
/proc/sys/debug/kprobes-optimization
: kprobes 优化开关。
在 CONFIG_OPTPROBES=y
的时候, sysctl
接口提供一个全局按钮,强制的开启或关闭跳转优化(查看跳转章节)。默认情况下,跳转优化是开启的。如果输出 “0” 到这个文件或者通过 sysctl
设置 debug.kprobes_optimization
为 0 ,所有优化的探针将会变成未优化的,而且在这之后任何新的被注册的探针都不会被优化。
注意,这个按钮会改变优化状态。表示已优化的探针(标记为 [OPTIMIZED])将变成未优化的(标记 [OPTIMIZED] 会被移除)。如果按钮被打开,探针将再次被优化。