目录
如何编写一个库
静态库
引入 -- 静态库原理
首先,写两个程序,并且他们都有自己独立的头文件
如果我们直接将*.o 和*.h 文件给别人,那个人是否可以成功使用呢?
生成main.o后,在目录下有*.o文件和*.h文件
直接进行链接所有.o文件,成功生成my.exe:
可以看到!!这样的方式是可以让程序成功运行的!!!
因此,只有.o和.h,是可以形成可执行文件的!
而这就是静态库的原理
形成静态库
介绍
但是,给别人一堆.o文件也太挫了 ,所以可以将他们打包起来再发送给别人
而将.o文件打包起来的过程就叫做形成静态库
我们可以将这样一系列的操作就写入makefile中
ar
用于创建和管理静态库(archive)文件的命令
选项
c(create): 用于创建一个新的静态库文件,如果指定的库文件已存在,则会被覆盖
r(replace): 用于向现有的静态库中添加目标文件,如果库文件中已经包含要添加的目标文件,它们将被替换
t(table of contents): 用于列出静态库中包含的目标文件的名称
x(extract): 用于从静态库中提取指定的目标文件,将其复制到当前目录中
d(delete): 用于从静态库中删除指定的目标文件
q(quickly append): 用于快速追加目标文件到现有的静态库,通常比"r"选项更快
u(update): 用于仅在库中目标文件不存在的情况下将目标文件添加到静态库
s(write an object-file index into the archive): 创建或更新静态库文件的索引,以提高检索速度
A(don't rebuild the archive): 通常,"ar"命令会重建静态库,但使用"A"选项可以阻止它重建库文件。这通常用于加快操作速度。
v(verbose): 以详细模式运行"ar"命令,显示每个操作的详细信息,如添加、删除或提取文件
创建静态库
ar cr libmylibrary.a file1.o file2.o file3.o
添加文件到现有静态库
ar rs libmylibrary.a new_file.o
当我们有了库之后,该如何给别人呢?
首先,我们要保证要将.h文件和.o文件都打包给别人
所以可以创建两个文件夹分别存放两种文件:
将.a文件(打包.o文件后的)放入lib中,.h文件放入include中
经过一系列操作,创建出了mylib:
(这样我们就拥有了一个自己的库)
动态库
静态库和动态库链接的不同
静态库
- 在编译可执行文件时,编译器将静态库的目标代码直接嵌入到可执行文件中
- 它在链接阶段完成地址分配和链接(也就是绝对编址),所以它的体积会更大
- 每个可执行文件都包含了静态库的副本
- 因此它们在内存中是相互独立的,不共享同一个实例
动态库
- 动态库在运行时被加载到可执行文件的地址空间中 (共享区中)
- 在符号解析时,当遇到一个符号引用,动态链接器会查找动态库中是否有对应的符号定义
- 如果找到符号定义,动态链接器会确定该符号在动态库中的地址(使用相对编址的方式),然后将找到的符号定义的地址分配给对应的符号引用(通过页表映射)
被多个可执行文件共享
- 它通常以共享内存的方式加载到地址空间中
- (在内存中实际只有一份,但通过不同的页表,映射到不同的地址空间中),以节省内存:
创建
它需要在形成.o文件时,就和普通的编译做出差别
-fPIC选项 -- 编译
生成.o文件
-shared选项 -- 打包动态库
静态库打包是使用ar工具,但动态库是使用gcc + -shared选项
其他就和静态库差不多噜
如何使用库
静态库
库的安装
介绍
将库拷贝到系统的默认路径下,就叫做库的安装
实际上,安装就是拷贝
但是,一般不要将自己写的库加入到系统库文件中
- 不可靠,不安全,没有经过官方测试
- 很长一段时间后,你可能就忘了自己把写的库加入到系统中了,可能会不小心用了自己写的库
文件默认搜索路径
将自己的文件拷贝到系统路径
将自己打包的头文件/库文件拷贝到系统路径下:
问题
完成之后编译main.c,发现还是编不过
说是找不到这些函数符号:
- 是因为我们自己写的库文件,属于第三方库
- 需要在编译时指明你要链接的库名(因为系统库文件有很多很多,需要指明是哪一个)
解决 -- -l+库名
用于指定库文件
库名 -- 去掉lib前缀,去掉.a后缀后,剩下的部分
这样就完成了编译,我们的程序也就可以运行啦 :
指定所使用的文件路径
引入
直接将文件拷贝进系统文件,实在是风险很大
所以我们可以手动指定路径,而不用添加进系统路径中
-I -- 对头文件的搜索
在指定的路径下搜索:
-L -- 对库文件的搜索
在指定路径下搜索库文件,并且必须要指明是哪一个库 :
这样,我们就成功编译出了a.out
动态库
系统安装
和上面一样
gcc指定路径(成功了一半)
当我们lib中有同名的静态库和动态库,会默认使用动态库:
ldd指令
用于列出可执行文件或共享库的动态链接依赖关系
当两种库都存在时,如何链接静态库
-static
我们生成的文件可以成功执行
问题
如果不指定,使用动态库进行链接
会显示这个可执行文件无法找到这个动态库:
链接静态库可以成功运行,那为什么动态库不可以???
分析
- 原因就在于动态库在运行时才会进行链接
- 而运行这个过程不归编译器管
- 你告诉gcc动态库在哪里,只能让他成功编译,而不能执行
- 而链接是需要os中的加载器进行工作的
- 所以我们需要告诉加载器相关信息才对!!!
- 也就是下面的方式
解决 -- 更改LD_LIBRARY_PATH
介绍
是环境变量,用于指定动态链接库的搜索路径,让加载器知道它应该链接的库在哪里
但是!!!该环境变量只对当前会话及其子进程生效
使用
通过export设置这个环境变量(将自己实现的动态库路径添加进去)
这时候我们就可以成功执行了:
但是,设置环境变量只能在这次对话中生效,下次重新启动就没啦
所以我们需要使用别的一劳永逸的方法
配置/etc/ld.so.conf.d/
介绍
- 它包含了用于动态链接器的库文件路径配置文件
- 该目录中的每个文件都是一个文本文件,以.conf为后缀,用于指定要由动态链接器搜索的库文件路径
- 这些文件中的每一行都包含一个路径,动态链接器将按照文件中指定的顺序搜索这些路径以查找所需的共享库文件
- 当我们在系统中安装了一些自定义的库文件或者第三方软件时,我们可以通过创建一个新的.conf文件并将该路径添加到其中,让动态链接器能够找到这些库文件
使用
由于是系统配置文件,所以需要root权限在里面创建文件
除此之外,修改文件内容也得sudo
将我们的动态库路径写入mylib.conf中:
当我们重新执行,会发现此时怎么还是找不到我们的动态库?我们不是已经设置了吗?
ldconfig -- 更新配置文件
- 创建或修改/etc/ld.so.conf.d/目录下的文件后,需要使用这个命令来更新动态链接器的缓存
- 该命令将会重新读取配置文件,并更新动态链接器的缓存,使其能够正确地找到新的库文件路径
虽然我们添加进去了,但是必须得经过更新,链接器才能拿到新路径
包括我们将设置的文件删除,也需要更新:
更新后,使用ldd命令,a.out使用的动态库就可以被找到啦!
执行当然也没有问题啦!!!
建立软链接
在系统库文件中,建立与第三方库的软链接,也可以使可执行文件找到它的动态库
这时候也可以成功执行:
可以找到我们的动态库:
为什么要有库
使用者
- 可以大大减少开发的周期,提高软件的质量
- 毕竟发布出来的库都是经过严格测试的,自己写的可能没那么安全
- 而且一旦改动,会非常影响上层的软件
编写库的人
- 如果要分享给别人自己的代码,打包成库发送会非常简单(如果直接给源代码,太乱太难找了)
- 给的一般都是经过编译的文件,而不是源代码,因此别人无法窃取自己的代码(代码安全+保证自己的专利)