Windows和Linux动态链接库总结

动态链接库为模块化应用程序提供了一种方式,使得更新和重用程序更加方便。当几个应用程序在同一时间使用相同的函数时,它也帮助减少内存消耗,这是因为虽然每个应用程序有独立的数据拷贝,但是它们的代码是共享的。

 

(1)动态链接库的概念

动态链接库是应用程序的一个模块,这个模块用于导出一些函数和数据供程序中的其他模

块使用。应该从以下 3 个方面来理解这个概念:

动态链接库是应用程序的一部分,它的任何操作都是代表应用程序进行的。所以动态链接库在本质上与可执行文件没有区别,都是作为模块被进程加载到自己的空间地址的

动态链接库在程序编译时并不会被插入到可执行文件中,在程序运行时整个库的代码才会调入内存,这就是所谓的“动态链接”。

如果有多个程序用到同一个动态链接库,Windows 在物理内存中只保留一份库的代码,仅通过分页机制将这份代码映射到不同的进程中。这样,不管有多少程序同时使用一个库,库代码实际占用的物理内存永远只有一份

动态链接库(DynamicLink Libraries)的缩写是 DLL,大部分动态链接库镜像文件的扩展

名为 dll,但扩展名为其他的文件也有可能是动态链接库,如系统中的某些 exe文件、各种控件(*.ocx等都是动态链接库。


(2)  Window下创建动态链接库

[1] 动态链接库中的函数DLL 能够定义两种函数,导出函数和内部函数。导出函数可以被其他模块调用,也可以被定义这个函数的模块调用,而内部函数只能被定义这个函数的模块调用。

[2]  *.h

#ifdef MY09DELLDEMO_EXPORTS
#define MY09DELLDEMO_API __declspec(dllexport)
#else
#define MY09DELLDEMO_API __declspec(dllimport)
#endif

// This class is exported from the 09DellDemo.dll
class MY09DELLDEMO_API CMy09DellDemo {//MY09DELLDEMO_API声明要导出声明要导出的类*****
public:
	CMy09DellDemo(void);
	// TODO: add your methods here.
};

extern MY09DELLDEMO_API int nMy09DellDemo;//MY09DELLDEMO_API声明要导出的变量*****

MY09DELLDEMO_API int fnMy09DellDemo(void);//MY09DELLDEMO_API声明要导出的函数

MY09DLLDEMO_API 宏是 VC++自动生成的,在这里它被解释为__declspec(dllexport),

下面的变量、函数和类将从 DLL 模块中导出。编译连接程序,最后 09DllDemo 工程产生的文件中有 3 个可以被其他工程使用:09DllDemo 文件夹下的 09DllDemo.h 头文件、debug 文件夹(或者 Release 文件夹)下的 09DllDemo.dll 文件和 09DllDemo.lib 文件。

.dll 文件就是动态链接库,.lib 文件是供程序开发用的导入库,.h 文件包含了导出函数的声明

[3]  *.cpp

// 09DellDemo.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "09DellDemo.h"

BOOL APIENTRY DllMain//动态链接库的入口点
( 
 HANDLE hModule, //本Dll模块句柄
 DWORD  ul_reason_for_call, //调用的原因
 LPVOID lpReserved//没有被使用
 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH://动态链接库刚被映射到某个进程的地址空间<-->4
		case DLL_THREAD_ATTACH://应用程序创建了一个新的线程<-->3
		case DLL_THREAD_DETACH://应用程序某个线程正常的终止
		case DLL_PROCESS_DETACH://动态链接库将被卸载
			break;
    }
    return TRUE;
}


// This is an example of an exported variable
MY09DELLDEMO_API int nMy09DellDemo=0;//声明要导出的变量

// This is an example of an exported function.
MY09DELLDEMO_API int fnMy09DellDemo(void)//声明要导出的函数
{
	return 42;
}

// This is the constructor of a class that has been exported.
// see 09DellDemo.h for the class definition
CMy09DellDemo::CMy09DellDemo()//声明要导出的类
{ 
	return; 
}

[4] 使用导出函数

调用 DLL 中的导出函数有两种方法:

(1)装载期间动态链接。模块可以像调用本地函数一样调用从其他模块导出的函数(API

函数就是这样调用的)。装载期间链接必须使用 DLL 的导入库(.lib 文件),它为系统提供了加载这个 DLL 和定位 DLL 中的导出函数所需的信息。

所谓装载期间动态链接,就是应用程序启动时由载入器(加载应用程序的组件)载入

09DllDemo.dll 文件。

这种方法加载 DLL 库的缺点很明显,如果用户丢失了 DLL文件,那么程序是永远也不能

