"我看到,久违的晴朗啊"
一、什么是动静态库
在本栏目前面的篇幅也提到过这个概念,因此本小节就小小地回顾一番。
在linux下:
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
一个库的名称为:去掉前缀lib去掉后缀.a(.so)
在windows下;
静态库(.lib)
动态库(.dll)
二、如何制作静态库
(1)理解.o文件
在开介绍如何制作动静态库之前,我们不得不提一嘴
程序从代码编译生成可执行程序分为四个阶段:
预处理 ---> 编译 ---> 汇编 --->链接
分别对应GCC编译器选项的 -E -S -C
生成的文件分别为: .i(头文件展开源码) .s(汇编码) .o(可重定位的二进制目标文件)
我们写了一个my_add.c my_sub.c 功能是实现加法和减法。
gcc -c + file.c 可以生成同名的 file.o 可重定位二进制文件
gcc -o 目标文件 源文件
链接后形成可执行程序!这不就是我们平常只需要使用GCC直接就可以生成 可执行程序吗?
只不过我们手动对.o文件进行了链接;
因此,如果不想给对方提供自己的源文件,源代码,那我是否提供一个.o文件就行了呢?是的!
未来我们可以为用户提供.h(有什么方法),.o(方法的实现)。
但是,在实际中不仅仅如我们上面的例子,那少得可怜的.o\.h文件。如C库中的printf、scanf、max、pow……于是,我们就会尝试将所有的".o文件"打一个包,给用户提供这个包即可。
因此,库文件的本质是.o文件的集合。
(2)如何打包静态库
1.生成.o文件集合
liunx中的ar命令:
ar命令是Linux的一个备份压缩命令,可以创建、修改备存文件(archive),或从备存文件中抽取成员文件。备存文件以一定的结构打包一个至多个其它文件(即成员文件),且成员文件的内容、模式、时间戳等信息将被保存在备存文件中。
参数(例举):
r :在库中插入模块(替换)。
当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar 显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
c :创建一个库。
Makefile;
libmath.a:my_add.o my_sub.o
ar -rf $@ $^ #静态库打包生成集合文件
my_add.o:my_add.c
gcc -c my_add.c -o my_add.o
my_sub.o:my_sub.c
gcc -c my_sub.c -o my_sub.c
#发型版本
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp -rf *.a mylib/lib
cp -rf *.h mylib/include
.PHONY:clean
clean:
rm -rf *.o libmath.a mylib mymath
于是我们也就制作了一个静态库了。
2.压缩与解压
我们也可以对mylib进行压缩。
tar -cf archive.tar foo bar
# Create archive.tar from files foo and bar.
tar -tvf archive.tar
# List all files in archive.tar verbosely.
tar -xf archive.tar
# Extract all files from archive.tar.
压缩;
解压;
(3)如何使用静态库?
此时我们用gcc进行编译;
但编译器找到不到路径,就算我们mylib里有那些方法的声明。
此时我们给编译器带上选项"-I" 指定寻找,但是它拿到了声明,但是没有方法的实现。
我们带上"-l" 指定库名!但是为什么仍然ld失败呢? 因为,库文件的名字:去掉前缀去掉后缀!
此时,我们打包的静态库终于可以被使用了~
(4)系统库路径
这不对啊!我们平常使用gcc\g++的时候都没有带上这些库路径和库名称。上面的实验则证实了,一旦要连接第三方库,一定需要指定库名称。然而事实上是,gcc\g++只会认识它们自己的C\C++标准库,并且gcc\g++的链接行为,默认是动态链接的。
这也是为什么我们上面的可执行程序是"dynamically linked"
系统include;
此时我们将my_add.h \ my_sub.h拷贝进这个路径下;
系统lib;
我们把自己的lib库拷贝进行库中;
我们在用gcc编译;
由此,我们不仅仅完成了对静态库的打包、使用。
同样也对yum有了一个新的理解。当我们通过yum源,在网上获取数据,下载下来的库、包,本质就是将文件的内容,拷贝赋值到系统库、路径下。这就叫做安装!
三、如何制作动态库
(1)制作动态库
制作动态库gcc可以提供了制作动态库的操作;
gcc -FPIC:生成的.o文件是位置无关的
gcc -shared lib.so file.o
此时我们生成的libmymath.so是一个动态链接的共享文件;
(2)如何使用动态库
我们也就根刚刚一样,去编译这个文件。
但是我们会发现就算生成了可执行程序,但是根本运行不起来! 我不是都告诉了库的头文件和.o文件吗??
我们是给谁的说的? gcc~
可是当程序被编译完后,还和gcc有关系吗? 没有! 那么是谁找不到呢? 操作系统!
当程序运行起来,OS需要找到动态库并将其中的代码 loading进程序内。而你的库没有在系统路径下,Shell 与 OS根本找不到!
1.环境变量搜索(LD_LIBRARY_PATH)
我们此时就需要导入 第三库的路径和lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wsy/workfile_new/lesson22/test
我们此时运行程序,也就没有行了~
但是这种环境变量的方法,仅仅是用于测试可行。仅仅是在本次登录有效。
2.etc配置
如何保证永久有效呢?直接拷贝入系统路径下,这显然是不用再多说的,不过也可以借用etc进行配置;
cd /etc/ld.so.conf.d
我们随便创建一个文件,并把动态库的路径写入进去。
ldconfig 更新
于是就可以找到这个动态库了。
3.软链接
当找动态库的时候,默认也会在当前路径,同级目录寻找。但注意,需要软链接是,是库文件名。
我们同样也可以将软链接 建立在系统路径下。效果也是一样。也就不在此多说了。
四、如何理解动静态库?
(1)静态库
(2)动态库的加载
由此,可见,进程地址空间的概念是操作系统最重要的!它不止可以看到进程内部自己的资源,也可以访问外部的库资源等。
总结:
linux下静态库以.a结尾,动态库以.so结尾。
库的本质是一堆.o可重定位二进制文件的集合。
静态库打包 ar -rc
需要链接第三方库: -I(指名include) -L(库) -l(库名)
gcc有一套制作动态库的方法 gcc -fPIC(生成位置无关 这与后面加载映射关系很大) gcc -shared生成动态库
告诉gcc动态库 != 告诉OS。有三种方法可以实现OS找到动态库在哪里:1.LD_LIBRARY_PATH 2.etc配置 3.软链接
动静态库加载细节
本节到此结束,感谢你的阅读,
祝你好运~向阳而生