linux动态库和静态库
设计库的目的
1)程序更加简洁,不需要维护太多的源文件
2)保护三方厂商的知识产权
gcc常用指令
复习一波gcc的常用指令:
-E :仅执行预处理(不要编译、汇编或链接)。
-S :只编译(不汇编或链接)。
-c :编译和汇编,但不链接。
-o <file> :指定输出文件。
-pie :创建一个动态链接、位置无关的可执行文件。
-I :指定头文件的包含路径。
-L :指定链接库的包含路径。
-shared :创建共享库/动态库。
-static :使用静态链接。
-fPIC/fpic :生成与位置无关的代码。
-std :指定语言版本,如-std=c++11,gcc默认的语言为c++11。
--help :显示帮助信息。
--version :显示编译器版本信息。
静态库
linux静态库一般以lib为前缀,以.a为后缀,中间是自己定义的库的名字,即最终静态库的名字为:
libxxx.a
静态库的制作:
借用网上的一张图:
要先经过三个步骤,预编译、编译、汇编
该三个步骤可以通过gcc -c来直接完成。获取到二进制格式的目标文件 .o 格式,然后通过linux提供的ar工具将目标文件进行打包就可以获得静态库libxxx.a文件了。
ar工具相关参数说明(ar --help):
Usage: ar [emulation options] [-]{dmpqrstx}[abcDfilMNoPsSTuvV] [--plugin <name>] [member-name] [count] archive-file file... ar -M [<mri-script] commands: d - delete file(s) from the archive m[ab] - move file(s) in the archive p - print file(s) found in the archive q[f] - quick append file(s) to the archive r[ab][f][u] - replace existing or insert new file(s) into the archive s - act as ranlib t - display contents of archive x[o] - extract file(s) from the archive command specific modifiers: [a] - put file(s) after [member-name] [b] - put file(s) before [member-name] (same as [i]) [D] - use zero for timestamps and uids/gids (default) [U] - use actual timestamps and uids/gids [N] - use instance [count] of name [f] - truncate inserted file names [P] - use full path names when matching [o] - preserve original dates [u] - only replace files that are newer than current archive contents generic modifiers: [c] - do not warn if the library had to be created [s] - create an archive index (cf. ranlib) [S] - do not build a symbol table [T] - make a thin archive [v] - be verbose [V] - display the version number
举个例子,创建一个静态库
准备的文件如下:
. ├── include │ └── test.h └── test.c
// test.h #include <stdio.h> void test();
// test.c #include "include/test.h" void test() { printf("hello world"); }
1)将源文件进行汇编,生成test.o文件
gcc -c test.c -I include/
2)用ar工具进行打包成静态库
ar rcs libtest.a *o
3)将libtest.a静态库和test.h文件发布测试
// main.c int main() { test(); }
将libtest.a、test.h、main.c放置于一个目录下,使用
gcc main.c -o main.out -L ./ -ltest
便可生成main.out的可执行文件
动态库(共享库)
动态链接库是程序运行加载时的库,运行的多个程序可以使用同一个加载到内存中的动态库,动态库也称共享库,后缀为.so,即shared object的缩写。动态库是有执行权限的,静态库没有执行权限。
linux动态库的命名规则:
libxxx.so
生成动态库是直接使用gcc命令并且需要添加-fPIC(-fpic)以及-shared参数。
-fpic称之为与位置无关的代码,与位置无关的意思就是说,进程是磁盘上运行的一个执行程序,执行几个执行程序就会得到一个虚拟地址空间,在这个虚拟地址空间中,需要加载一些代码,如果是静态库,就会直接打包到执行程序中,因此静态库的代码就会直接放在代码区,如果程序中用的是动态库,库里面的代码不会放到代码区的,会放到动态库加载区,动态库加载区的代码是随着程序的运行并且调用到库里面函数的时候才会加载代码,因此不同的进程中,如果说要调用这个动态库文件,对应的代码的位置都是不一样的,加了-fpic之后我们调用的这个库函数对应得代码在虚拟地址空间中用的是一个相对地址。如果是静态库,对应的代码都是绝对地址,静态库的文件已经被打包到应用程序中去了,所以代码的位置已经固定了。
借用网上的一张图:
与制作静态库的步骤基本类似,准备的文件一致。
1)将源文件进行汇编,生成test.o文件
gcc -c -fpic test.c -I include/
2)gcc -shared生成动态库
gcc -shared *o -o libtest.so
3)将libtest.so动态库和test.h文件发布测试,新建main.c如静态库第三点。
生成可执行程序
gcc main.c -o main.out -L ./ -ltest
在该目录下就可以执行main.out执行程序了
换个目录就可能执行不了了,原因分析:
通过gcc main.c -o main.out -L ./ -ltest生成的main.out执行程序只包含这个动态库的名称已经调用程序的源代码,因此执行可执行程序的时候只有调用程序的源代码加载到进程的代码区。动态库的代码随用随加载,不调用就不会加载到进程中。
解决方式可以将libtest.so文件放到存储动态库的系统目录/lib/下就可以运行了。
关于动态链接器和相关解决方案可以参考:
5、extern "C"
我们修改一下第四点中的main.c文件为main.cpp,并修改其内容为:
#include <iostream> int main() { test(); }
用g++链接第四点中生成的libtest.so动态库:
g++ main.cpp -o main -L . -ltest
会出现以下的报错:
我们可以得出:
c++中不能直接调用c编译器编译的代码,我们需要对test()函数进行提前声明,修改main.cpp为:
#include <iostream> extern "C"{ extern void test(); } int main() { test(); }
告诉编译器,将接下来的代码当成c语言来进行处理,目的是支持c++和c的混合编程。