在 Linux 操作系统当中,函式库是很重要的一个项目。因为很多的软件之间都会互相取用彼此提供的函式库来进行特殊功能的运作,例如很多需要验证身份的程序都习惯利用 PAM 这个模块提供的验证机制来工作,而很多网络联机机制则习惯利用 SSL 函式库来实现联机加密的机制。所以说,函式库的利用是很重要的。不过,函式库又依照是否被编译到程序内部而分为动态与静态函式库,这两者之间有何差异?哪一种函式库比较好?
动态与静态函式库
首先我们要知道的是,函式库的类型有哪些?依据函式库被使用的类型而分为两大类,分别是静态(Static)与动态(Dynamic)函式库两类。下面来看一看这两种类型的函式库吧!
静态函式库特点
- 扩展名:这类的函式库通常扩展名为
libxxx.a
; - 编译行为:这类函式库在编译的时候会直接整合到执行程序当中,所以利用静态函式库编译成的文件会比较大一些;
- 独立执行的状态:这类函式库最大的优点,就是编译成功的可执行文件可以独立执行,而不需要再向外部要求读取函式库的内容(请对比动态函式库的说明)。
- 升级难易度:虽然执行档可以独立执行,但因为函式库是直接整合到执行档中,因此若函式库升级时,整个执行档必须要重新编译才能将新版的函式库整合到程序当中。也就是说,在升级方面,只要函式库升级了,所有将此函式库纳入的程序都需要重新编译!
动态函式库特点
- 扩展名:这类函式库通常扩展名为
libxxx.so
的类型; - 编译行为:动态函式库与静态函式库的编译行为差异挺大的。与静态函式库被整个放到程序中不同的,动态函式库在编译的时候,在程序里面只有一个指向(Pointer)的位置而已。也就是说,动态函式库的内容并没有被整合到执行档当中,而是当执行档要使用到函式库的机制时,程序才会去读取函式库来使用。由于执行文件当中仅具有指向动态函式库所在的指标而已,并不包含函式库的内容,所以他的文件会比较小一点。
- 独立执行的状态:这类型的函式库所编译出来的程序不能被独立执行,因为当我们使用到函式库的机制时,程序才会去读取函式库,所以函式库文件必须要存在才行,而且,函式库的所在目录也不能改变,因为我们的可执行文件里面仅有指标,亦即当要取用该动态函式库时,程序会主动去某个路径下读取,所以动态函式库可不能随意移动或删除,会影响很多相依的程序软件。
- 升级难易度:虽然这类型的执行档无法独立运作,然而由于是具有指向的功能,所以,当函式库升级后,执行档根本不需要进行重新编译的行为,因为执行档会直接指向新的函式库文件(前提是函式库新旧版本的档名相同)。
目前的 Linux distribution 比较倾向于使用动态函式库,因为如同上面提到的最重要的一点,就是函式库的升级方便!由于 Linux 系统里面的软件相依性太复杂了,如果使用太多的静态函式库,那么升级某一个函式库时,都会对整个系统造成很大的冲击!因为其他相依的执行档也要同时重新编译啊!这个时候动态函式库可就有用多了,因为只要动态函式库升级就好,其他的软件根本无须变动。
那么这些函式库放置在哪里呢?绝大多数的函式库都放置在:/ib64
, /lib
目录下!此外,Linux 系统里面很多的函式库其实 kernel 就提供了,那么 kernel 的函式库放在哪里?就是在 /lib/modules
里面啦!里面的数据可多着呢!不过要注意的是,不同版本的核心提供的函式库差异性是挺大的,所以 kernel 2.4.xx 版本的系统不要想将核心换成 2.6.xx 喔!很容易由于函式库的不同而导致很多原本可以执行的软件无法顺利运作呢!
ldconfig 与 /etc/ld.so.conf
在了解了动态与静态函式库后,也知道我们目前的 Linux 大多是将函式库做成动态函式库之后,接下来要了解的就是,有没有办法增加函式库的读取效能?我们知道内存的访问速度是硬盘的好几倍,所以,如果我们将常用到的动态函式库先加载内存当中(cache),如此一来,当软件要取用动态函式库时,就不需要从头由硬盘里面读出啰!这样不就可以增进动态函式库的读取速度吗?没错,是这样的!这个时候就需要 ldconfig
与 /etc/ld.so.conf
的协助了。
如何将动态函式库加载高速缓存当中呢?
- 首先,我们必须要在
/etc/ld.so.conf
里面写下想要读入高速缓存当中的动态函式库所在的目录,注意,是目录而不是文件; - 接下来则是利用
ldconfig
这个执行档将/etc/ld.so.conf
的资料读入 cache 当中; - 同时也将数据记录一份在
/etc/ld.so.cache
这个文件当中;
使用 ldconfig
将动态函式库加载到内存中示意图:
ldconfig
常见使用选项如下:
ldconfig [-f conf] [-C cache]
-f conf:conf 指的是一个文件名,把 conf 作为函式库(libarary)的取得路径,而不使用默认值 /etc/ld.so.conf ;
-C cache:cache 指的也是一个文件名,使用 cache 作为函式库的缓存文件,而不是用默认值 /etc/ld.so.cache;
ldconfig [-p]
-p:列出目前加载到缓存的所有函式库信息(同 `/etc/ld.so.cache` 中的内容);
例:假设 Mariadb 数据库函式库在 /usr/lib64/mysql
中,如何读进 cache?
在 /etc/ld.so.conf.d/
下新建文件 *.conf
内容如下:
/usr/lib64/mysql
接着执行 ldconfig
命令,无输出信息。
接下来可以查看加载进 cache 的函式库信息,如下:
[root@localhost ~]# ldconfig
[root@localhost ~]# /usr/lib64/mysql/
-bash: /usr/lib64/mysql/: 是一个目录
[root@localhost ~]# ldconfig -p | grep mysql
libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.18
以
libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.18
为例,它描述的是函式库名称 => 函式库实际路径
。
通过如上步骤,我们可以将 Mariadb 的相关函式库给他读入缓存当中,这样可以加快函式库读取的效率。在某些时候,你可能会自行加入某些 Tarball 安装的动态函式库,而如果你想要让这些动态函式库的相关链接可以被读入到缓存当中,这个时候你可以将动态函式库所在的目录名称写入 /etc/ld.so.conf.d/*.conf
当中,然后执行 ldconfig
就可以啦!
程序的动态函式库解析:ldd
说了这么多,那么如何查看某个可执行的 binary 文件含有什么动态函式库呢?很简单,利用 ldd
命令就可以!先看一下 ldd
命令的使用:
ldd [-vdr] [filename]
-v:列出所有内容信息;
-d:重新将资料有遗失的link 点显示;
-r:将 ELF 有关的错误内容显示;
例 1:假如想要知道 /usr/bin/passwd
这个程序含有的动态函式库有哪些,可以这样做:
[root@localhost ~]# ldd /usr/bin/passwd
linux-vdso.so.1 => (0x00007ffcc99ba000)
libuser.so.1 => /lib64/libuser.so.1 (0x00007ff46df1f000)
libgobject-2.0.so.0 => /lib64/libgobject-2.0.so.0 (0x00007ff46dccf000)
...
例 2:如果我要找出 /lib64/libc.so.6
这个函式的相关函式库,可以这样做:
[root@localhost ~]# ldd -v /lib64/libc.so.6
/lib64/ld-linux-x86-64.so.2 (0x00007f3e3c5e0000)
linux-vdso.so.1 => (0x00007fffa6685000)
Version information:
/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
在上面的示例中,我们检查了 libc.so.6
这个在 /lib64
当中的函式库,结果发现他其实还跟 ld-linux-x86-64.so.2
有关!如果要知道该文件到底是什么软件的函式库,可使用 -v
这个参数,这样可以得知该函式库来自于哪一个软件。像上面的数据中,就可以得到该 libc.so.6
其实可以支持 GLIBC_2.3
等版本。
评论区