现在的位置: 首页 > 综合 > 正文

静态链接库与动态链接库

2013年09月15日 ⁄ 综合 ⁄ 共 3545字 ⁄ 字号 评论关闭

一、静态链接库
静态链接库的代码在编译时链接到应用程序中,因此编译时库必须存在,并且需要通过 “-L” 参数传递给编译器,应用程序执行时不需要静态库的存在。
静态库的生成
静态库的生成分三步,设计库原码、编译.o文件和使用ar命令生成库。
1. 设计库原码。

//****pt1.c***/
#include <stdio.h>
int pt1(void)
{
printf("I am print1./n");
return 0;
}
//****pt2.c***/
#include <stdio.h>
int pt2(void)
{
printf("I am print2./n");
return 0;
}

2. 编译.o
使用 –c 选项产生目标文件,使用 –O选项优化代码;

# gcc –c –O pt1.c pt2.c

3. 使用ar库
创建静态库libpt.a, 并通过 ar 的 “-r” 选项增加中间目标文件到静态库文件中.

#ar –rsv libpt.a pt1.o pt2.o

4. 静态库的命名规则
为了在编译中正确的查找到库文件,表态库必需以 lib[name].a 的规则命名,如libpt.a.
静态库的使用
1. 调用库代码

/****main.c*******/
int main(void)
{
pt1();
pt2();
return 0;
}

2. 编译链接选项

#gcc –O –o main main.c –L ./-lp

或:

#gcc –O –o main main.c ./libpt.a

其中 –lpt的含义是 libpt.a, “-L ./” 含义是可以在当前目标下查找库文件。
3. 执行目标文件

#./main

Ar命令详解
Ar [drqtpmx] [options] archivefile objfile1 objfile2 …..
参数说明:
-r 将objfile文件插入静态库尾或替换静态库中的同名文件
-x 从静态库文件中抽取库文件objfile
-t 打印静态库中的文件列表
-d 从静态库中删除文件objfile
-s 重置静态库文件索引
-v 创建文件的冗余信息
-c 创建静态文件库

二、动态库
动态库也称为共享库,其代码不会链接到目标文件中,只有当动态库能正确访问时,应用程序才能正确的执行动态链接库函数。应用程序执行动态链接库有两种方式:隐式调用和显式调用。
1. 隐式调用
也称为共享库的静态加裁,其中动态库函数会在应用程序执行开始时加入内存,在应用程序执行完毕后自动卸载。隐式调用的编译方式与静态库一致。隐式调用的动态库必须放在特定的目录内才能被执行。
2. 显式调用
显式调用也称为动态库的动态加裁,编译时可以不显示的提供原动态库文件入名称,但必须遵循dlopen等函数的规则实现调用。应用程序可以在其运行的任意时刻加载或卸载动态链接库。
动态库的生成
动态库的生成主要包括三步:设计库原码、编译位置无关码(PIC)型.o文件、链接动态库。链接动态库的命令包括特殊标志,与链接静态库和链接可执行动态文件有区别,不同的UNIX系统也不尽相同。编译PIC型.o文件的方法一般采用C语言编译器的 “-KPIC” 或者 “-fpic”选项,创建最终动态库的方法一般是采用C语言编译器的 “-G”可者 “-shared”选项,或者直接使用工具ld创建。

1. 设计库原码。

/*pr1.c*/
#include <stdio.h>
int p=1;
void print(void)
{
printf("this is the first dll src./n");
}

 

/*pr2.c*/
#include <stdio.h>
int p=2;
void print(void)
{
printf("this is the second dll src./n");
}

2. 编译位置无关码(PIC)型.o文件和链接动态库(linux和其它使用gcc编译器的UNIX下)

#gcc –fpic –c pr1.c pr2.c
#gcc –shared –o pr1.so pr1.o
#gcc –shared –o pr2.so pr2.o

或者:

#gcc –O –fpic –shared –o pr1.so pr1.c
#gcc –O –fpic –shared –o pr2.so pr2.c

某些版本gcc 也可以使用 “-G”替换”-shared”;

