在这之前我们已经讲述了如何将自定义源文件打包并生成静态库,本文来带你了解如何打包成为动态库并使用
关于源文件的书写,以及相关库的知识的引入在上一篇“【Linux】如何将自定义源文件打包并生成静态库”已经说明过了,本篇就不再过多赘述。
动态库打包
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
跟静态库类似,我们也是要打包.o
文件,但是我们要增加一个fPIC
(产生位置无关码position independent code)选项
[AMY@VM-12-15-centos lesson_13]$ gcc -c -fPIC my_add.c my_sub.c
[AMY@VM-12-15-centos lesson_13]$ ll
total 28
-rw-rw-r-- 1 AMY AMY 110 Jun 25 20:55 my_add.c
-rw-rw-r-- 1 AMY AMY 64 Jun 25 20:47 my_add.h
-rw-rw-r-- 1 AMY AMY 1592 Jun 26 15:33 my_add.o
-rw-rw-r-- 1 AMY AMY 110 Jun 25 20:55 my_sub.c
-rw-rw-r-- 1 AMY AMY 64 Jun 25 20:49 my_sub.h
-rw-rw-r-- 1 AMY AMY 1592 Jun 26 15:33 my_sub.o
将.o
文件归档生成动态库:
[AMY@VM-12-15-centos lesson_13]$ gcc -shared -o libmymath.so my_add.o my_sub.o
[AMY@VM-12-15-centos lesson_13]$ file libmymath.so
libmymath.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked,
BuildID[sha1]=13ba29aec0c8343949d51a3690896ed7b071f867, not stripped
我们手动创建一个目录,将库文件与头文件放入一个目录下:
[AMY@VM-12-15-centos lesson_13]$ mkdir mylib
[AMY@VM-12-15-centos lesson_13]$ mkdir mylib/lib
[AMY@VM-12-15-centos lesson_13]$ mkdir mylib/include
[AMY@VM-12-15-centos lesson_13]$ mv libmymath.so mylib/lib/
[AMY@VM-12-15-centos lesson_13]$ cp *.h mylib/include/
[AMY@VM-12-15-centos lesson_13]$ tree mylib
mylib
├── include
│ ├── my_add.h
│ └── my_sub.h
└── lib
└── libmymath.so
2 directories, 3 files
我们将左边的目录归为使用者,右边的目录为开发者,现在我们lesson_13
目录下归整好的mylib
目录拷贝到test
目录下,这一操作相当于,使用者得到了这个静态库
动态库使用
按照我们上一篇详细讲过的如何使用库操作:
[AMY@VM-12-15-centos test]$ gcc -o mymath main.c -I ./mylib/include -L ./mylib/lib -l mymath
虽然这样我们生成了一个mymath
文件,但是我们直接运行:
[AMY@VM-12-15-centos test]$ ls
main.c mylib mymath
[AMY@VM-12-15-centos test]$ ./mymath
./mymath: error while loading shared libraries: libmymath.so:
cannot open shared object file: No such file or directory
错误:./mymath
:加载共享库时出现错误:libmymath.so
:不能打开共享目标文件原因是没有这样的文件或目录
我们查看一下mymath
用于打印程序或者库文件所依赖的共享库列表:
[AMY@VM-12-15-centos test]$ ldd mymath
linux-vdso.so.1 => (0x00007ffdf4deb000)
libmymath.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f29261ca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2926598000)
由上面可发现libmymath.so => not found
我们在静态库中看不到的现在在动态库能够看到,但是它显示not found
找不到这个库文件。
我们现在将我们所能提供的头文件库文件路径,以及库文件的具体名称都提供了,但是为什么这个库文件还是找不到呢?
我们是告诉了库文件头文件的路径以及库名称,但是这些都是告诉给gcc
编译器的,当我们把程序编译完以后和gcc已经没有关系了,程序运行起来,操作系统OS和shell也是需要知道库在哪的!!
错误原因:我们的库没有在系统路径下面,OS无法找到
如何告诉操作系统OS我们的库在哪:
1.增加环境变量方法
系统运行时搜索库,除了会在系统固定路径搜索,还会在环境变量中搜索
[AMY@VM-12-15-centos test]$ echo $LD_LIBRARY_PATH
:/home/AMY/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
将库所在的路径添加到LD_LIBRARY_PATH
环境变量中
[AMY@VM-12-15-centos test]$ pwd
/home/AMY/zt/test
[AMY@VM-12-15-centos test]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/AMY/zt/test/mylib/lib
添加完以后的环境变量,会多出当前库所在的路径:
[AMY@VM-12-15-centos test]$ echo $LD_LIBRARY_PATH
:/home/AMY/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/AMY/zt/test/mylib/lib
最终结果,我们发现libmymath.so
链接上了
[AMY@VM-12-15-centos test]$ ldd mymath
linux-vdso.so.1 => (0x00007ffd06ffe000)
libmymath.so => /home/AMY/zt/test/mylib/lib/libmymath.so (0x00007f914e427000)
libc.so.6 => /lib64/libc.so.6 (0x00007f914e059000)
/lib64/ld-linux-x86-64.so.2 (0x00007f914e629000)
运行结果:
我们这种添加库的路径到环境变量中的方法,一旦我们重新登陆shell,就会失效,因为每次登陆环境变量会回到默认,我们需要在系统文件中去更改,这个操作很麻烦。
2.配置.conf
文件
我们在这个目录下面,将当前库的目录随意写入任意.conf
文件
[AMY@VM-12-15-centos test]$ cd /etc/ld.so.conf.d/
[AMY@VM-12-15-centos ld.so.conf.d]$ ll
total 28
-rw-r--r-- 1 root root 26 Dec 16 2020 bind-export-x86_64.conf
-rw-r--r-- 1 root root 19 Aug 9 2019 dyninst-x86_64.conf
-rw-r--r-- 1 root root 24 Nov 5 2020 hcoll.conf
-r--r--r-- 1 root root 63 Dec 19 2020 kernel-3.10.0-1160.11.1.el7.x86_64.conf
-rw-r--r-- 1 root root 17 Oct 2 2020 mariadb-x86_64.conf
-rw-r--r-- 1 root root 22 Nov 5 2020 mxm.conf
-rw-r--r-- 1 root root 24 Nov 5 2020 sharp.conf
我们这里创建一个my.conf
文件,然后将之前库的路径写入my.conf
之中(我们需要使用sudo来提权,否则会失败)
[AMY@VM-12-15-centos ld.so.conf.d]$ sudo touch my.conf
[AMY@VM-12-15-centos ld.so.conf.d]$ sudo vim my.conf
[AMY@VM-12-15-centos ld.so.conf.d]$ cat my.conf
/home/AMY/zt/test/mylib/lib
我们还需要更新一下配置操作:
[AMY@VM-12-15-centos ld.so.conf.d]$ sudo ldconfig
现在我们就可以运行成功:
这种写法,我们重新登录还是可以继续正常运行
如果我们想要撤回刚刚的操作只需要删除这个文件,并更新一下:
[AMY@VM-12-15-centos ld.so.conf.d]$ sudo rm my.conf
[AMY@VM-12-15-centos ld.so.conf.d]$ sudo ldconfig
3.创建一个软连接在当前目录
[AMY@VM-12-15-centos test]$ ln -s ~/zt/test/mylib/lib/libmymath.so libmymath.so
[AMY@VM-12-15-centos test]$ ll
total 20
lrwxrwxrwx 1 AMY AMY 40 Jun 26 21:07 libmymath.so -> /home/AMY/zt/test/mylib/lib/libmymath.so
-rw-rw-r-- 1 AMY AMY 201 Jun 25 21:18 main.c
drwxrwxr-x 4 AMY AMY 4096 Jun 26 15:46 mylib
-rwxrwxr-x 1 AMY AMY 8432 Jun 26 15:58 mymath
[AMY@VM-12-15-centos test]$ ldd mymath
linux-vdso.so.1 => (0x00007ffd531ef000)
libmymath.so (0x00007fed48430000)
libc.so.6 => /lib64/libc.so.6 (0x00007fed48062000)
/lib64/ld-linux-x86-64.so.2 (0x00007fed48632000)
这样程序可以直接运行:
4.创建一个软连接在系统库目录
我们还可以将软连接建立在系统库目录中:
[AMY@VM-12-15-centos test]$ sudo ln -s ~/zt/test/mylib/lib/libmymath.so /lib64/libmymath.so
//下面是链接查看
[AMY@VM-12-15-centos test]$ ls /lib64/libmymath.so -l
lrwxrwxrwx 1 root root 40 Jun 26 21:12 /lib64/libmymath.so -> /home/AMY/zt/test/mylib/lib/libmymath.so
在系统库的目录下就具备一个软连接指向我们创建的库的路径,这样我们的libmymath.so
就有了链接
[AMY@VM-12-15-centos test]$ ldd mymath
linux-vdso.so.1 => (0x00007ffd4b161000)
libmymath.so => /lib64/libmymath.so (0x00007f2bb759f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2bb71d1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2bb77a1000)
静态库的加载
静态库是可在编译时链接到可执行文件的目标文件的集合。这意味着静态库中的代码将复制到可执行文件中,然后可执行文件可以在不存在静态库的情况下运行。
Linux 静态库的加载过程分为两步:
- 链接器将静态库链接到可执行文件。此过程涉及解析静态库中的所有符号,并将代码从静态库复制到可执行文件中。
- 然后加载并执行可执行文件。加载可执行文件时,操作系统会将静态库从磁盘加载到内存中。
静态库的优点:
它们比动态库更快,因为静态库中的代码被复制到可执行文件中。它们比动态库小,因为它们不需要包含有关如何在运行时加载库的任何信息。
静态库的缺点:
如果不重新编译可执行文件,则无法更新它们。它们可以使可执行文件更大。
动态库的加载
动态库是在运行时加载到内存中的库。这意味着动态库中的代码不会复制到可执行文件中,并且只有在存在动态库时,可执行文件才能运行。
Linux 动态库的加载过程:
- 操作系统会在预定义的搜索路径中查找动态库。查找到以后将其加载到内存中,加载过程中会分配内存,并将动态库的代码和数据段读入内存中。此时,动态库还没有关联到任何一个进程,因此这段内存是只读的。
- 在动态库加载到内存中后,还需要进行重定位。这是因为动态库中的函数和变量引用的地址是相对地址,需要根据实际的内存地址进行修正。这个过程会使用到内存中的页表,将动态库的相对地址映射到实际的内存地址上。
- 在重定位完成后,动态库就可以被映射到进程的地址空间中。这个过程会使用到虚拟内存机制,将动态库的代码和数据段映射到进程的地址空间中。这个过程会使用到页表,将动态库的虚拟地址映射到实际的物理地址上。
- 一旦动态库被映射到进程的地址空间中,就可以通过调用动态库中的函数来使用其中的功能了。
动态库的优点:
- 节省内存:动态库在运行时才会被加载到内存中,而在程序未运行时并不会占用内存。
- 可以共享:动态库可以在多个进程之间共享,因为它们的代码是在运行时才被加载。这样可以减少磁盘空间的占用,提高系统效率。
- 可以动态更新:动态库可以在运行时被更新,而不需要重新编译整个程序。这样可以方便地修复程序中的错误,或者升级程序中的功能。
动态库的缺点:
- 运行时开销:动态库在运行时需要进行加载和链接,这会增加一些运行时的开销。
- 难以分发:由于动态库需要在运行时动态加载,因此在分发程序时需要同时分发动态库,这可能会增加分发的难度。
如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