so文件的编译与使用
C使用so文件
共写了两个c文件和两个h文件。
文件一:calculate.c
#include "min.h"
int max(int a,int b){
return a>b?a:b;
}
int whoissmall(int a,int b){
return min(a,b);
}
文件二:calculate.h
int max(int a,int b);
int whoissmall(int a,int b);
文件三:min.c
int min(int a,int b){
return a<b?a:b;
}
文件四:min.h
int min(int a,int b);
方法一
-
利用以下命令将两个c文件生成一个so文件,此处生成的so文件为libcalculate.so,(so文件名要求前缀为lib,扩展名为so)
gcc -shared -fPIC min.c calculate.c -o libcalculate.so。 -
接着新建一个main.c,其中用到了so文件中的calculate.c中实现的函数,需要将头文件include进来。
文件名:main.c
#include <stdio.h>
#include <stdlib.h>
#include "calculate.h"
int main() {
printf("%d\n", whoissmall(1,2));
}
- 用以下命令编译,
gcc main.c -lcalculate -L testso/ -I testso/ -o main
1. -l代表所连接的so文件的名字,因为我这里用的是libcalculate.so,连接的时候需要去掉lib和.so。
2. -L是代表so文件所在的位置
3. -I是代表编译的c文件(这里是main.c)所include的头文件的位置。这里使用的whoissmall函数是calculate.c中实现的,所以include calculate.h。(同样,若想用min.h中的min函数,在main.c中include min.h即可)。
扩展:
1. 对于-L,可通过以下命令来添加gcc寻找so文件的位置。
export LIBRARY_PATH=/home/haimiao/testso:$LIBRARY_PATH
这样编译的时候直接写gcc main.c -lcalculate -I testso/ -o main即可。
gcc在寻找so文件时的寻找顺序为-L > /lib和/usr/lib和/usr/local/lib > LIBRARY_PATH。
2. 对于-I ,gcc默认寻找头文件的位置/usr/include和本目录下,因而若希望找到头文件在别的目录下,需要加-I。
- 直接运行main即可。
方法二
- 使用方法一的so文件,改写main.c。
main2.c
#include <stdio.h>
#include <dlfcn.h>
int main(){
void* handle;
int(*func) (int, int);
char* error;
handle = dlopen("libcalculate.so", RTLD_LAZY);
if (!handle) {
printf("%s\n", dlerror());
return -1;
}
func = dlsym(handle, "whoissmall");
if ((error = dlerror()) != NULL) {
printf("%s\n", error);
return -1;
}
printf("%d\n,", func(1, 2));
return 0;
}
以下介绍所用到的函数:
包含头文件:
#include <dlfcn.h>
函数定义:
void * dlopen( const char * pathname, int mode );
函数描述:
在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode:分为这两种
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
返回值:
打开错误返回NULL
成功,返回库引用
void* dlsym(void* handle,const char* symbol)
该函数在<dlfcn.h>文件中。
handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用
dlerror()
包含头文件:
#include <dlfcn.h>
函数原型:
const char *dlerror(void);
函数描述:
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
dlclose()
包含头文件:
#include <dlfcn.h>
函数原型为:
int dlclose (void *handle);
函数描述:
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
- 编译时需要加上-ldl选项。
gcc main2.c -ldl
若执行时报以下错误
说明so文件找不到,需要设置一下LD_LIBRARY_PATH(为什么方法一时用LD_LIBRARY_PATH不行,只能用LIBRARY_PATH),如下
export LD_LIBRARY_PATH=/home/haimiao/testso:$LD_LIBRARY_PATH
其中冒号前是so文件所在位置,冒号是路径的分隔符。
使用这种方法不需要include头文件。
python使用so文件
这里使用的python版本为2.7.12
- 使用的so文件同样如上。改写main.py
from ctypes import cdll
cur = cdll.LoadLibrary('./libcalculate.so')
print cur.whoissmall(1,2)
直接执行即可。