文章目录
- 1. 动态库相关概念
- 2. ldd 查看依赖项
- 3. 动态链接器 ld.so的加载路径
- 4. 动态版本库版本控制
- 5. ldconfig自动更新soname到linkname
- 6. 可执行程序的执行过程
1. 动态库相关概念
Soname、linkname和realname都是在Linux系统下与共享库(shared library)相关的概念。
-
Soname(Shared Object Name)是共享库的逻辑名称,用于在程序编译和链接时指定要使用的共享库版本。Soname通常以"lib"开头,后面跟着库的名称和版本号。例如,一个共享库的Soname可能是"libexample.so.1"。Soname是在编译时指定的,可以使用-soname=libexample.so.x选项来进行指定,它用于描述与共享库兼容的接口。
-
Linkname是在链接器中使用的共享库的文件名,包括库的名称,一般不包含版本号。例如,一个共享库的Linkname可能是"libexample.so"。
-
Realname是共享库的实际文件名,包括库的名称、版本号和平台相关的文件扩展名。它是文件系统中共享库的实际名称。例如,一个共享库的Realname可能是"libexample.so.1.0.0"。
在应用程序引用共享库时,链接选项里面用的是共享库的link name
。通过link
名字找到对应的real name
动态库,并且把其中的soname
提取出来,写在应用程序自己的文件头的共享库字段里面。当应用程序运行时,就会通过soname
,结合动态链接程序(ld.so)
,在给定的路径下加载real name
的共享库。
举例:
wangzhonglai@shell:/media/wangzhonglai/file/github/pnglib/libpng-1.6.39/install/lib$ ls -l
总用量 3051
-rwxrwxrwx 1 wangzhonglai wangzhonglai 2024626 5月 20 10:16 libpng16.a
-rwxrwxrwx 1 wangzhonglai wangzhonglai 987 5月 20 10:16 libpng16.la
lrwxrwxrwx 1 wangzhonglai wangzhonglai 19 5月 20 10:16 libpng16.so -> libpng16.so.16.39.0 #linkname
lrwxrwxrwx 1 wangzhonglai wangzhonglai 19 5月 20 10:16 libpng16.so.16 -> libpng16.so.16.39.0 #sonmae
-rwxrwxrwx 1 wangzhonglai wangzhonglai 1088704 5月 20 10:16 libpng16.so.16.39.0 #realname
lrwxrwxrwx 1 wangzhonglai wangzhonglai 10 5月 20 10:16 libpng.a -> libpng16.a
lrwxrwxrwx 1 wangzhonglai wangzhonglai 11 5月 20 10:16 libpng.la -> libpng16.la
lrwxrwxrwx 1 wangzhonglai wangzhonglai 11 5月 20 10:16 libpng.so -> libpng16.so
drwxrwxrwx 1 wangzhonglai wangzhonglai 0 5月 20 10:16 pkgconfig
上述实例中,可以看到实际中, linnkname: libpng16.so
和soname: lib16.so.16
都是指向realname: lib16png.so.16.39.0
的软链接,链接时使用 linnkname,ld链接器会根据linkname指向的realname–>lib16png.so.16.39.0的文件头中找到实际的soname名字 lib16.so.16
写入到 实际生成的共享库的头部。
2. ldd 查看依赖项
在 Linux 系统中,ldd
命令可以用来检查一个可执行文件或共享库的依赖项,它在加载可执行文件或共享库时,会按特定的顺序搜索可使用的共享库,这些共享库的搜索路径包括以下几个位置:
$LD_LIBRARY_PATH
如果定义了环境变量 $LD_LIBRARY_PATH
,则 ldd
命令会先在指定的 $LD_LIBRARY_PATH
目录中查找共享库,如果找到了则继续搜索下一个。
$ORIGIN
如果 $ORIGIN
环境变量被定义,动态链接器会将 $ORIGIN
解释为包含库文件的可执行文件所在的目录。然后动态链接器会去这个目录下查找共享库。
RUNPATH
如果可执行程序或共享库中包含了 RUNPATH
的标识,则 ldd
命令会先检查这个目录。
/etc/ld.so.cache
Linux 系统维护了一个内部缓存(/etc/ld.so.cache
)以提高共享库的加载速度。这个缓存记录了系统中存在的所有的共享库,以及它们的位置。如果动态链接器在前面的路径中没有找到匹配的共享库,则进一步搜索 /etc/ld.so.cache
目录下的库。
/usr/lib
和/lib
如果动态链接器在前四个位置中都没找到所需的共享库,就会在这些固定位置(通常是 /usr/lib
和 /lib
)中查找它。
总之,在 Linux 系统中,通过 $LD_LIBRARY_PATH
、$ORIGIN
、 RUNPATH
、/etc/ld.so.cache
和 /usr/lib
等路径,ldd
命令将进行可执行文件或共享库可能需要的共享库检查。
举例:
wangzhonglai@shell:~/learn/c_test/ldd_test$ ldd mainlinux-vdso.so.1 (0x00007ffd1cdb3000)libtest.so.0 => /home/wangzhonglai/learn/c_test/ldd_test/libtest.so.0 (0x00007f6323384000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6323172000)/lib64/ld-linux-x86-64.so.2 (0x00007f6323390000)
可以看到 可执行程序所有依赖的动态库。
3. 动态链接器 ld.so的加载路径
在 Linux 系统中,动态链接器 ld.so
在加载共享库时,默认会搜索一个或多个目录,以找到需要加载的共享库文件。这些目录包括:
$LD_LIBRARY_PATH
$LD_LIBRARY_PATH
环境变量指定的目录,其中 $
是一个环境变量的引用符号。在这个目录下,ld.so
会寻找需要加载的共享库。
/etc/ld.so.conf
文件
/etc/ld.so.conf
文件规定了 ld.so
要查找共享库的目录列表。在该文件中,可以指定要查找的目录和忽略的目录。
/etc/ld.so.conf.d/
目录
Linux 系统中, /etc/ld.so.conf.d/
的目录包含了其它的配置文件,这些配置文件包含了需要加入到动态加载库路径中的目录信息,这相当于 ld.so.conf
文件的一种补充。例如,针对某些特定应用程序,系统管理员可以新增配置文件,来指定与应用程序相关的运行时库目录。
- 缓存文件
/etc/ld.so.cache
ld.so
会将共享库的文件名及路径缓存到 /etc/ld.so.cache
文件中,从而加速共享库的加载。因此,在加载共享库时,ld.so 会在缓存文件中查找共享库。如果找到,则会直接加载;如果没有找到,则会继续搜索目录。
/usr/lib
和/lib
如果在上述目录中都没有找到目标共享库,则 ld.so
会在这些固定位置中查找它,例如标准 libc 库。
总之,在 Linux 系统中,ld.so
在加载共享库时,会搜索 $LD_LIBRARY_PATH
、/etc/ld.so.conf
、/etc/ld.so.conf.d/
、缓存文件 /etc/ld.so.cache
和 /usr/lib
和 /lib
。这些搜索路径旨在确保所需的共享库被正确加载,从而支持程序正常运行。
4. 动态版本库版本控制
ld.so将会在上面第3节描述的路径下寻找soname,这里面分为以下几种情况:
-
soname是一个link 文件,指向实际的 realname
wangzhonglai@shell:~/learn/c_test/ldd_test$ ll 总用量 264 drwxrwxr-x 2 wangzhonglai wangzhonglai 4096 5月 20 14:18 ./ drwxrwxr-x 3 wangzhonglai wangzhonglai 4096 5月 20 11:10 ../ lrwxrwxrwx 1 wangzhonglai wangzhonglai 16 5月 20 13:23 libtest.so -> libtest.so.0.0.0* lrwxrwxrwx 1 wangzhonglai wangzhonglai 17 5月 20 14:16 libtest.so.0 -> libtest.so.0.2.238* -rwxrwxr-x 1 wangzhonglai wangzhonglai 16208 5月 20 13:21 libtest.so.0.0.0* -rwxrwxr-x 1 wangzhonglai wangzhonglai 16200 5月 20 13:48 libtest.so.0.0.1* -rwxrwxr-x 1 wangzhonglai wangzhonglai 16200 5月 20 13:56 libtest.so.0.2.238*
ld.so 将会实际加载soname 软链接指向的 realname。
当有更新的realname发布的时候,只需要更改 soname指向的软链接即可,不需要重新编译程序。这个更改soname的软链接,可以手动更改,也可以使用ldconfig自动更新。
ldconofig可以更新主版本号相同的so,跨主版本号的so是无法自动更新的。当然自动更新后也可能会出现很多的兼容问题。
-
soname是一个非link的普通文件
这时候,ld.so将会加载soname到内存。这样更新只能更新 soname,也就不能存在多版本so共存的现象。
5. ldconfig自动更新soname到linkname
在 Linux 系统中,ldconfig
是一个用于动态链接器(ld.so
)缓存(/etc/ld.so.cache
)的工具,它的主要功能包括:
- 自动加载共享库
执行 ldconfig
命令时,它会在系统中搜索共享库,并解析出共享库的 .so
文件路径。然后它会根据共享库的 soname
命名规则,生成一组基于 soname
的符号链接,该符号链接指向共享库的 .so
文件。这样当动态链接器 ld.so
在运行时寻找共享库时(例如,ldd
命令所使用的),就可以使用符号链接找到共享库。
- 缓存共享库的目录
ldconfig
会将生成的 .so
文件和它所在的目录缓存下来,存储到 /etc/ld.so.cache
文件中,以便系统在后续查找共享库时能够快速定位共享库所在的目录和文件。
- 管理共享库的依赖关系
当系统上安装了多个版本的共享库时,ldconfig
可以管理它们之间的依赖关系。ldconfig
可以根据指定的搜索路径,将共享库的依赖链整理成一条有效的依赖链,并将其保存在缓存文件中。这样,在运行指定程序时,ld.so
在启动过程中就可以快速扫描缓存文件,从而快速地找到所有需要的共享库,并加载它们。
- 更新共享库缓存
每次安装、更新或删除共享库后,都需要运行 ldconfig
命令来更新共享库缓存。这样,ld.so
就可以找到这些共享库,而不影响其他系统中的共享库。
- 管理搜索路径
通过修改 /etc/ld.so.conf
文件和 /etc/ld.so.conf.d/
目录下的配置文件,可以配置 ldconfig
命令的搜索路径。这样就可以定制化共享库的搜索路径,从而支持基于不同程序使用不同共享库的功能。
总之,ldconfig
是一个管理动态链接器缓存的工具,它可以自动加载共享库、缓存共享库的目录、管理共享库的依赖关系、更新共享库缓存和管理搜索路径。这些功能能够帮助维护者管理系统中的共享库,并加速共享库的加载,从而使程序和应用更加高效地运行。
6. 可执行程序的执行过程
在 Linux 系统中,可执行程序执行的大致过程如下:
- 装载(Loading)可执行程序
当用户执行可执行程序时,系统会使用 execve()
系统调用加载它。加载器首先解析可执行文件的 ELF 头,获取各个段的信息,然后将程序读入内存,并分配虚拟地址空间,将各个段映射到合适的虚拟地址上。
- 动态链接(Dynamic Linking)
在装载可执行程序时,如果其中包含了动态链接库,系统会使用动态链接器(例如 ld.so
)将动态链接库加载到内存中。动态链接器还会检查可执行文件和所依赖的库中的符号,将其绑定到相应的地址上。这样就能确保程序能在运行时动态调用库函数。
- 运行可执行程序
在装载和链接完成后,CPU 开始执行程序的 main()
函数。可执行程序使用系统调用(例如 open()
和 write()
)与操作系统进行交互,执行相应的操作。程序的运行过程可能包括对文件系统进行读写、对网络进行访问等各种操作。
- 退出程序
当程序执行完毕或发生错误时,它会调用 exit()
系统调用退出。此时,系统会收回可执行程序占用的内存和其他资源并结束程序的运行。
总之,在 Linux 系统中,可执行程序执行的过程涉及到装载、动态链接、运行和退出。详细地说,可执行程序在加载后,动态链接器会将共享库加载到内存中,并将其符号与相应的代码绑定在一起,然后 CPU 执行程序的 main()
函数,并与操作系统进行交互。执行完毕后,程序退出并释放其所占用的资源。