3

根据文档,kprobes 禁用抢占:

探测处理程序在禁用抢占的情况下运行。根据架构和优化状态,处理程序也可以在禁用中断的情况下运行(例如,在 x86/x86-64 上,kretprobe 处理程序和优化的 kprobe 处理程序在没有中断禁用的情况下运行)。

提交 9a09f261a中,我们可以清楚地看到优化的 kprobe 用于在启用抢占的情况下运行。

为什么会这样?我将 kprobes 理解为在内核中的特定地址注入一些代码的一种方式,并且理解任何代码都应该没问题。

  • 是什么让 kprobes 如此特别以至于必须禁用抢占?
  • 在什么情况下可以重新启用抢占?
4

1 回答 1

3

至少在 x86 上,Kprobes 的实现依赖于在 Kprobe 处理程序运行时禁用抢占这一事实。

当您在一条指令上放置一个普通的(不是基于 Ftrace 的)Kprobe 时,该指令的第一个字节将被 0xcc(int3,“软件断点”)覆盖。如果内核尝试执行该指令,则会发生陷阱并被kprobe_int3_handler()调用(参见do_int3()的实现)。

要调用您的 Kprobe 处理程序,kprobe_int3_handler()会找到哪个 Kprobe 命中,将其保存为 percpu 变量current_kprobe并调用您的预处理程序。之后,它准备一切以单步执行原始指令。单步执行后,调用您的后处理程序,然后执行一些清理。current_kprobe和一些其他的每 cpu 数据用于完成所有这些。只有在那之后才启用抢占。

现在,假设预处理程序启用了抢占,立即被抢占并在不同的 CPU 上恢复。如果 Kprobes 的实现试图访问current_kprobe或其他 per-cpu 数据,内核可能会崩溃(如果此时该 CPU 上没有 current_kprobe,则 NULL 指针 deref)或更糟。

或者,被抢占的处理程序可以在同一个 CPU 上恢复,但另一个 Kprobe 可能会在它休眠时到达那里 -current_kprobe等将被覆盖,并且很可能发生灾难。

在 Kprobe 处理程序中重新启用抢占可能会导致难以调试的内核崩溃和其他问题。

所以,简而言之,这是因为 Kprobes 是这样设计的,至少在 x86 上是这样。关于它们在其他架构上的实现,我不能说太多。


根据您要完成的工作,其他内核工具可能会有所帮助。

例如,如果您只需要在某些函数的开头运行代码,请查看Ftrace。然后,您的代码将在与您挂钩的函数相同的条件下运行。


话虽如此,我的一个项目实际上需要使用 Kprobes,以便处理程序在与被探测指令相同的条件下运行。你可以在这里找到实现。然而,它必须在不破坏任何东西的情况下跳过箍来实现这一目标。到目前为止它工作正常,但它比我想要的更复杂,也有可移植性问题。

于 2018-02-03T14:04:40.703 回答