启动了。所以很多时候要采取运行期间动态链接的方法,以便决定加载失败后如何处理。

(2)运行期间动态链接。模块也可以使用LoadLibrary或者 LoadLibraryEx 函数在运行期

间加载这个 DLLDLL被加载之后,加载模块调用GetProcAddress函数取得 DLL 导出函数的地址然后通过函数地址调用 DLL 中的函数

使用第 1 种方法时,9.1.3 小节最后生成的 3 个文件都会被用到,使用第 2 种方法时,只

有 09DllDemo.dll 文件会被用到。

 

Eg1:装载期动态链接

#include <windows.h>
#include "09DllDemo.h"
// 指明要链接到 09DllDemo.lib 库
#pragma comment(lib,"09DllDemo")
void main()
{ // 像调用本地函数一样调用09DllDemo.dll 库的导出函数
ExportFunc("大家好!");
}
 

Eg2:运行期动态链接

#include <windows.h>
// 声明函数原形
typedef void (*PFNEXPORTFUNC)(LPCTSTR);
int main(int argc, char* argv[])
{ // 加载 DLL 库
HMODULE hModule = ::LoadLibrary("..\\09DllDemo\\Debug\\09DllDemo.dll");
if(hModule != NULL)
{ // 取得 ExportFunc 函数的地址
PFNEXPORTFUNC mExportFunc =(PFNEXPORTFUNC)::GetProcAddress(hModule,"ExportFunc");
if(mExportFunc != NULL)
{ mExportFunc("大家好!");}
// 卸载 DLL 库
::FreeLibrary (hModule);
}
return 0;
}


eg3:实例测试


(3)Linux动态链接库的创建与使用


[1]主要概念

动态链接库,和静态函数库不同,它里面的函数并不是执行程序本身的一部分,而是根据执行程序需要按需装入,同时其执行代码可在多个执行程序间共享,节省了空间,提高了效率,具备很高的灵活性,得到越来越多程序员和用户的青睐。那么,在LINUX系统中有无这样的函数库呢?

LINUX的动态链接库不仅有,而且为数不少。/lib目录下,就有许多以.so作后缀的文件,这就是LINUX系统应用的动态链接库,只不过与WINDOWS叫法不同,它叫so,即Shared Object,共享对象。(在LINUX下,静态函数库是以.a作后缀的)

 

[2]使用共享函数库


通过一个API来打开一个函数库,寻找符号表,处理错误和关闭函数库

这一过程涉及的相关库函数如下:

#include <dlfcn.h>

 

dlopen()

dlopen函数打开一个函数库然后为后面的使用做准备。C语言原形是:

void * dlopen(const char *filename, intflag);

dlopen()函数中,参数flag的值必须是RTLD_LAZY或者RTLD_NOW。

RTLD_LAZY:在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)。

RTLD_NOW: 需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL,错误为:: undefined symbol: xxxx.......

如果有好几个函数库,它们之间有一些依赖关系的话,例如X依赖Y,那么你就要先加载那些被依赖的函数。例如先加载Y,然后加载X。

 

dlerror()

通过调用dlerror()函数,我们可以获得最后一次调用dlopen(),dlsym(),或者dlclose()的错误信息。

 

void * dlsym(void *handle, char *symbol);

使用一个DL函数库的最主要的一个函数就是dlsym(),这个函数在一个已经打开的函数库里面查找给定的符号。

函数中的参数handle就是由dlopen打开后返回的句柄,symbol是一个以NIL结尾的字符串。

 

dlclose()

dlopen()函数的反过程就是dlclose()函数,dlclose()函数用力关闭一个DL函数库。Dl函数库维持一个资源利用的计数器,当调用dlclose的时候,就把这个计数器的计数减一,如果计数器为0,则真正的释放掉。

 

编译时候要加入-ldl (指定dl)

例如

gcc test.c -o test -ldl


例子:

#include <stdio.h>
#include <dlfcn.h>//dlopen()  etc
#include <stdlib.h>

double (*cosine)(double); //define function pointer

int main()
{
void *handle;
char *error;

handle = dlopen("/lib/libm.so.6",RTLD_LAZY);//1
if(!handle)
{
fputs(dlerror(),stderr);
exit(1);
}

cosine = dlsym(handle,"cos");//2
if((error = dlerror()) != NULL)
{
fputs(error,stderr);
exit(1);
}

printf("cosine(3.14159/2) = %f\n",cosine(3.14159/2));//3

dlclose(handle);//4

return 0;
}

运行结果:


[3]定义和创建动态链接库





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值