目录
一、什么是库
二、为什么要有库
三、库的命名
四、静态库的形成原理
4.1 使用ar指令创建库文件
4.2 编译时声明要使用的第三方库
4.3 打包库文件
五、静态库的使用
5.1 在系统默认搜索路径外使用静态库
5.1.1 在gcc/g++指令中使用-I选项声明头文件路径
5.2 在系统默认搜索路径中使用静态库
六、动态库的形成原理
6.1 形成与位置无关码的二进制文件
6.2 打包形成动态库文件
6.3 形成库文件的压缩包
七、动态库的使用
7.1 修改环境变量LD_LIBRARY_PATH(临时方案)
7.2 在库搜索路径下添加指向动态库的软链接
7.3 配置文件
我们写代码时都要包含头文件,头文件内有方法的声明,但光声明没什么用,我们还要找到这些方法实现的方式,关于具体的实现方法被存在一个叫库的地方
在Linux下有动态库和静态库两种库(其中以.a为文件后缀的是静态库,以.so为文件后缀的是动态库):
所以我们使用一门计算机语言来写程序时,需要下载其对应的头文件和动静态库(即我们常说的环境搭建的一部分)
当然是为了提升开发者的效率
如果我们没有库,我们每想用一个最基本的printf函数都要自己去手动实现一下,这不是太费劲了吗?造轮子只适合我们学习原理,但并不适合用来开发
不管是动态库还是静态库,其命名都有下面的一套规则:
lib+库名+. +文件后缀(so/a+版本号)
下面我们拿两个库名来举个例子:
libc.a libstdc++.so.6
我们把lib和文件后缀去掉看看:
libc.a libstdc++.so.6
所以这两个库名分别为c和stdc++
下面我们写两段简单的代码来实现一下库的原理:
现在我们实现了整数的加减这两个功能,现在得想想办法将自己写的功能交给其他人用
直接将源码交给别人?
是自己信任的人当然可以,不过在公司层面上因为安全和版权问题并不会这样干!
那我们可以先将我们自己写的源文件汇编成二进制文件目标文件(二进制文件不会暴漏源码),再将头文件和二进制文件目标文件交给别人,别人想要使用我们自己写的功能联合编译一下不就行了嘛:
完美运行
但是一下子给别人这么多文件感觉不太方便哎,我们能不能把这些.o文件(目标文件)打包成一份发给别人呢?(别人调用功能时需要查看头文件,因此头文件不适合打包)
ar指令可让我们集合许多文件,成为单一的备存文件。在备存文件中,所有成员文件皆保有原来的属性与权限:
指令参数
-d | 删除库文件中的成员文件。 |
-m | 变更成员文件在库文件中的次序。 |
-p | 显示库文件中的成员文件内容。 |
-q | 将文件附加在库文件末端。 |
-r | 将文件插入库文件中。 |
-t | 显示库文件中所包含的文件。 |
-x | 自库文件中取出成员文件。 |
选项参数
a<成员文件> | 将文件插入库文件中指定的成员文件之后。 |
b<成员文件> | 将文件插入库文件中指定的成员文件之前。 |
c | 建立库文件。 |
f | 为避免过长的文件名不兼容于其他系统的ar指令指令,因此可利用此参数,截掉要放入库文件中过长的成员文件名称。 |
i<成员文件> | 将文件插入库文件中指定的成员文件之前。 |
o | 保留库文件中文件的日期。 |
s | 若库文件中包含了对象模式,可利用此参数建立备存文件的符号表。 |
S | 不产生符号表。 |
u | 只将日期较新文件插入库文件中。 |
v | 程序执行时显示详细的信息。 |
V | 显示版本信息。 |
下面来演示一下:
接下来看看能不能成功编译:
咦?编译器找不到库中的二进制文件,我们自己打包的库文件属于第三方库,编译器并不能识别,所以我们在编译时要声明:
在gcc指令下使用-L选项声明第三方库所在的路径,使用-l选项声明第三方库的库名:
但是我们去一些官网下载库文件, 我们下到的是.tar的压缩文件,该文件内有一个库文件存放的目录和一个头文件存放的目录,以及一些说明性文件
现在我们来实现一个库文件的打包:
这样子一个mymain.tgz的库文件压缩包就被我们打包好了
如果一个使用者拿到了我们打包的库压缩文件,首先要做的就是解压了(对于指令不熟悉的同学可以看到这里:【Linux】常用基本指令详解):
然后就是编译了:
咦,这次编译是找不到头文件了,因为我们要使用的头文件并不在当前目录下,而是存放在include目录下
我们可以在gcc指令中使用-I选项声明要使用的第三方头文件路径:
编译成功,完美运行~
如果静态库没有默认安装到系统gcc、g++默认的搜索路径下,用户必须指明对应的选项,告知编译器: 头文件在哪里、库文件在哪里、库文件具体的名称
上面的方法比较麻烦,我们还可以将我们下载下来的库和头文件,拷贝到系统默认路径下:
这时再去编译:
这里需要注意的是,我们安装的库是第三方的(除语言本身接口、操作系统系统接口之外的库),要想正常使用,即便是已经全部安装到了系统中,gcc/g++必须用-l指明具体库的名称!
所以对任何软件而言,安装/卸载的本质就是到系统特定的路径下添加/删除文件
但是安装到系统中,我们安装大部分指令,库等等都是需要sudo的或者超级用户来操作的
要形成动态库的第一步,首先需要将用到的源文件编译形成与位置无关码(我们在后面解释这是什么)的二进制文件,在gcc/g++指令后面加上-fPIC选项即可:
有了与位置无关码的二进制文件后,我们再用gcc/g++指令加上-shared选项将它们打包形成一个动态库文件:
接下来我们就按照打包静态库的方法来将我们形成的动态库文件和头文件打包成为一个压缩包:
这样一个动态库的压缩包文件我们就打包好了
下面又一个使用者拿到了我们打包的动态库压缩文件,废话不多说,先解压:
再来按先前的方法编译:
成功了,运行一下试试看:
?报错了?我们不是已经告诉gcc动态库的位置了吗?怎么还找不到目标文件呢?
这是因为我们告诉的是gcc,它是个编译器,所以编译并没有报错,但是我们运行程序时是操作系统进行的,OS可不知道库在哪里
那静态库怎么就可以运行呢?
静态库的链接原则是:将用户使用的二进制代码直接拷贝到目标可执行程序中。
但是动态库不会!所以我们最终形成的可执行程序没有库中的二进制代码,导致了运行报错。
那动态库形成的可执行文件要怎么运行呢?
我们Linux有个环境变量LD_LIBRARY_PATH,存储的是系统查找动态库的路径
所以我们将我们动态库所在的路径添加到该环境变量中即可:
运行一下:
成功了
但是这个方法只是一个临时方案,我们修改后的环境变量重新登录就没了
我们可以在系统默认搜索的库路径下添加一个软链接来指向我们写的动态库:
运行一下:
我们Linux系统下有一个文件:/etc/ld.so.conf.d/
这个文件中记录的都是一些路径,系统会通过这些路径查找所需要链接的动态库
所以我们可以向该目录中添加我们动态库所在目录的文件(该文件后缀为.conf)即可:
添加完后使用ldconfig指令使配置文件生效即可:
该期博客后期会继续更新,敬请期待~