根据docs,kprobes禁用抢占:
探针处理程序在禁用抢占的情况下运行。取决于 架构和优化状态,处理程序也可以运行 中断禁用(例如,kretprobe处理程序和优化的kprobe 处理程序在x86 / x86-64上没有禁用中断的情况下运行。
从commit 9a09f261a我们可以清楚地看到用于运行抢占的优化kprobes。
为什么会这样?我理解kprobes是一种在内核中的特定地址注入一些代码的方法,并且理解任何代码都应该没问题。
答案 0 :(得分:2)
至少在x86上,Kprobes的实现依赖于在Kprobe处理程序运行时禁用抢占的事实。
当您在指令上放置普通(不是基于Ftrace的)Kprobe时,该指令的第一个字节将被0xcc(int3,“软件断点”)覆盖。如果内核尝试执行该指令,则会发生陷阱并调用kprobe_int3_handler()
(请参阅do_int3()的实现)。
要调用Kprobe处理程序,kprobe_int3_handler()会找到哪个Kprobe命中,将其保存为percpu变量current_kprobe
并调用预处理程序。在那之后,它准备好了一步到原来的指令。单步后,调用后处理程序,然后执行一些清理。 current_kprobe
和其他一些per-cpu数据用于完成所有这些操作。抢占仅在此之后启用。
现在,假设预处理程序已启用抢占,立即被抢占并在不同的CPU上恢复。如果Kprobes的实现试图访问current_kprobe
或其他每个cpu数据,内核可能会崩溃(如果此CPU上没有current_kprobe,则指针为NULL指针deref)或更糟。
或者,被抢占的处理程序可以在同一个CPU上恢复,但是另一个Kprobe可能会在它睡觉时点击那里 - current_kprobe
等会被覆盖并且很可能发生灾难。
重新启用Kprobe处理程序中的抢占可能导致难以调试的内核崩溃和其他问题。
因此,简而言之,这是因为Kprobes以这种方式设计,至少在x86上。我不能多说他们在其他架构上的实现。
根据您要完成的任务,其他内核工具可能会有所帮助。
例如,如果您只需要在某些功能开始时运行代码,请查看Ftrace。然后,您的代码将在与您挂钩的函数相同的条件下运行。
所有这一切,在我的一个项目中实际上需要使用Kprobes,以便处理程序在相同的条件下运行w.r.t.抢先作为探测指令。您可以找到实施here。然而,它必须跳过篮球才能实现这一点而不会破坏任何东西。它到目前为止一直运行良好,但它比我想要的更复杂,也有可移植性问题。