# 函数库的管理

在 Linux 操作系统中,函数库是很重要的一个项目,因为很多软件之间都会互相取用彼此提供的函数库来进行特殊功能的运行,所以很函数库的利用是很重要的。

不过函数库又按照是否被编译到程序内部而分为动态与静态函数库,他们有什么差异呢?

# 动态与静态函数库

# 静态函数库的特色

  • 扩展名:.a

    通常扩展名为 libxxx.a 类型

  • 编译行为:

    在编译时会直接整合到执行程序中,所以利用静态函数库编译成的文件会比较大

  • 独立执行的状态:

    最大优点就是编译成功的可执行文件可以独立执行,而不需要再向外部要求读取函数库的内容

  • 升级难易度

    由于全部打包到执行文件中,因此若函数库升级,整个执行文件必须要重新编译

# 动态函数库的特色

  • 扩展名:.so

    通常为 libxxx.so

  • 编译行为

    动态函数库在编译的时候,在程序里面只有一个「指向(Pointer)」位置而已。在实际运行时才会去读取函数库来使用,因此编译后的执行文件相对小很多

  • 独立执行的状态

    不能被独立执行,因为所指向的函数库必须要存在才行,而且函数库「所在的目录也不能改变」,因为可执行文件里面仅有「指向」,也就是在需要使用函数库时,程序会主动去某个路径下读取,

  • 升级难易度

    相对来说,执行文件可能不需要重新编译,由于有指向,只需要更新某个函数库即可

目前的 Linux distribution 比较倾向于使用动态函数库,上面已经提到过了,好处多多

绝大多数的函数库都放在 /lib64/lib 目录下,此外, Linux 系统里很多的函数库其实 kernel 就提供了,那么 kernel 的函数库是放在 /lib/modules 里面的。

注意:不同版本核心提供的函数库差异有可能差异很大,不要版本混用函数库

# ldconfig 与 /etc/ld.so.conf

目前我们的 Linux 大多数将函数库做成动态函数库后,在运行时有没有办法改善函数库的读取效率?将常用到的动态函数库先加载到内存中,如此一来当软件要调用动态函数库时,就不需要去硬盘中读取了,这个时候就需要用到 Idconfig 与 /etc/id.so.conf

如何将动态函数库加载到高速缓存中呢?

  1. 首先,必须要在 /etc/ld.so.conf 里写下「想要读入高速缓存中的动态函数库所在的目录」
  2. 利用 Idconfig 程序将 /etc/ld.so.conf 的资料读取到内存中
  3. 同时也将数据记录一份在 /etc/ld.so.cache 文件中

事实上,Idconfig 还可以用来判断动态函数库的链接信息

ldconfig [-f conf] [-C cache]
ldconfig [-p]

选项与参数
	-f conf:conf 指某个文件名,含义是使用 conf 作为 libaray 函数库的取得路径,而不以 `/etc/ld.so.conf` 作为默认值
	-C cache:自定义 cache 文件的路径
	-p:列出目前的所有函数库资料内容(在 /etc/ld.so.cache 内的资料) 

1
2
3
4
5
6
7
8
# 范例 1:假设我的 Mariadb 数据库函数库在 /usr/lib64/mysql 中,如何读入 cache
[root@study ~]# vim /etc/ld.so.conf.d/mrcode.conf
/usr/lib64/mysql			# 仅增加一行数据
[root@study ~]# ldconfig
# 执行后不会显示任何信息的

# 就发现找到了我们刚刚加载的
[root@study ~]# ldconfig -p | grep mysql
        libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.18
1
2
3
4
5
6
7
8
9

这做了之后,可以加快函数库读取的效率。在某些时候,你可能会自行加入某些 Tarball 安装的动态函数库,而想要这些动态函数库的相关链接可以被读入到缓存中,这个时候就可以像面那样写到 .conf 文件中

# 程序的动态函数库解析:ldd

可以通过 ldd 判断某个可执行的 binary 文件含有哪些动态函数库。

ldd [-vdr] [filename]

选项与参数:
	-v:列出所有内容信息
	-d:重新将资料有遗失的 link 显示
	-r:将 ELF 有关的错误内容显示
1
2
3
4
5
6

实践练习

# 范例 1:找出 /usr/bin/passwd 文件的函数库数据
[root@study ~]# ldd /usr/bin/passwd 
	linux-vdso.so.1 =>  (0x00007ffe3234e000)
	libuser.so.1 => /lib64/libuser.so.1 (0x00007f320cce7000)
	libgobject-2.0.so.0 => /lib64/libgobject-2.0.so.0 (0x00007f320ca96000)
	libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f320c780000)
	libpopt.so.0 => /lib64/libpopt.so.0 (0x00007f320c576000)
	libpam.so.0 => /lib64/libpam.so.0 (0x00007f320c367000)	# PAM 模块
	libpam_misc.so.0 => /lib64/libpam_misc.so.0 (0x00007f320c163000)
	libaudit.so.1 => /lib64/libaudit.so.1 (0x00007f320bf3a000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f320bd13000)	# SELinux
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f320baf7000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f320b729000)
	libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f320b525000)
	libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f320b2ee000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f320b08c000)
	libffi.so.6 => /lib64/libffi.so.6 (0x00007f320ae84000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f320ac80000)
	libcap-ng.so.0 => /lib64/libcap-ng.so.0 (0x00007f320aa7a000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f320d10d000)
	libfreebl3.so => /lib64/libfreebl3.so (0x00007f320a877000)
	
# 之前一直在说 passwd 有使用到 pam 模块,怎么知道的?
# 上面通过 ldd 就知道了,使用了 libpam.so 函数库

# 范例 2:找出 /lib64/libc.so.6 这个函数库的相关其他函数库
[root@study ~]# ldd -v /lib64/libc.so.6 
	/lib64/ld-linux-x86-64.so.2 (0x00007fbe6ee0f000)
	linux-vdso.so.1 =>  (0x00007ffe450fa000)

	Version information:  # 使用 -v 选项,显示其他版本信息
	/lib64/libc.so.6:
		ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
		ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
1
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

未来如果你常常升级安装 RPM 软件时(下一章会介绍),应该常常会发现那个「相依属性」的问题,可以先用 ldd 观察「相依函数库」之间的相关性

如上,检查了 libc.so.6 这个函数库,结果发现他还与 ld-linux-x86-64.so.2 (GLIBC_2.3) 有关,就可以了解下那个文件到底是什么软件的函数库