内核探针 (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 的检测以内核模块的形式打包。模块的 init 函数安装(“注册”)一个或多个探针,而 exit 函数取消注册它们。诸如 register_kprobe() 之类的注册函数指定要插入探针的位置以及命中探针时要调用的处理程序。

还有用于批量注册/注销一组 *probesregister_/unregister_*probes() 函数。当您必须一次注销大量探针时,这些函数可以加快注销过程。

接下来的四个小节解释了不同类型的探针如何工作以及跳转优化如何工作。它们解释了一些您需要了解的内容,以便最好地利用 Kprobes,例如 pre_handler 和 post_handler 之间的区别,以及如何使用 kretprobe 的 maxactive 和 nmissed 字段。但是,如果您急于开始使用 Kprobes,则可以跳到 支持的架构

Kprobe 如何工作?

当注册 kprobe 时,Kprobes 会复制被探测的指令,并将被探测指令的第一个字节替换为断点指令(例如,i386 和 x86_64 上的 int3)。

当 CPU 命中断点指令时,会发生陷阱,保存 CPU 的寄存器,并且控制权通过 notifier_call_chain 机制传递给 Kprobes。Kprobes 执行与 kprobe 关联的“pre_handler”,并将 kprobe 结构和保存的寄存器的地址传递给处理程序。

接下来,Kprobes 单步执行其被探测指令的副本。(单步执行实际指令会更简单,但是 Kprobes 必须临时删除断点指令。这将打开一个很小的时间窗口,在此窗口中,另一个 CPU 可以直接越过探针点。)

单步执行指令后,Kprobes 执行与 kprobe 关联的“post_handler”(如果有)。然后,继续执行探针点之后的指令。

更改执行路径

由于 kprobe 可以探测正在运行的内核代码,因此它可以更改寄存器集,包括指令指针。此操作需要最大程度的注意,例如保留堆栈帧,恢复执行路径等。由于它在正在运行的内核上运行并且需要深入了解计算机体系结构和并发计算,因此您很容易自找麻烦。

如果在 pre_handler 中更改指令指针(并设置其他相关寄存器),则必须返回 !0,以便 kprobes 停止单步执行并仅返回到给定地址。这也意味着不再应调用 post_handler。

请注意,此操作在某些使用 TOC(目录)进行函数调用的体系结构上可能会更加困难,因为您必须在模块中为函数设置新的 TOC,并在从函数返回后恢复旧的 TOC。

返回探针

返回探针如何工作?

当您调用 register_kretprobe() 时,Kprobes 会在函数入口处建立一个 kprobe。当调用被探测的函数并命中此探针时,Kprobes 会保存返回地址的副本,并将返回地址替换为“trampoline”的地址。trampoline 是一段任意的代码,通常只是一个 nop 指令。在启动时,Kprobes 会在 trampoline 处注册一个 kprobe。

当被探测的函数执行其返回指令时,控制权将传递到 trampoline,并且命中该探针。Kprobes 的 trampoline 处理程序调用与 kretprobe 关联的用户指定返回处理程序,然后将保存的指令指针设置为保存的返回地址,这就是从陷阱返回后执行恢复的位置。

在被探测的函数执行期间,其返回地址存储在 kretprobe_instance 类型的对象中。在调用 register_kretprobe() 之前,用户设置 kretprobe 结构的 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 准备一个“绕行”缓冲区,其中包含以下指令序列

  • 用于推送 CPU 寄存器的代码(模拟断点陷阱)

  • 调用 trampoline 代码的代码,该代码调用用户的 probe 处理程序。

  • 用于恢复寄存器的代码

  • 来自优化区域的指令

  • 跳回到原始执行路径的跳转。

预优化

准备好绕行缓冲区后,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 断点除外)。

给极客的注意:跳转优化会更改 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) 之前,不会命中其处理程序。

注意

  1. 随着“symbol_name”字段引入到 struct kprobe 中,现在内核将负责处理探测点地址解析。以下现在可以使用

    kp.symbol_name = "symbol_name";
    

    (64 位 powerpc 复杂性(如函数描述符)以透明方式处理)

  2. 如果已知安装探测点的符号偏移量,请使用 struct kprobe 的“offset”字段。此字段用于计算探测点。

  3. 指定 kprobe 的“symbol_name”或“addr”。如果同时指定两者,则 kprobe 注册将失败,并返回 -EINVAL。

  4. 对于 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);

使用指向与断点关联的 kprobe 的 p 以及指向命中断点时保存的寄存器的 struct 的 regs 来调用。除非您是 Kprobes 极客,否则此处返回 0。

用户的 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:指向相应的任务 struct

  • data:指向每个返回实例的私有数据;有关详细信息,请参阅“Kretprobe

    entry-handler”。

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 不使用互斥锁或分配内存。

探针处理程序在禁用抢占或禁用中断的情况下运行,具体取决于体系结构和优化状态。(例如,kretprobe 处理程序和优化的 kprobe 处理程序在 x86/x86-64 上运行时不会禁用中断)。无论如何,您的处理程序不应放弃 CPU(例如,通过尝试获取信号量或等待 I/O)。

由于返回探针是通过将返回地址替换为跳转地址来实现的,因此堆栈回溯和对 __builtin_return_address() 的调用通常会产生跳转地址,而不是 kretprobed 函数的实际返回地址。(据我们所知,__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 字节的跳转指令替换。所以存在几个限制。

  1. DCR 中的指令必须是可重定位的。

  2. DCR 中的指令不得包含调用指令。

  3. JTPR 不得成为任何跳转或调用指令的目标。

  4. 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

待办事项

  1. SystemTap (http://sourceware.org/systemtap): 为基于探针的检测提供了一个简化的编程接口。试一试。

  2. sparc64 的内核返回探针。

  3. 支持其他架构。

  4. 用户空间探针。

  5. 观察点探针(在数据引用时触发)。

Kprobes 示例

请参阅 samples/kprobes/kprobe_example.c

Kretprobes 示例

请参阅 samples/kprobes/kretprobe_example.c

已弃用功能

Jprobes 现在是一个已弃用的功能。依赖它的人应该迁移到其他跟踪功能或使用旧内核。请考虑将您的工具迁移到以下选项之一

  • 使用 trace-event 跟踪带有参数的目标函数。

    trace-event 是一个低开销(如果关闭则几乎没有可见开销)的静态定义事件接口。您可以定义新事件,并通过 ftrace 或任何其他跟踪工具跟踪它。

    请参阅以下网址

  • 使用带有 perf-probe 的 ftrace 动态事件(kprobe 事件)。

    如果您使用调试信息 (CONFIG_DEBUG_INFO=y) 构建内核,您可以使用 perf-probe 查找哪个寄存器/堆栈分配给哪个局部变量或参数,并设置新事件来跟踪它。

    请参阅以下文档

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 的更多信息,请参阅以下网址