我正在搜索一些有关使用运行时链接器加载程序时发生的运行时时间开销的统计信息(例如ld.so
)。我不是运行时链接器如何工作的专家,但据我了解,它通常会执行以下操作:
- 在众所周知的路径或
LD_LIBRARY_PATH
中搜索共享库 - 加载共享库
- 二手功能的符号分辨率
因此,当我通过GUI启动程序或通过命令行启动程序时,在某个时候,系统调用exec
的系统将发生,并且启动了请求的程序。让我们快速查看那时会发生什么:
-
Exec(myprogram)
- 操作系统将
myprogramm
加载到内存 - 操作系统将执行转换为
_start
- 发生了一些初始化,运行时链接器已运行
-
main()
称为
假设上面的列表是正确的,我没有遗漏任何重大步骤,我对两件事感兴趣:
- 第4步的开销是什么?根据理论?
- 在实践中,我该如何确定步骤4的开销。(例如,对于诸如Firefox或Chrome之类的真实程序)?
假设上面的列表正确
这不是正确的,正如此答案所解释的那样。
第4步的开销是什么?根据理论?
it 取决于和
发挥作用的一些因素:
-
该程序链接到多少个动态库?
有几十个被加载的库并不少见。
当其中有5000个或更多时,事情会急剧减慢。 -
程序参考多少数据和功能符号?
必须在加载时解决数据参考,但是函数符号可以懒惰地解决。(有关懒惰符号分辨率的更多信息。)
我如何在实践中确定步骤4的开销。(例如,对于诸如Firefox或Chrome之类的实际程序)?
使用GLIBC,只需在环境中设置LD_DEBUG=statistics
即可。例如:
LD_DEBUG=statistics /bin/date
104984:
104984: runtime linker statistics:
104984: total startup time in dynamic loader: 1348294 clock cycles
104984: time needed for relocation: 501668 clock cycles (37.2%)
104984: number of relocations: 90
104984: number of relocations from cache: 3
104984: number of relative relocations: 1201
104984: time needed to load objects: 413792 clock cycles (30.6%)
Sun Dec 11 17:51:35 PST 2016
对于粗略的估计,编写两个测试程序:a static 程序和一个使用动态库(变体:仅C运行时动态库)。然后测量开始时间的差异。如果两个程序的大小相当,则差异可以归因于动态加载。
Exec(myprogram)
- 操作系统将
myprogramm
加载到内存- 操作系统将执行转换为
_start
- 发生了一些初始化,运行时链接器已运行
main()
称为假设上面的列表是正确的,我没有遗漏任何 主要步骤
实际上这不是正确的列表,至少对于Linux而言。主要注意:动态链接器(将映射需要库的程序程序地址空间在程序获得控制之前运行。有具有动态链接器的路径的精灵部分,通常类似于/lib/ld-linux.so.2
,并且该程序在真实程序之前获得控制,并且它"加载"共享库。
次要注意:"加载到内存"实际上并不真实,实际上,实际上文件的文件具有可执行文件和共享的库文件,映射到了过程的地址空间中,并且按需加载了下一个4K代码/数据(4K是常见的存储器页面大小)。将控件送给_ start也不是完全正确的,从ELF标题中获取执行控制的点,这是" _ start"符号指向此地址的典型,但是我想您可以创建无需" _ start"符号的ELF文件。
我会对两件事感兴趣:
- 第4步的开销是什么?根据理论?
- 在实践中,我该如何确定步骤4的开销。(例如,对于诸如Firefox或Chrome之类的真实程序)?
正如我写的那样,在步骤4动态链接器不运行时,实际上,如果您仪器Firefox或Chrome程序,您将无法测量LD-Linux.so.2的工作时间,因为它在Firefox/Chrome的任何指示之前运行可执行文件控制。
您可以编辑firefox的可执行文件,然后替换/lib/ld-linux.so.2至/lib/ld-linux.so。它的)和仪器以测量时间。
所有其他在main
之前运行的代码,我认为您可以以正常方式进行配置,例如这样:什么是Linux上C 的易于使用?