引言
前面我们讲解了什么是函数库(函数库就是一些事先写好的函数集合),函数库有什么作用(可以打包我们的编写的源代码,供他人使用,同时源码不可见,保护了自己的知识产权)以及函数库有2种提供方式:静态链接库和动态链接库,前面2篇分别讲解了如何在linux中使用gcc工具链和windows中keill armcc工具链制作我们静态链接库,今天我们继续来说说如何在linux gcc制作动态链接库。
为什么不在Keil中制作动态库
我们知道动态库往往应用在多个程序运行,就单个程序而言的话,不如使用静态链接库来的效率直接,因为不管动态库还是静态库,最终都是要被加载运行的,只是动态库被加载一份,而静态库随着程序的个数重复加载。 而一般在Keil环境中开发的,多是单片机,不管是裸机还是RTOS(实时操作系统,如RT-Thread, ucosii/iii,freertos等),都是单程序运行, 因此基本上很少在单片机中使用动态库,绝大部分情况下也没必要使用。如果大家真的感兴趣可以参考下这篇文章:
https://bbs.csdn.net/topics/392392689
制作动态链接库
需要说明的在linux中动态链接库的后缀名是.so,而在Windows系统的则是.dll。好了,正式进入今天我们的主题:
第一步,老规矩还是先创建我们的.c源文件和.h头文件,在.c中实现具体的功能函数,在.h中声明这些函数,具体如下:
//创建一个test.c,简单实现了加减乘除计算//加int add(int i, int j){ return (i+j);}//减int sub(int i, int j){ return (i - j);}//乘int multi(int i, int j){ return (i*j);}//除int div(int i, int j){ if(j == 0) return -1; else return (i/j);}/********************分隔符**************************///创建test.h,声明以上函数int add(int i, int j);int sub(int i, int j);int multi(int i, int j);int div(int i, int j);
第二步,只编译不链接,这里还需要编译成位置无关码(为什么要编译成位置无关码呢?这是因为我们所有的动态链接库是位置无关码,只在实际运行时,操作系统加载后才能真正确定加载的地址),具体指令:
gcc -c test.c -o test.o -fPIC
其中:
-c 表示只编译不链接,即只生成目标文件,不生成最终的可执行文件
-o 表示指定生成文件名,如test.o
-fPIC 表示位置无关码
第三步,将生成得到的test.o作为材料,制作动态库,具体指令如下:
gcc test.o -o libmySync.so -shared
其中:
libmySync.so中lib是固定的,意思为库,而后面的mySync是自定义库名,.so表示动态链接库,总结为:lib+库名.so
-shared表示制作为动态链接库,固定参数。
使用动态链接库
首先我们知道不管静态链接库还是动态链接库,在发布的时候需要同时发布库以及对应的.h头文件。
因此在动态链接库的目录下创建一个空目录
mkdir lib_test
然后将test.h头文件拷贝到lib_test路径中(这里我们就不拷贝libmySync.so动态库了,到时候使用时指定路径即刻,因此来模拟真实使用情况),并在创建test_lib.c用来测试动态库。
//test_lib.c内容#include "test.h"#include int main(void){ printf("1 + 2 = %d.\n", add(1,2)); printf("12 - 7 = %d.\n", sub(12,7)); printf("23 * 3 = %d.\n", multi(23,3)); printf("100 / 4 = %d.\n", div(100,4)); return 0;}
接着我们尝试着编译, 与之前的静态链接库的指令同理,使用以下命令:
gcc test_lib.c -lmySync -L..
其中-lmySync中的-l(小写的L)是指定库名,即后面的mySync, -L是指定库所在的路径,..是表示上一级目录。
可以看到编译无错误无警告,且生成了可执行文件a.out,接着我们尝试运行下:
发现运行时报错,根据错误提示是:无法加载我们的动态库。
我们知道这里生成的可执行文件a.out并不是完整的,一些调用函数还在动态库中,并没有实际链接到可执行文件中,虽然我们在编译时指定了库的路径,但是系统真正是将特定目录/usr/lib中的动态库加载到内存中的,因此我们需设置下,以下有两种方式:
将我们的制作的动态库libmySync.so拷贝到/usr/lib下,再尝试运行a.outsudo cp ../libmySync.so /usr/lib
设置环境变量LD_LIBRARY_PATH, 告诉系统去加载该环境变量路径下的动态库export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/winshare/c_test/make_dyncLib
这两者方式都能成功解决,个人推荐第二种,因为/usr/lib是系统路径,放着系统自带的动态库,轻易不要在这个目录下操作,以免紊乱。
总结
动态链接库与静态链接库制作过程类似,都是先要创建源文件(实现具体功能函数)和头文件(声明函数原型),接着只编译不链接源文件,生成.o目标文件(注意的是制作动态链接库时,需要使用-fPIC选项指定为位置无关码),再以.o为材料去制作我们的库:
制作静态链接库:ar -rc libmymath.a test.o
制作动态链接库: gcc test.o -o libmySync.so -shared
最后是使用库时,编译时需要指定库名和库路径,动态链接库还需要告知系统去实际加载我们动态库,因为我们可执行文件并不是完成的!而使用静态库链接的可执行文件是完整,可直接独立运行。