1.静态库
.a
作为文件后缀2.动态库
.so
作为文件后缀制作静态库有以下三个步骤:
将所有的 .c
文件编译生成 .o
文件。
打包成静态库
我们只提供所有的 .o 文件,别人可以直接链接使用吗?可以。因为链接的本质就将需要的 .o 文件链接起来。我们可以手动将所有 .o
文件链接:
虽然我们可以直接将所有 .o
文件链接生成可执行程序,但是当库中的.o文件很多的时候,使用会比较繁琐,整个文件也会比较冗余。因此我们可以使用ar
(archive) 指令将所有.o文件打包成静态库:
[注意]:库的名字也是有讲究的,需要以 lib 作为前缀,.a 或者 .so作为后缀
发布
使用一个库需要什么?头文件
和库文件
,因此我们分别将其添加到 /lib文件夹和/include文件夹下,作为我们的发布方式:
.c
文件编译生成 .o
文件-fPIC
选项,从而产生位置无关码,采用相对地址的方案确定动态库的位置 - shared
选项用于指定生成共享库大家都知道,包含头文件有以下两种方式:
在使用自实现的库时,包含头文件是一个大问题。因为往往我们所写的目标文件和库文件、头文件不在同一路径下,而且库文件和头文件通常会分别存储(参见发布方式),因此我们不能直接使用上述的两种包含头文件的方式。以下给出三种解决方案:
/usr/include
路径下,库文件则被存放在 /lib64
路径下 。编译器会自动去系统路径下寻找头文件和库文件,因此我们可以考虑将头文件和库文件添加到系统路径中。这本质就是在安装第三方库。-l
选项指定我们要链接的库:-l
和后面的库名之间,空格也有可五-l
指定的库名需要去掉lib
前缀和.a
后缀,因为在匹配的时候会自动加上。例如使用名为 libmydate.a
的库文件时,就需要写成-lmydate
但是不推系荐将我们日常写的库添加到系统路径下,因为有可能会污染系统目录的命名池。当然如果你水平很高,那也当然没问题。
当我们指定文件路径生成可执行程序后,尝试运时却遇到如下错误提示:
使用ldd
指令查看文件的依赖关系就可以发现问题所在:
为什么 .so
文件会找不到呢,我们明明指定了路径了啊?这里我们又需要区分一组概念了:
-I
-L
-l
都是 gcc 的选项,只与gcc有关下面提供四种解决方案:
/lib64
路径下查找对应库文件LD_LIBRARY_PATH
环境变量中查找需要的动态库路径,因此我们可以手动将库文件的路径添加到 LD_LIBRARY_PATH
中,最好添加绝对路径,这样在哪里都可以用。(:用于分隔不同路径)// 注意不要写成如下的形式,这样就将LD_LIBRARY_PATH的初始数据覆盖了
export LD_LIBRARY_PATH=xxx
配置系统文件
当我们重新启动xshell的时候,会发现我们之前追加的环境变量信息不见了。这是因为每次重启的时候,bash都会从配置文件中读取数据重新生成环境变量。
为了永久修改,我们可以尝试修改系统配置文件。操作系统除了在默认路径下搜索库文件外,还会遍历 /etc/ld.so.conf.d
下的所有配置文件,根据配置文件的内容找到目标库所在路径。
配置文件的内容也非常简单,就是库文件所在的路径
再使用 ldconfig
命令将配置文件加载到内存中,从而让配置文件生效。
在系统路径中创建库文件的软链接
软链接本质上就是快捷方式,使用 unlink
指令可以取消链接
[问题一]:为什么静态库不需要运行时查找?
答:使用静态库生成可执行程序时,库文件的代码已经拷贝到代码区了。因此不需要运行时查找。
[问题二]:为什么动态库需要运行时查找?
答:因为程序和动态库是分开加载的。(以下的讲解需要对进程地址空间有一定的理解)
当我们将程序加载到内存中时,OS为我们的程序创建进程控制块PCB,并建立起进程地址空间。因为我们的代码中用到了动态库,因而有部分代码是需要跳转到动态库中执行的。
要想跳转到动态库中,前提是将动态库加载到内存。既然要加载到内存中,动态库就必须要能被找到。动态库加载到内存后,通过页表将内存中的动态库映射到堆栈之间的区域,这个区域就叫做共享区
。
我们的代码被存放在代码区,当需要执行动态库中的代码时,跳转到共享区即可,执行完毕再跳转回代码区中继续向下执行。由此我们可以在自己的地址空间中执行完所有的代码(自己的和动态库的)。
整个内存中,虽然动态库只有一份,但是通过进程地址空间,动态库可以被多个进程所共享,因此可以大大节省内存空间。