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



我正在尝试监视进程在执行过程中调用了哪些函数。我的目标是了解一个流程在每个功能上花费了多少时间。函数被推送到堆栈上,并在函数调用返回时弹出。我想知道在内核代码中,这种推送和弹出实际上发生在哪里。

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

我必须编写一个模块来利用这段代码。在这种情况下请帮帮我。

函数被推送到堆栈上,并在函数调用返回时弹出。我想知道在内核代码中,这种推送和弹出实际上发生在哪里。

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

您可以用call my_tracing_routine修补内核中的每个callret指令,并在那里记录指令指针,然后将控制权传递给原始的被调用者/调用者。有一些工具可以做到这一点:LTTng、SystemTap以及内核内接口,如kprobes、ftrace。。。这种方法称为跟踪

但是,如果修补所有指令,即使用SystemTap探针kernel.function("*"),您将扼杀性能,并可能导致系统死机。因此,您不能测量每一次函数调用,但您可以测量每一次Nth功能调用,并希望您能得到等效的结果,但您需要大的样本(即运行程序几分钟),这称为评测

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周期后,会引发中断,并且性能处理程序(它已经嵌入内核)会保存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使用连接到性能事件cpu-clocktimer.profile探测。为此,它生成、构建和加载内核模块。您可以使用stap -k -p 3进行检查

最新更新