#参考:
# http://www.abc188.com/info/html/wangzhanyunying/jianzhanjingyan/20080417/70810.html
# http://os.51cto.com/art/201001/176625.htm
# http://os.51cto.com/art/201001/176618.htm
[概述]本文是我在学习linux下动态链接库的总结,主要内容为:为什么要使用动态链接库、动态链接库的制作、使用。
0 为什么要使用动态链接库
linux下的库文件有:静态库和动态库两种。静态库文件的制作和使用在前文中已简要总结。LINUX系统中动态链接库,类似于 WINDOWS中以.dll为后缀的文件(dll即Dynamic Link Library)。
静态库与动态库的区别。如果程序是在编译时加载库文件的,就是使用了静态库。如果是在运行时加载目标代码,就成为动态库。换句话说,如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码段,程序的体积会膨胀。如果使用动态库,则程序中只保留库文件的名字和函数名,在运行时去查找库文件和函数体,程序的体积基本变化不大。
静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;
动态库则是“以时间换空间”,增加了运行时间,但减少了程序本身的体积。
1 动态链接库的制作
在LINUX系统下,创建动态链接库是件非常简单的事情。只要在编译函数库源程序时加上-shared选项即可,这样所生成的执行程序即为动态链接库。从某种意义上来说,动态链接库也是一种执行程序。按一般规则,动态库的命名应为lib*.so.*。其中第一个*为动态库的名字,第二个*为版本号,比如libc.so.6。编译生成动态库的命令: gcc -o libmy.so -fpic -shared getdate.c gettime.c
下面制作动态库的例子中有3个文件: adatetime.h, getdate.c, gettime.c。adatetime.h为动态链接库的头文件; getdate.c和gettime.c分别实现getDate()和getTime()函数。
1.1 编写动态库头文件adatetime.h,代码如下:
[cpp] view plain copy
/*
* adatetime.h
*/
#ifndef _DATETIME_H
#define _DATETIME_H
typedef struct /*日期结构*/
{
int year;
int month;
int day;
}DATETYPE;
typedef struct /*时间结构*/
{
char hour;
char min;
char sec;
}TIMETYPE;
/*函数原型说明*/
int getDate(DATETYPE *d); /*取当前日期*/
int getTime(TIMETYPE *t); /*取当前时间*/
#endif
1.2 编写getdate.c和gettime.c,代码如下:
[cpp] view plain copy
/*
* getdate.c: 定义获取系统日期的函数getDate()
*/
#include<time.h>
#include "adatetime.h"
int getDate( DATETYPE *d )
{
long ti;
struct tm *tm;
time(&ti);
tm = localtime(&ti);
d -> year = tm -> tm_year + 1900;
d -> month = tm -> tm_mon + 1;
d -> day = tm -> tm_mday;
}
/*
* gettime.c: 定义获取系统时间的函数getTime()
*/
#include<time.h>
#include "adatetime.h"
int getTime( TIMETYPE *t )
{
long ti;
struct tm *tm;
time(&ti);
tm = localtime(&ti);
t -> hour = tm -> tm_hour;
t -> min = tm -> tm_min;
t -> sec = tm -> tm_sec;
}
1.3 编译生成动态库
(1) 编写维护文件makefile-lib,内容如下
[cpp] view plain copy
#makefile-lib: 动态库实例维护文件
all: libmy.so
SRC = getdate.c gettime.c
TGT = $(SRC:.c=.o)
$(SRC): adatetime.h
@touch $@
%.o: %.c
cc -c $?
#生成动态链接库(libmy.so)
libmy.so: $(TGT)
cc -s -fpic -shared -o $@ $(TGT) #-fpic选项,增加动态库的可重定位性
注意: 每行维护代码必须以TAB(跳格键)开始,不是的话make时将出错。
编写维护文件的目的,在于方便程序员维护程序,尤其是维护比较大的工程项目。
(2) 运行命令make -f makefile-lib, 则生成名为libmy.so的动态库文件。
1.4 共享动态库文件
/etc/ld.so.conf文档中,存放着可被LINUX共享的动态链接库所在目录的名字(系统目录/lib,/usr/lib除外)。我们可以把自己制作的动态库放到/lib或/usr/lib;也可以新建用户自己的库文件夹:/home/uname/lib,然后把此文件夹路径写入/etc/ld.so.conf中(并把自己的动态库复制到该文件夹下)。
#ldconfig //刷新缓存文档/etc/lb.so.cache,使共享设置生效
然后就可以使用动态库文件libmy.so了。
2 动态库文件的使用
2.1 重要的dlfcn.h头文件
LINUX下使用动态链接库,源程序需要包含dlfcn.h头文件,此文件定义了调用动态链接库的函数的原型。下面详细说明一下这些函数。
2.1.1 dlerror
原型为: const char *dlerror(void);
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
2.1.2 dlopen
原型为: void *dlopen (const char *filename, int flag);
dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。
filename: 如果名字不以/开头,则非绝对路径名,将按下列先后顺序查找该文件。
(1) 用户环境变量中的LD_LIBRARY值;
(2) 动态链接缓冲文件/etc/ld.so.cache
(3) 目录/lib,/usr/lib
flag表示在什么时候解决未定义的符号(调用)。取值有两个:
1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。
2) RTLD_NOW : 表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。
dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。
2.1.3 dlsym : 取函数执行地址
原型为: void *dlsym(void *handle, char *symbol);
dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。
如程序代码: void (*add)(int x,int y); /* 说明一下要调用的动态函数add */
add=dlsym("xxx.so","add"); /* 打开xxx.so共享库,取add函数地址 */
add(89,369); /* 带两个参数89和369调用add函数 */
2.1.4 dlclose : 关闭动态链接库
原型为: int dlclose (void *handle);
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
2.2 在程序中使用动态链接库函数
2.2.0
动态库的分为隐式调用和显式 调用两种调用方法。
隐式调用的使用使用方法和静态库的调用差不多,具体方法如下:
gcc -c -I/home/weil/inc main.c
gcc -o main1 -L/home/ weil /lib main.o libmytime.so //这里是*.so
在这种调用方式中,需要维护动态链接库的 配置文件/etc/ld.so.conf 来让动态链接库为系统所使用,通常将动态链接库所在目录名追加到动态链接库配置文件中。否则在执行相关的可执行文 件的时候就会出现载入动态链接库失败的现象。 在编译所引用的动态库时,可以在gcc采用 –l或-L选项或直接引用所需的动态链接库方式进行编译。
在Linux里面,可以采用ldd命令来检查程序依赖共享库。
显式调用 :
gcc -o main -ldl main.c
用gcc编译对应的源文件生成可执行文件,-ldl选项,表示生成的对象模块需要使用共享库。
在示例程序ady.c中调用动态库libmy.so中的getDate()和getTime()函数,用来显式系统的当前时间。
2.2.1 编写ady.c (本例中使用显示调用的方法)
[cpp] view plain copy
/*
* ady.c: 动态链接库应用示范程序
*/
#include<stdio.h>
#include<dlfcn.h>
#include<string.h>
#define SOFILE "libmy.so"
#include "adatetime.h"
int main()
{
DATETYPE d;
TIMETYPE t;
void *dp;
char *error;
printf("A sample for loading SO!/n");
dp = dlopen(SOFILE, RTLD_LAZY); /* 打开动态链接库 */
if(dp == NULL)
{
fputs(dlerror(), stderr);
return -1;
}
int (*getDate)(); /*定义指针函数用于获取动态库中getDate()的入口地址*/
getDate = dlsym(dp, "getDate");
error=dlerror(); /* 检测错误 */
if ( error ) /* 若出错则退出 */
{
fputs(error,stderr);
return -1;
}
getDate(&d); /* 调用此共享函数 */
printf("Current Date: %04d-%02d-%02d/n",d.year,d.month,d.day);
int (*getTime)();
getTime=dlsym(dp,"getTime"); /* 定位取时间函数 */
error=dlerror(); /* 检测错误 */
if (error) /* 若出错则退出 */
{
fputs(error,stderr);
return -1;
}
getTime(&t); /* 调用此共享函数 */
printf("Current Time: %02d:%02d:%02d/n",t.hour,t.min,t.sec);
dlclose(dp); /* 关闭共享库 */
return 0;
}
2.2.2 编写维护文件makefile
[ruby] view plain copy
#makefile: 示例程序维护文件
all: ady
DYSRC = ady.c
DYTGT = $(DYSRC:.c=.o)
%.o:%.c
cc -c $?
#动态库示例
ady:$(DYTGT)
cc -rdynamic -s -o $@ $(DYTGT) -ldl
2.3 编译、运行测试程序
#make
#./ady
可显式系统当前日期、时间。
Linux中动态链接库总结
最新推荐文章于 2023-07-20 16:40:08 发布