转自:https://blog.csdn.net/u011057439/article/details/51459620
https://blog.csdn.net/qq_33850438/article/details/52014399
so可以供多个进程使用,不同进程调用同一个so文件,所使用so文件不同。
so文件源程序不需要main函数,有也不会被执行。
下面通过一个简单例子,来学习.so文件的制作跟使用(前提已经配置好环境)、
主要了解学习用C语言编译一个动态链接库,如何使用这个库
1、通过简单max函数,生成一个libmax.so链接库
- /*
- * max.c
- *
- * Created on: 2016年7月24日
- * Author: Andy_Cong
- */
- /*生成libmax.so链接库*/
- /*
- * # -shared 为链接库 让编译器知道是要编译一个共享库
- * # -fPIC(Position Independent Code) 编译生成代码与位置无关
- */
- int max(int a,int b)
- {
- return a>b?a:b;
- }
- /*
- * gcc -Wall -g -fPIC -c max.c -o max.o
- * gcc -shared max.o -o libmax.so
- * -g -Wall 供调试使用,不是必须的
- */
编译结果:生成libmax.so
![](https://i-blog.csdnimg.cn/blog_migrate/b75eeab8312bbf84726359a837a7110c.png)
2、使用链接库,需要包含头文件(很正常,我们平时使用C库函数也需要包含相关头文件)
max.h头文件如下
- /*
- * max.h
- *
- * Created on: 2016年7月24日
- * Author: Andy_Cong
- */
- #ifndef MAX_H_
- #define MAX_H_
- int max(int a,int b);
- #endif /* MAX_H_ */
测试函数main.c如下
- /*
- * main.c
- *
- * Created on: 2016年7月24日
- * Author: Andy_Cong
- */
- #include<stdio.h>
- #include"max.h"
- int main(void)
- {
- printf("call max function results is: %d\n",max(1,1));
- return 0;
- }
- /* 使用libmax.so库
- * gcc -o main main.c -L. -lmax
- *
- *-L.: 在当前路径下寻找.so文件
- *-lmax: 要链接这个libmax.so文件
- *
- * */
运行结果:生成可执行程序main(成功了)
3、使用C++编译使用C语言提供的链接库, 编译链接出错(下面只是简单将main.c 改为main.cpp)
怎么办呢??
libmax这个库仅适合C使用,C++并不适合,如果想编译一个可以供C++使用。那么头文件(max.h)就需要改变,
需要额外增加一句:extern "C"
max.h(修改如下)
- /*
- * max.h
- *
- * Created on: 2016年7月24日
- * Author: Andy_Cong
- */
- #ifndef MAX_H_
- #define MAX_H_
- extern "C" // 这句话可理解为,告诉编译器,这个动态库(.so)是用C语言写的,
- // 需要用C语言链接方式来链接这个库,这样就可以g++来编译了。
- int max(int a,int b);
- #endif /* MAX_H_ */
运行结果
这样就解决了。
4、但是这样有一个问题,难道每次编译都要改来改去,有没有同时适合C/C链接库的方法呢?
答案是有的,只需要改动头文件即可,使用条件编译
C++有一个宏:__cplusplus 当用g++编译的时候,就可以识别这个宏
- /*
- * max.h
- *
- * Created on: 2016年7月24日
- * Author: Andy_Cong
- */
- /*条件编译*/
- #ifndef MAX_H_
- #define MAX_H_
- #ifdef __cplusplus
- extern "C" //C++
- {
- #endif
- int max(int a,int b);
- #ifdef __cplusplus
- }
- #endif
- #endif /* MAX_H_ */
--------------------------------下面这个更详尽--------------------
1、动态库的编译
下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test_a.c、test_b.c、test_c.c,我们将这几个文件编译成一个动态库:libtest.so。
//so_test.h:
#include "stdio.h"
void test_a();
void test_b();
void test_c();
//test_a.c:
#include "so_test.h"
void test_a()
{
printf("this is in test_a...\n");
}
//test_b.c:
#include "so_test.h"
void test_b()
{
printf("this is in test_b...\n");
}
//test_c.c:
#include "so_test.h"
void test_c()
{
printf("this is in test_c...\n");
}
将这几个文件编译成一个动态库:libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
2、动态库的链接
在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这个库里的函数。程序的源文件为:test.c。
test.c:
#include "so_test.h"
int main()
{
test_a();
test_b();
test_c();
return 0;
}
将test.c与动态库libtest.so链接生成执行文件test:
$ gcc test.c -L. -ltest -o test
测试是否动态连接,如果列出libtest.so,那么应该是连接正常了
$ ldd test
执行test,可以看到它是如何调用动态库中的函数的。
3、编译参数解析
最主要的是GCC命令行的一个选项:
-shared该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
-L.:表示要连接的库在当前目录中
-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
4、注意
调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。
在linux下可以用export命令来设置这个值,在linux终端下输入:
export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: $LD_LIBRARY_PATH:
然后再输入:export
即会显示是否设置正确
export方式在重启后失效,所以也可以用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH变量。
例如:LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin。