我想知道printf
在我的 C 代码中执行时调用哪个内核模块。我怎样才能找到那个?比方说,当我使用时,ldd
我可以找到哪些库附加到我的二进制文件中。我正在寻找类似的东西。基本上,我想学习文件结构负责 printf 。
考虑下面的代码
#include <stdio.h>
int main()
{
int i = 0;
int N = 100;
while (i++ <= N) {
printf("%d ", i);
}
return 0;
}
当我运行 perf 命令时perf record -e cycles -j any -a -o perf.data ./test
,我在数据文件中看到以下输出
# Overhead Command Source Shared Object Source Symbol Target Symbol Basic Blo
# ........ ....... .................... ...................................... ...................................... .........
#
21.19% swapper [unknown] [k] 0000000000000000 [k] 0000000000000000 -
10.98% swapper [kernel.vmlinux] [k] __intel_pmu_enable_all [k] native_write_msr -
10.66% swapper [kernel.vmlinux] [k] intel_pmu_lbr_enable_all [k] __intel_pmu_enable_all -
10.66% swapper [kernel.vmlinux] [k] native_write_msr [k] intel_pmu_lbr_enable_all -
5.00% perf [kernel.vmlinux] [k] smp_call_function_single [k] smp_call_function_single -
3.14% swapper [kernel.vmlinux] [k] acpi_os_read_memory [k] acpi_os_read_memory -
2.23% swapper [kernel.vmlinux] [k] intel_idle [k] intel_idle -
1.88% swapper [kernel.vmlinux] [k] sched_clock [k] native_sched_clock -
1.82% swapper [kernel.vmlinux] [k] native_sched_clock [k] sched_clock -
1.15% swapper [kernel.vmlinux] [k] nmi_handle [k] sched_clock -
1.15% swapper [kernel.vmlinux] [k] native_set_fixmap [k] native_set_fixmap -
1.09% swapper [kernel.vmlinux] [k] sched_clock [k] nmi_handle -
0.82% swapper [kernel.vmlinux] [k] __x86_indirect_thunk_rax [k] __x86_indirect_thunk_rax -
...
...
那么,模块/文件与 关联在哪里printf
? perf 输出中的第一行是什么意思?
比方说,我想分析printf
为在屏幕上显示内容而执行的代码。
更新:
以下输出显示我的测试程序已链接到自定义 glibc 版本,我已使用我想要的选项从源代码重新编译了该版本。
$ ldd test
linux-vdso.so.1 => (0x00007ffe26875000)
libc.so.6 => /opt/glibc-2.23-install/libc.so.6 (0x00007f28196d5000)
/opt/glibc-2.23-install/lib/ld-2.23.so => /lib64/ld-linux-x86-64.so.2 (0x00007f2819a76000)
答案1
您正在查看的事件都不是来自您的test
程序;您需要在“命令”字段中找到一行test
,然后放大它。如果运气好的话(因为perf record
样本),您将看到类似的条目
1.31% 590585 libc-2.30.so [.] __vfprintf_internal [.] __vfprintf_internal
0.18% 590585 libc-2.30.so [.] _itoa_word [.] _itoa_word
0.18% 590585 libc-2.30.so [.] __vfprintf_internal [.] _IO_file_xsputn@@GLIBC_2.2.5
0.18% 590585 libc-2.30.so [.] _IO_file_xsputn@@GLIBC_2.2.5 [.] __vfprintf_internal
0.18% 590585 libc-2.30.so [.] __strchrnul_avx2 [.] __strchrnul_avx2
test
等等,显示从(590585
在我的跟踪中)到 C 库的调用,这些调用与您的printf
调用相对应。
printf
主要在 C 库中实现,因此您不会在内核中找到太多相关性。运行你的程序strace
会产生类似的结果
...
write(1, "1 2 3 4 5 6 7 8 9 10 11 12 13 14"..., 2961 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 ) = 296
exit_group(0) = ?
+++ exited with 0 +++
程序中唯一与输出相关的系统调用是单个write
:C 库缓冲其输出,因此所有调用printf
都附加到缓冲区,并且当程序退出时,该缓冲区将在一次调用中刷新。
如果您想了解printf
GNU C 库中的实现方式,请查看 中的源代码stdio-common
,从printf.c
。要对其printf
自身进行分析,您可能会更好地为您服务gprof
。