如何在 linux 内核中访问进程的内核堆栈?

How to access a process's kernel stack in linux kernel?

我正在尝试监视进程在执行过程中调用了哪些函数。我的目标是了解一个进程在每个功能上花费了多少时间。函数被压入堆栈并在函数调用 returns 时弹出。我想知道这个 push 和 pop 实际发生在内核代码的什么地方。

我在 task_struct 中找到了一个 void *stack 字段。我不确定这是否是我要寻找的领域。如果是,那么如何知道它是如何更新的?

我必须编写一个将使用此代码的模块。在这种情况下请帮助我。

The functions are pushed over a stack and popped when function call returns. I would like to know where in the kernel code this push and pop actually happens.

它不会发生在内核代码中,它是由处理器完成的。 IE。当 x86 汇编 CPU 找到 call 指令时,它将 IP 压入堆栈,而 ret 指令将弹出该值。

您可以用 call my_tracing_routine 修补内核中的每个 callret 指令,并在那里记录指令指针,而不是将控制权交给原始 callee/caller。有一些工具可以做到这一点:LTTng, SystemTap,以及 kprobes、ftrace 等内核接口...这种方法称为 tracing

但是如果补丁 all 指令,即使用 SystemTap 探测 kernel.function("*"),你会降低性能,并可能导致系统崩溃。所以,你不能衡量 every 函数调用,但你可以衡量每 Nth 函数调用,并希望你会得到等效的结果,但是您将需要大型 sample(即 运行 几分钟的程序)——这称为 profiling

Linux 随分析器一起提供 perf:

# perf record -- dd if=/dev/zero of=/dev/null
...
^C

# perf report
9.75%  dd  [kernel.kallsyms]  [k] __clear_user
6.69%  dd  [kernel.kallsyms]  [k] __audit_syscall_exit
5.61%  dd  [kernel.kallsyms]  [k] fsnotify
4.73%  dd  [kernel.kallsyms]  [k] system_call_after_swapgs
4.37%  dd  [kernel.kallsyms]  [k] system_call
...

您也可以使用 -g 来收集调用链。默认情况下 perf 使用 CPU 性能计数器,因此在 N CPU 周期后,中断被引发,并且 perf 处理程序(它已经嵌入到内核中)保存 IP.

如果你想收集堆栈,你可以用 SystemTap 来做:

# stap --all-modules -e '
    probe timer.profile { 
        if(execname() == "dd") { 
            println("----"); 
            print_backtrace(); } 
        }' -c 'dd if=/dev/zero of=/dev/null' 
...
    ----
0xffffffff813e714d : _raw_spin_unlock_irq+0x32/0x3c [kernel]
0xffffffff81047bb9 : spin_unlock_irq+0x9/0xb [kernel]
0xffffffff8104ac68 : get_signal_to_deliver+0x4f0/0x528 [kernel]
0xffffffff8100216f : do_signal+0x48/0x4b1 [kernel]
0xffffffff81002608 : do_notify_resume+0x30/0x63 [kernel]
0xffffffff813edd6a : int_signal+0x12/0x17 [kernel]

在此示例中,SystemTap 使用 timer.profile 探测器附加到性能事件 cpu-clock。为此,它会生成、构建和加载内核模块。您可以使用 stap -k -p 3

检查