【问题标题】:Why do kprobes disable preemption and when is it safe to reenable it?为什么 kprobes 禁用抢占,什么时候可以安全地重新启用它?
【发布时间】:2018-07-13 03:29:55
【问题描述】:

根据docs,kprobes禁用抢占:

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

commit 9a09f261a 我们可以清楚地看到优化的 kprobes 过去在启用抢占的情况下运行。

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

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

【问题讨论】:

    标签: linux kernel preemption kprobe


    【解决方案1】:

    至少在 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 或其他 per-cpu 数据,内核可能会崩溃(如果此时该 CPU 上没有 current_kprobe,则 NULL 指针 deref)或更糟。

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

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

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


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

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


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

    【讨论】:

    • 感谢您的精彩回答和指向 ftrace 的指针。我花了一段时间才理解这一切,但现在我对 kprobe 机制的理解要好得多!
    • 感谢您的精彩回答!我不明白为什么在提交9a09f26 之前没有禁用抢占。
    • @pchaigno 很确定这是一个疏忽。常规 kprobe 始终禁用抢占。只有优化的 kprobes 缺少这个。但是,优化的 kprobes 无论如何都不适用于抢占式内核(请参阅文档),因此这不会改变任何内容。
    • 我明白了。感谢您的回答!
    猜你喜欢
    • 2016-02-25
    • 2014-01-13
    • 1970-01-01
    • 2014-04-13
    • 2015-10-02
    • 2012-12-06
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多