内核探针 (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() 这样的注册函数指定探针要插入的位置以及在探针被触发时要调用的处理程序。

还有 register_/unregister_*probes() 函数用于批量注册/注销一组 *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”(如果有)。然后,执行继续从探针点之后的指令开始。

更改执行路径

由于 kprobes 可以探测正在运行的内核代码,因此它可以更改寄存器集,包括指令指针。此操作需要非常小心,例如保持堆栈帧,恢复执行路径等。因为它在运行的内核上运行,并且需要深入了解计算机体系结构和并发计算,所以您很容易自食其果。

如果在 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 代码的代码,该代码调用用户的探针处理程序。

  • 用于恢复寄存器的代码

  • 来自优化区域的指令

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

预优化

在准备好绕行缓冲区后,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”,在“General architecture-dependent options”下查找“Kprobes”。

为了能够加载和卸载基于 Kprobes 的检测模块,请确保“Loadable module support”(CONFIG_MODULES)和“Module unloading”(CONFIG_MODULE_UNLOAD)设置为“y”。

还要确保 CONFIG_KALLSYMS 甚至 CONFIG_KALLSYMS_ALL 设置为“y”,因为 kallsyms_lookup_name() 被内核中的 kprobe 地址解析代码使用。

如果您需要在函数的中间插入一个探针,您可能会发现“Compile the kernel with debug info”(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);

调用时,p 指向与断点关联的 kprobe,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:指向相应的 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 的代码中安装探针,register_*probe 函数将返回 -EINVAL(主要是 kernel/kprobes.c 和 arch/*/kernel/kprobes.c,但也包括 do_page_fault 和 notifier_call_chain 等函数)。

如果您在可内联的函数中安装一个探针,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)。

由于返回探针是通过将返回地址替换为 trampoline 的地址来实现的,因此堆栈回溯和对 __builtin_return_address() 的调用通常会生成 trampoline 的地址,而不是 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 中的指令不能包含 call 指令。

  3. JTPR 不能成为任何跳转或 call 指令的目标。

  4. DCR 不能跨越函数边界。

无论如何,这些限制都由内核中的指令解码器检查,所以您无需担心这些问题。

探针开销

在 2005 年使用的典型 CPU 上,处理一个 kprobe 命中需要 0.5 到 1.0 微秒。具体来说,一个重复命中相同探针点的基准测试,每次触发一个简单的处理程序,报告每秒 100-200 万次命中,这取决于架构。return-probe 命中通常比 kprobe 命中花费的时间长 50-75%。当您在一个函数上设置了 return probe 时,在该函数的入口处添加一个 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 或任何其他跟踪工具对其进行跟踪。

    请参见以下网址

  • 将 ftrace 动态事件(kprobe 事件)与 perf-probe 结合使用。

    如果您使用调试信息构建内核(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),而第三列指定探针的 symbol+offset。如果探测的函数属于模块,则还会指定模块名称。后面的列显示探针状态。如果探针位于不再有效的虚拟地址上(模块初始化段,与已卸载的模块相对应的模块虚拟地址),则此类探针标记为 [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 接口,它提供一个旋钮,以全局且强制的方式打开或关闭跳转优化(请参见跳转优化如何工作?)。默认情况下,允许跳转优化(ON)。如果您向此文件回显“0”或通过 sysctl 将“debug.kprobes_optimization”设置为 0,则所有优化的探针将被取消优化,并且之后注册的任何新探针将不会被优化。

请注意,此旋钮更改优化状态。这意味着优化的探针(标记为 [OPTIMIZED])将被取消优化([OPTIMIZED] 标签将被删除)。如果打开该旋钮,它们将再次被优化。

参考资料

有关 Kprobes 的更多信息,请参阅以下 URL