1. 编译参数
1.1 -fPIC
告诉编译器产生与位置无关代码.如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位, 重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy。每个copy都不一样,取决于这个.so文件代码段和数据段内存映射的位置。不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码) 如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了。
1.1 -rdynamic
1、是一个链接器选项,当将所有的*.o
和库链接到最终可执行文件中就使用了它。该参数的作用是:将指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号,但不包括静态符号,比如被static修饰的函数)都添加到动态符号表(即.dynsym表)里,以便那些通过dlopen()或backtrace()(这一系列函数使用.dynsym表内符号)这样的函数使用。
2 动态链接库使用函数如下
//编译时候要加入 -ldl (指定dl库)
//包含的头文件
#include
void *dlopen(const char *filename, int flag);
//dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄
//参数filename是共享库文件的路径,可以是相对路径或绝对路径。参数flag用于指定打开方式,常用的取值包括RTLD_NOW(立即解析所有符号)、RTLD_LAZY(懒加载,只有当使用时才解析符号)等。
mode: 参数必须包括以下两个值中的一个
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
mode也可以通过以下零或多个值进行或运算设置
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
void *dlsym(void *handle, const char *symbol);
//根据动态链接库操作句柄与符号,返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称.
int dlclose(void *handle);
//用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
char *dlerror(void);
//当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
3. 动态库函数的使用demo记录
3.1 动态库demo的头文件和源文件
3.1.1 libdemo.h头文件
#ifndef _LIBDEMO_H_
#define _LIBDEMO_H_
#include
typedef struct
{
int val;
}Val_t;
extern void add(Val_t* addval);
extern int getVal(void);
#endif
3.1.2 libdemo.c 文件
#include "libdemo.h"
int g_libValue = 0;
void add(Val_t *addval)
{
g_libValue += addval->val;
}
int getVal(void)
{
return g_libValue;
}
3.1.3 makefile 简易编写
src=$(wildcard ./*.c) #把所有以.c文件 匹配上,并把文件名提取出来,作为一个列表赋值给src
object=$(patsubst %.c,%.o,$(src)) #将src里的每个文件都由.c替换成.o
target1=libdemo0.so #库文件
target2=libdemo1.so
CC=gcc
CFLAGS=-I./ -shared -fPIC #头文件参数、动态链接编译参数
all:$(target1)
$(target1):$(object)
$(CC) -o $@ $^ $(CFLAGS) #生成动态库文件
cp $(target1) $(target2) #拷贝一个一样的动态库
#参数解释, $@表示目标文件
#参数解释, $^表示所有的依赖文件
#参数解释, $<表示第一个依赖文件
%.o:%.c
$(CC) -o $@ -c $< $(CFLAGS) #生成obj文件
.PHONY:clean
clean:
rm -f $(target1) $(target2) $(object) #清除动态库文件和obj文件
3.2 应用层调用头文件和源文件
3.2.1 appldemo.h头文件
#ifndef _APPL_DEMO_H_
#define _APPL_DEMO_H_
#include "libdemo.h"
//调用动态库操作函数
typedef struct
{
void (*p_add)(Val_t*);
int (*p_getval)(void);
} SharedObjOps_t;
extern int SharedObjInit(int id, SharedObjOps_t*op);
#endif
3.2.2 appldemo.c源文件
#include
#include
#include
#include
#define FILE_PATH_NAME "../lib/libdemo"
/*
* brif :调用动态库初始化函数
*/
int SharedObjInit(int id, SharedObjOps_t*op)
{
char filePathName[128] = FILE_PATH_NAME;
char idnum[10] = {0};
char filetype[10] = ".so";
char file[128] = {0};
void *handle; //库文件句柄
strcat(file, filepath);
sprintf(idnum, "%d", id);
strcat(file, idnum);
strcat(file, filetype);
handle = dlopen(file, RTLD_LAZY); //打开动态库,且为暂缓决定,等有需要时再解出符号
if (handle == NULL)
{
return 1; //打不开库
}
op->p_add= dlsym(handle, "add");
op->p_getval= dlsym(handle, "getval");
if(!op->p_add || !op->p_getval)
{
return 2; //找不到其中的库函数
}
return 0;
}
int main()
{
SharedObjOps_t op0, op1; //两个动态库操作函数结构体句柄
Val_t val0 = {0}, val1 = {0};
//分别初始化两个动态库
if(SharedObjInit(0, &op0))
{
return 1; //失败
}
if(SharedObjInit(1, &op1))
{
return 1; //失败
}
val0.val = 10;
val0.val = 20;
op0.p_add(&val0);
op1.p_add(&val1);
printf("op0's g_libValue returned: %d\n", op0.p_getval());
printf("op1's g_libValue returned: %d\n", op1.p_getval());
val0.val = 1;
val1.val = 2;
op0.p_add(&val0);
op1.p_add(&val1);
printf("op0's g_libValue returned: %d\n", op0.p_getval());
printf("op1's g_libValue returned: %d\n", op1.p_getval());
return 0;
}
3.2.3 Makefile编写
src=$(wildcard ./*.c)
object=$(patsubst %.c,%.o,$(src))
target=appdemo
CC=gcc
CFLAGS=-I./ -I../lib -ldl
all:$(target)
$(target):$(object)
$(CC) -o $@ $^ $(CFLAGS)
%.o:%.c
$(CC) -o $@ -c $< $(CFLAGS)
.PHONY:clean
clean:
rm -f $(target) $(object)