动态链接库的隐式调用
动态链接库的隐式调用分三个步骤:调用库函数代码、编译链接、动态库查找
1. 调用库函数

/*td1.c*/
int main(void)
{
print();
return 0;
}

2. 编译链接选项
可直接将自建动态库文件名作为参数传递给编译器。

#cp pr1.so dll.so
#gcc –O –o td1 td1.c ./dll.so
#./td1

如果无法执行dll.so中的print,可将pr2.so COPY 成dll.so,再执行;
2. 动态链接库的查找
试着将dll.so删除,再可执行 ./td1,则提示夫法找到相应库,

#rm dll.so
#./td1
Dynamic linker: …….
Killed

当需要载入动态库时,UNIX会按一定的方法查找相关的库,上述中UNIX无法定位库文件,所以出错。一般情况下,当前目录是默认查找目标,但有些UNIX系统默认目录不是当前目录,这样库文件在当前目录下也会提示出错。解决方法为:
1)。带路径编译

#mkdir dll
#cp pr1.so ./dll/dll.so
#gcc –O –o td1 td1.c . ./dll/dll.so

当程序执行时会自动到./dll下查找相关库文件。
2)。更改环境变量
UNIX遍历某个特定环境变量的存储路径,来查找动态库,用户可以修改环境变量以达到自动搜索动态库的目的。

#LD_LIBRARY_PATH = ./:dll
#export LD_LIBRARY_PATH
#./td1

注:不同的UNIX所依赖的动态库查找路径环境变量名称不相同;
3. 动态库的更换

#cp pr2.so ./dll/dll.so
#./td1
this is the second dll src.

 

动态链接库的显式调用
显式中,应用程序应该遵循dlopen函数的规则调用动态链接库代码,依次为打开动态库、获取动态对象地址、调用动态库对象、错误检查、关闭动态库。
相关函数族:
1.UNIX中使用dlopen打开动态库:

#include<dlfcn.h>
void *dlopen(const char *pathname, int mode);

dlopen加载动态库,成功时返回指向动态库的句柄,失败时返回NULL,参数说明如下:
pathname 带路径的动态库名
mode 加载方式,可取值:
RTLD_LAZY: 动态库的对象符号在被调用时解释
RTLD_NOW:动态库的对象所有符号在函数dlopen返回前被解释;
2. UNIX中使用dlsym取得动态库中对象地址

#include<dlfcn.h>
void *dlsym(void *handle, const char * name);

dlsym在打开的动态库中搜索给为 name的函数对象或全变量,成功则返回相关地址,失败返回NULL,由于返回的是 void *类型指针,需要类型转换。参数说明如下:
handle 打开的动态库句柄
name 查找的全局变量名或函数名
3. 错误检查 UNIX中使用dlerror显式动态库操作中的错误信息

#include<dlfcn.h>
char *dlerror(void)

本函数为返回最近的错误信息,函数执行后自动将错误信息置为NULL
4. 关闭动态库 动态库使用完后,要关闭,以释放内存

#include<dlfcn.h>
int dlclose(void * handle);

参数说明:
handle 为打开的动态库
应用示例:
以下程序动态的调用以上述创建的动态链接库

/****/
#include<dlfcn.h>
char errs_msg[100];
void main(void)
{
void *pHandle;
void (*pFunc)();
int *p;
pHandle = dlopen("./dll.so",RTLD_NOW);
if (NUll == pHandle)
{
strcpy(errs_msg,"can not find dll.so");
goto ERR;
}
pFunc = (void(*)()) dlsym(pHandle,"print");
if (NUll != pFunc)
{
pFunc();
}else
{
strcpy(errs_msg,"can not find print");
goto ERR;
}
p = (int *)dlsym(pHandle,"p");
if (p)
{
printf("p=%d/n",p);
}else
{
strcpy(errs_msg,"can not find p./n");
goto ERR;
}
dlclose(pHandle);
return 0;
///err handle
ERR:
perror(errs_msg);
dlclose(pHandle);
exit(1);

抱歉!评论已关闭.