动静态库的定义
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
静态库的制作
例子:将自己编写的my_sub.c、my_add.c具体实现文件和my_sub.h、my_add.h头文件生成静态库,首先在makefile文件中编写以下代码
libmymatch.a:my_sub.o my_add.o
ar -rc $@ $^
my_sub.o:my_sub.c
gcc -c -o my_sub.o my_sub.c
my_add.c:my_add.c
gcc -c -o my_add.o my_add.c
在上述文件所在目录,执行make语句,会将my_sub.c和my_add.c通过gcc -c命令,分别编译生成.o文件,再通过ar -rc将两个.o文件打包成一个.a文件(注意.a文件的开头以lib开头,中间是自己给的命名)。至此,我们生成了静态库libmylib.a。这之后在makefile中编写下面代码
.PHONY:output
output:
makedir -p mylib/lib
makedir -p mylib/include
cp *.h mylib/include
cp *.a mylib/lib
在上述文件所在目录,执行make output语句,将会创建一个mylib文件夹,并在其中创建lib和include文件夹,然后分别把刚才生成的.a和.h文件放入对应文件夹中。
静态库的使用
使用者拿到了我们生成的静态库,mylib之后,在自己的程序main.c中正常包含头文件,使用库中函数。将mylib放在与main.c相同路径下(也可以放在其他路径,接下来的步骤只需根据该种情况进行修改即可)。
gcc -o mymath main.c -I./mylib/include -L./mylib/lib -lmymath
-I./mylib/include:头文件路径,如果未标明,只会在默认路径或当前路径进行搜索,所以我们要标明头文件所在位置。
-L./mylib/lib:库路径未标明也只会在默认路径下进行搜索,所以要指明库路径
-lmymath:一个库路径下,还可能有多个库文件,还需要指明到底是那一个库,注意要去掉开头的lib和后缀.a
动态库的制作
libmymath.so:my_sub.o my_add.o
gcc -shared -o $@ $^
my_sub.o:my_sub.c
gcc -fPIC -c -o my_sub.o my_sub.c
my_add.c:my_add.c
gcc -fPIC -c -o my_add.o my_add.c
.PHONY:output
output:
mkdir -p mylib/lib
mkdir -p mylib/include
cp *.so mylib/lib
cp *.h mylib/include
在命令行输入make,会将my_sub.c和my_add.c分别生成.o文件,加上-fPIC选项,表示的是生成与位置无关码。之后使用gcc自带的打包工具,加上-shared选项,表示生成共享库格式。即可生成动态库库libmymath.so。
之后,我们输入make output,创建一个目录mylib,在其中创建两个目录lib和include,分别将.so文件和.h文件放入对应的文件夹中。未来使用者拥有了mylib整个文件夹后,就可以使用该动态库。
动态库的使用
对于编译阶段,与静态库的使用完全一样
gcc -o mymath main.c -I./mylib/include -L./mylib/lib -lmymath
但是如果直接运行,会出现以下情况结果显示,无法找到动态库。虽然我们在编译阶段已经告诉了gcc,我们使用的库的路径和库名称。但是程序一旦编译完成,和gcc就脱离了关系。而程序一旦运行起来,操作系统和shell也需要知道库在哪里,而我们的库又不在系统路径下,所以OS无法找到。
这里我们提供两种运行程序的方法:
①将.so库文件放到系统默认路径下,一般是/usr/lib
②运行下面三行Linux命令,更改LD_LIBRARY_PATH,编译程序,运行程序。但是此方法不进行特殊设置的话,重启后会失效
export LD_LIBRARY_PATH=.
gcc main.c -lmymath
./a.out
动静态库的加载原理
下面附待两张自己画的原理图和简单说明。
静态库的加载,在程序编译时期,把对应的静态库代码拷贝到程序中,最后跟随程序一同加载到内存中的代码区动态库的加载,在编译时期,不会将库的代码直接拷贝,而是只将指定函数的地址(gcc是生成的与位置无关码),写入到可执行程序中。该地址可以看做是对应库起始位置的偏移量。待程序加载到内存中运行时,动态库也加载到内存,通过页表映射到共享区。库有了实际的物理位置之后,起始位置确定,通过保存的函数的偏移地址,就可以找到库中对应函数的具体位置。