我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。
两者对比:
| 动态库 | 静态库 |
编译时 | 不连接到目标代码 | 连接到目标代码 |
运行时 | 动态加载 | 不再需要(已经在目标代码内) |
动态库的生成和使用,静态库的生成和使用
假如编写一个数组相关的应用,大概代码框架如下:
/home/xiantao/binarysearchtree/
▾arrdata/一个关于创建数组的函数包括创建数组和打印数组,被test.c使用
arr.c
arr.h
test.c
▾ taolib/自己的一个调试宏的实现,arr.h中会使用这个
maclib.h
这次就把arr.c中的函数当成我们要用的到库,分别生成动态库和静态库来使用。
静态库的生成和使用:
1.编写源程序,上边已经有了,包括arr.c,arr.h和test.c这三个文件
下面是arr.h的内容
1 #ifndef_ARR_H_
2 #define_ARR_H_
3 #include"../taolib/maclib.h"
4 voidswap(DataType *a,DataType *b);
5 voidprintarr(DataType arr[],int len);
6 voidcreatearr(DataType arr[],int len);
7 #endif
2.编译,将arr.c编译成arr.o
xiantao@tao:~/binarysearchtree/arrdata$gcc -c arr.c此时生成arr.o
无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序arr.c通过gcc先编译成.o文件。
3.由.o文件创建静态库(.a)
xiantao@tao:~/binarysearchtree/arrdata$ar cr libarr.a arr.o此时生成libarr.a
静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为arr,则静态库文件名就是libarr.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。
第4步:在test.c中使用静态库
静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。
xiantao@tao:~/binarysearchtree/arrdata$ls注意此时有libarr.a这个静态库,可以没有arr.o
arr.c arr.h libarr.a test.c
编译test.c文件并测试
xiantao@tao:~/binarysearchtree/arrdata$gcc -o test test.c -L. -larr (注意此时时arr,而不是libarr)
以下三种方式都行
xiantao@tao:~/binarysearchtree/arrdata$gcc -o test test.c./libarr.a
xiantao@tao:~/binarysearchtree/arrdata$gcc -o test test.c libarr.a
xiantao@tao:~/binarysearchtree/arrdata$gcc -o test test.c arr.a
xiantao@tao:~/binarysearchtree/arrdata$./test
9 4 0 1 8 19 19 10
-L指示静态库的目录,-l指示静态库的名称,如果把libarr.a移动到/usr/lib/下,就不用加-L选项了,编译器会自动到那个目录下找到。
回头看以下,其实如果文件夹下只有三个文件arr.c,arr.h和test.c,然后编译:
xiantao@tao:~/binarysearchtree/arrdata$gcc arr.c test.c
测试也能成功。所以如果使用了静态库,我们只需要一个test.c源文件,在编译阶段加上静态库目录和名称就行;此时是在进行编译的时候,并没有编译arr.c这个文件,而只是在链接阶段使用到了libarr.a这个静态库。(不同于动态库的使用,它是在运行时和arr.so进行绑定的)。假如在运行./test之前删除这个静态库也是能成功运行的。
动态库到生成和使用:
1.源文件准备,与上边一样
2.编译生成arr.o
xiantao@tao:~/binarysearchtree/arrdata$ gcc-fPIC-c arr.c
如果在编译arr.c成arr.o的时候没有加上-fPIC这个选项,后面编译生成动态库时会出现以下这个错误:
/usr/bin/ld:arr.o: relocation R_X86_64_32 against `.rodata' can not be used whenmaking a shared object; recompile with -fPIC
arr.o: erroradding symbols: Bad value
collect2: error:ld returned 1 exit status
3.由.o文件创建动态库(.so)
xiantao@tao:~/binarysearchtree/arrdata$gcc -shared arr.o -o libarr.so
xiantao@tao:~/binarysearchtree/arrdata$ls
arr.c arr.h arr.o libarr.so test.c
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为arr,则动态库文件名就是libarr.so。用gcc来创建动态库。
也可以第二步第三步合起来编译:
xiantao@tao:~/binarysearchtree/arrdata[master]$ gcc -shared -o libarr.so arr.c (如果不加-fPIC选项会错现下列错误)
/usr/bin/ld: /tmp/cce8PdBO.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/cce8PdBO.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
正确编译方式:
xiantao@tao:~/binarysearchtree/arrdata[master]$ gcc -shared -fPIC -o libarr.so arr.c
4编译test.c,使用动态库
xiantao@tao:~/binarysearchtree/arrdata$ gcc -o test test.c -L.-larr (和静态库使用一样)
xiantao@tao:~/binarysearchtree/arrdata[master]$ gcc test.c libarr.so -o test(也行)
测试运行:
xiantao@tao:~/binarysearchtree/arrdata$./test
./test: error whileloading shared libraries: libarr.so: cannot open shared object file:No such file or directory
发现提示找不到这个动态库。其实linux上是有个默认到动态库的/usr/lib和/lib。如果把刚刚生成到liarr.so移动到/usr/lib/或者/lib/在运行./test,成功.
方法二,就是可以设置环境变量的:LD_LIBRARY_PATH。只要在这个变量中包含想要使用到动态库的目录就行。
方法三,修改/etc/ld.so.conf文件,把该库所在绝对路径添加到文件末尾,并执行ldconfig刷新
方法四,在第四步编译的时候,使用参数-Wl,-rpath参数告诉编译器,在编译生成test 可执行文件的时候,记住这个库的位置,以致于在运行的时候不需要在设置这个动态库的位置。当然,如果该动态库的位置最后改变的话汇报错,找不到相应的动态库(共享库):
./test: error while loading shared libraries: libarr.so: cannot open shared object file: No such file or directory
方法四扩展:可以使用ldd命令查看可执行程序(test)所依赖的动态库:
xiantao@tao:~/binarysearchtree/arrdata[master]$ ldd test
linux-vdso.so.1 => (0x00007fff463fc000)
libarr.so => /home/xiantao/binarysearchtree/arrdata/libarr.so (0x00007f60e9860000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f60e9485000)
/lib64/ld-linux-x86-64.so.2 (0x00007f60e9a64000)
假如把在编译生成test可执行文件后,删除libarr.so这个文件,再运行时,会出现什么结果,猜测会提示找不到共享库。试验以下,果然提示如下错误:
./test: error whileloading shared libraries: libarr.so: cannot open shared object file:No such file ordirectory。有点眼熟,其实就是没有设置好动态运行库到路径的提示错误一样,对于程序来说,也就是它找不到动态库到路径。
总结:
使用静态库编译生成的可执行文件大小要大于动态库的(必需的啊,毕竟动态库人家是在运行时动态加载使用的)。静态库使用时,只需要在编译阶段(test)使用,动态库需要再编译阶段(test)和运行(test)阶段都要有相应的设置,而且都要有相应的.so文件。
上边提到过一次:在编译test.c文件时,编译命令在两种情况下式样的,那假如目录下同时有动态库(libarr.so)和静态库(libarr.a)时,到底使用的时谁呢?测试一下:
同时编译两种库,然后运行test
xiantao@tao:~/binarysearchtree/arrdata$gcc -c arr.c
xiantao@tao:~/binarysearchtree/arrdata$ar -cr libarr.a arr.o
xiantao@tao:~/binarysearchtree/arrdata$gcc -fPIC -c arr.c
xiantao@tao:~/binarysearchtree/arrdata$gcc -o test test.c -L. -larr
xiantao@tao:~/binarysearchtree/arrdata$./test
./test: error whileloading shared libraries: libarr.so: cannot open shared object file:No such file or directory
然后再添加LD_LIBRARY_PATH
xiantao@tao:~/binarysearchtree/arrdata$LD_LIBRARY_PATH=./
xiantao@tao:~/binarysearchtree/arrdata$./test成功
说明如果同时有静态库和动态库,编译器是会默认使用动态库的。
同时,还需要注意以下更多的编译选项,-W -D,-rdynamic -Wl,-rpath
以下时引用别人博客
LIBRARY_PATH和LD_LIBRARY_PATH是Linux下的两个环境变量,二者的含义和作用分别如下:
LIBRARY_PATH环境变量用于在程序编译期间查找动态链接库时指定查找共享库的路径,例如,指定gcc编译需要用到的动态链接库的目录。
LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径,注意,LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找。
区别与使用:
开发时,设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。
发布时,设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。
GCC里的链接器的选项是-rpath和-rpath-link,看了下man ld,大致是这个意思:
GCC链接选项-L,-rpath-link和-rpath
-L: “链接”的时候,去找的目录,也就是所有的-lFOO选项里的库,都会先从-L指定的目录去找,然后是默认的地方。
-rpath_link (或者-rpath-link):这个也是用于“链接”的时候的,例如你显示指定的需要FOO.so,但是FOO.so本身是需要BAR.so的,后者你并没有指定,而是FOO.so引用到它,这个时候,会先从-rpath-link给的路径里找。
-rpath:“运行”的时候,去找的目录。运行的时候,要找.so文件,会从这个选项里指定的地方去找。对于交叉编译,只有配合--sysroot选项才能起作用。
也就是说,-rpath指定的路径会被记录在生成的可执行程序中,用于运行时。
-rpath-link则只用于链接时。