一.基础
在实际开发中,对于一些需要被许多模块反复使用的公共代码,我们通常可以将它编译为库文件。
库从本质上来说就是一种可执行的二进制格式,可以被载入内存中执行。在linux系统中,库是以文件形式的,并且可以分为动态链接库和静态链接库。静态链接库文件的后缀名.a ;动态链接库文件的后缀名.so
动态和静态二者的区别在于代码被载入的时刻不同。静态是编译时直接连接到目标代码中,动态则是运行时加载的。
gcc编译方面基础:
二.静态库
2.1、基本概念
当有程序使用某个静态库时,在链接步骤中,链接器将从静态库文件中取得的代码复制到生成的可执行文件中,即整个库中的所有函数都被链接到可执行文件中。优点:可以不在使用库就可以运行;缺点:可执行文件大,而且不利于更改升级,升级必须重新编译。而动态库就不需要,直接替换掉动态库即可,但需要注意的事可执行程序必须能够找到这个动态库。
静态标准库Gcc安装
2.2、静态库的创建和使用
(1)编辑一段小程序(helloLib.c / helloLib.cpp)
#include <iostream>
using namespace std;
void helloLib(int age)
{
cout << "you age is " << age << endl;
}
(2)通过gcc(gcc -c helloLib.c / gcc -c -lstdc++ helloLib.cpp ) / g++( g++ -c helloLib.cpp)生成二进制.o文件
(3)用ar归档目标文件,生成静态库 ar rcs libhelloLib.a helloLib.o
*)ar 常见用法 ar [option] libxxx.a xx1.o xx2.o xx3.o .....
常用option选项:
*①选项c:用来创建一个库
*②选项s:创建目标文件索引,这在创建较大的库时能加快时间。如果不需要创建索引,可以改成大写S参数;如果.a文件缺少索引,可以直接使用ranlib命令添加
*③选项r:在库中插入模块
*④选项t:显示库文件中有哪些目标文件,只显示名称
*⑤选项tv:显示库文件中有哪些目标文件,显示的信息包括权限、时间、大小、文件名
(4)配合静态库写头文件,文件里的内容就是提供给调用者使用的函数、变量和类的声明
实例使用上面编译的静态库
main.cpp
extern void helloLib(int age);
int main()
{
helloLib(100);
return 0;
}
命令编译这个文件 g++ -o main main.cpp -L. -lhelloLib
-L. 是用来告诉g++去哪里找到这个库,它后面加了一个点,则代表在本目录
-l 是用来指定具体的库
-lhelloLib 后面的helloLib ,其中lib和.a不用指定,g++或者gcc会自动的去找库文件
三.动态库
3.1、基本概念
动态库又称为共享库。这类库名称一般为libxxx.M.N.so, xxx为库的名字,M是库的主版本号,N是库的次版本号,也可以不要版本本,如libxxx.so。
相对于静态库,动态库在编译的时候并没有被编译进目标代码中,我们的程序在执行到相关函数时才调用该动态库里的相应的函数,因此动态库所产生的可执行文件比较小。
由于没有整合到你的程序中,所以程序运行时需要动态的申请并调用,所以程序的运行环境中必须提供相应的库。
动态库文件的改变并不改变你的程序,所以动态库的升级比较方便。
静态库是链接器找出程序需要的函数,将它们复制到执行文件,由于这种复制的完整的,因此一旦连接成功,静态程序库就不需要了。但对于动态库而言,就不是这样的,动态库会在执行程序内留下一个标记,指明当程序执行时,首先必须载入这个库。由于动态库省空间,linux下进行链接的默认操作是动态库,也就是说同时具有动态库和静态库,默认是将与动态库相连接的。
3.2、静态库的创建和使用
(1)编辑一段小程序(test.cpp)
#include <iostream>
using namespace std;
void fun(int age)
{
cout << "you age is " << age << endl;
}
(2)通过gcc/g++生成
g++ test.cpp -fPIC -shared -o libtest.so
-shared:表明产生共享库
-fpic:表明使用地址无关代码。在linux下必须加上此参数,不然就使用了绝对地址等,这样就不是多进程共享了。
原因具体说明:共享库文件可能会被不同的进程加载到不同的位置上,如果共享对象中的指令使用了绝对地址、外部模块地址,那么在共享对象被加载时就必须根据相关模块的加载位置对这个地址进行调整,也就是修改这些地址,让它在对应的进程中能正确访问,这样就不能实现多进程共享一份物理内存。共享库在每个进程中都必须有一份物理内存的复制(共享的是内存地址),fPIC指令就是为了让使用同一个共享对象的多个进程能尽可能多地共享物理内存,它背后把那些涉及绝对地址、外部模块地址访问都抽离出来,保证代码段的内容可以多进程相同,实现共享。
(3)测试使用(2)中编译的动态库
extern void fun(int age);
int main()
{
fun(100);
return 0;
}
编译 g++ main.cpp -o main -L. -ltest
直接测试main可执行程序运行,会发生错误,因为找不到动态库;
(a) 虽然动态库libtest.so和main 在一个目录下,但还是找不到,动态库是系统路径进行寻找的,所以就需要此文件所在的路径加入系统环境变量path内,或者把libtest.so拷贝到系统path(/us/lib)路径下
(b)或者临时在终端上添加临时环境变量,但只对此次命令有效
LD_LIBRARY_PATH=/home/demo/ ./main
然后运行main即可
(c)修改系统配置文件 /etc/ld.so.conf文件,修改后执行以下ldconfig保险一些。
将程序路径放入此文件。一劳永逸