如何减少动态库加载时间?

问题描述 投票:0回答:3

我有一个关于减少动态链接时间的问题。

我有一个链接到 189 个共享库的二进制文件。我使用

perf
工具分析了执行情况,动态链接器 (ld.so) 占用了程序运行时间的 40%,大约 90 毫秒的时间。

有什么方法可以优化动态库加载时间,还是我被迫使用静态链接?

我运行这个程序很多次。

linux shared-libraries ld dynamic-linking
3个回答
2
投票
  1. 避免在库中使用 C++ 构造函数,因为它们会导致运行构造函数本身、解析符号和从硬盘驱动器加载代码的延迟
  2. 通过使用
    -fvisibility=hidden
    编译库来最大程度地减少导出符号的数量(并选择性地使用
    __attribute__((visibility("default")))
    来导出您想要导出的符号)
  3. 使用
    --as-needed
    最大限度地减少库依赖性
  4. 确保启用了延迟绑定(即未设置
    LD_BIND_NOW
    并且未使用
    -Wl,-z,now
    编译库)
  5. 最后,您可以使用 Prelink 工具静态预计算符号偏移量

可以通过导出来测量库加载时间

LD_DEBUG=statistics


1
投票

加载时间取决于几个因素:将库从媒体存储传输到内存所需的时间、链接的符号数量...以下是一些建议:

  1. 如果使用dlopen(),可以设置惰性绑定标志(RTLD_LAZY)
  2. 确保使用 ldconfig 更新 ld.so.cache(如有必要,更新 ld.so.conf)以缩短库搜索时间。使用 strace 可能有助于查看查找库文件的尝试次数。另请检查 LD_LIBRARY_PATH 环境变量。
  3. 确保未设置LD_BIND_NOW环境变量以使动态链接器(ld.so)以惰性模式运行

小贴士

  • 由于库的某些部分(尤其是代码等只读部分)在运行的进程之间共享。您可以安排一个后台进程与库动态链接,以便为使用相同库的任何新创建的进程节省一些动态链接时间,因为它们中的大多数已经加载到内存中。
  • 您可以在
    strace
    下启动程序来检查是否直接在正确的位置找到了库,或者动态链接器是否正在多次尝试找到它们。这通常意味着
    LD_LIBRARY_PATH
    环境变量设置错误或根本不应该设置。这也可能意味着链接器的命令行错误地设置了构建时间。
    例如,这是
    strace
    可执行文件启动的
    /bin/ls
    结果。像 libmlibpam 这样的库可以在正确的位置找到,但是 libc 需要在多个目录中多次尝试才能最终在 /lib 中找到:
[root@sa415m ~]# strace /bin/ls
execve("/bin/ls", ["/bin/ls"], [/* 17 vars */]) = 0
brk(NULL)                               = 0x1429000
uname({sysname="Linux", nodename="sa415m", ...}) = 0
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6f16000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=57138, ...}) = 0
mmap2(NULL, 57138, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb6f08000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\320l\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0555, st_size=476584, ...}) = 0
mmap2(NULL, 540784, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e63000
mprotect(0xb6ed7000, 61440, PROT_NONE)  = 0
mmap2(0xb6ee6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x73000) = 0xb6ee6000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libpam.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\30\34\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=42672, ...}) = 0
mmap2(NULL, 106852, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e48000
mprotect(0xb6e52000, 61440, PROT_NONE)  = 0
mmap2(0xb6e61000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x9000) = 0xb6e61000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libpam_misc.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\\\n\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=9684, ...}) = 0
mmap2(NULL, 73936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e35000
mprotect(0xb6e37000, 61440, PROT_NONE)  = 0
mmap2(0xb6e46000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0xb6e46000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\250#\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0555, st_size=67284, ...}) = 0
mmap2(NULL, 141272, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e12000
mprotect(0xb6e22000, 61440, PROT_NONE)  = 0
mmap2(0xb6e31000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0xb6e31000
mmap2(0xb6e33000, 6104, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb6e33000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\200Q\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=116992, ...}) = 0
mmap2(NULL, 187996, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6de4000
mprotect(0xb6e00000, 61440, PROT_NONE)  = 0
mmap2(0xb6e0f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b000) = 0xb6e0f000
mmap2(0xb6e11000, 3676, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb6e11000
close(3)                                = 0
openat(AT_FDCWD, "/lib/tls/v7l/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/v7l/neon/vfp", 0xbe941508) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/v7l/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/v7l/neon", 0xbe941508) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/v7l/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/v7l/vfp", 0xbe941508)  = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/v7l/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/v7l", 0xbe941508)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/neon/vfp", 0xbe941508) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/neon", 0xbe941508)     = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls/vfp", 0xbe941508)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/tls", 0xbe941508)          = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/v7l/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/v7l/neon/vfp", 0xbe941508) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/v7l/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/v7l/neon", 0xbe941508)     = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/v7l/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/v7l/vfp", 0xbe941508)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/v7l/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/v7l", 0xbe941508)          = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/neon/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/neon/vfp", 0xbe941508)     = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/neon/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/neon", 0xbe941508)         = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/vfp/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/lib/vfp", 0xbe941508)          = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0Lr\1\0004\0\0\0"..., 512) = 512
[...]

0
投票

如果您的某些库很少使用或在特定用例中使用,您可以使用延迟加载仅在首次使用时加载它们。 Windows 和 macOS 中有针对此问题的内置解决方案。对于 Linux,您可以使用 Implib.so

© www.soinside.com 2019 - 2024. All rights reserved.