动态tracing点 keprobes 作用是啥?

可以动态追踪任意内核函数

kprobes怎么实现的呢?

通过在运行时,巧妙地、临时地修改目标函数在内存中的机器码,用一个“陷阱”(Trap)指令替换掉函数的原始指令,从而劫持 CPU 的执行流。

实现前提—kallsyms符号表

内核为了方便调试,会保留一个叫做 kallsyms 的符号表,里面记录了几乎所有内核函数名和其内存地址的对应关系。Kprobe 就是靠这个表来找到你指定函数的位置的。

实现步骤

Info

一句话概括: 通过特殊中断, 进入kprobe中预先注册好的处理程序, 执行后, 再恢复

第一步:注册探针 (Registering a Probe)
  • 查找地址:内核会在它的“地图”(kallsyms 符号表)里查找 do_sys_openat2 这个函数名字对应的内存地址。比如,它发现这个函数位于内存地址 0xffffffff8129a350
  • 备份指令:Kprobe 模块会小心地复制这个地址处原本的几个字节的机器码,并保存起来。这至关重要,就像你在施工前,先把原有的铁轨拆下来,并妥善保管好,以便将来恢复。
  • 准备“跳板”:Kprobe 模块会准备一小段代码,我们称之为“蹦床”(Trampoline)或者“处理程序”(Handler)。这段代码的职责是:
    • 保存火车当前的状态(CPU 寄存器)。
    • 调用你指定的处理函数(比如你的 BPF 程序)。
    • 恢复火车的状态。
    • 执行刚才被备份的原始指令。
    • 让火车跳回到原始轨道的下一条指令继续行驶。
第二步:激活探针 (Arming the Probe)

修改内存中的内核代码, 会用一个特殊的、特定于 CPU 架构的**“陷阱”或“断点”指令**(在 x86 架构上通常是 int3 指令,机器码为 0xcc),去覆盖do_sys_openat2 函数开头的那几个字节的机器码。

第三步:探针触发 (Probe Hit)
  • CPU 执行了我们放置的 int3 指令,立即产生一个“断点异常”(Breakpoint Exception)。
  • CPU 会暂停当前的执行流,并根据一个叫做“中断描述符表”(IDT)的系统表格,跳转到内核预先注册好的断点异常处理程序去执行。
  • 这个处理程序就是 Kprobe 模块的一部分。Kprobe 的异常处理程序被唤醒后,它会检查这个中断是在哪个地址触发的。它发现是在 0xffffffff8129a350,就知道是 do_sys_openat2 上的探针被命中了。
第四步:恢复执行 (Resuming Execution)
  • 跳回主线:执行完被替换掉的指令后,它会计算出原始函数的下一条指令的地址(比如 0xffffffff8129a351),然后把 CPU 的指令指针直接设置为这个地址。
  • 继续前进:CPU 从 0xffffffff8129a351 继续执行 do_sys_openat2 函数剩下的代码,就好像什么都没发生过一样。火车顺利地驶出了临时站点,回到了主轨道上。