动态链接库和静态链接库若干的问题
参考:
动态链接和静态链接库
显式加载与隐式加载区别
Window下隐式和显式调用动态库
Linux下隐式和显式调用动态库
- 问题1:动态链接需不需要lib(导入库)及其区别
- 问题2:动态链接库显式加载与隐式加载的区别
- 总结
问题1:动态链接需不需要lib(导入库)及其区别
在Linux下:
生成动态库的最常用方式是:g++ -shared
其所生成的动态链接库都是只有.so文件。没有导入库文件.a。
在Window下:
生成动态库的最常用方式是使用Visual Studio工具:
其代码中都必须有导出函数的声明_declspec(dllexport)关键字(g++不需要)。且这种方式生成的动态链接库dll必须有导入库文件lib。
当然,CMake工具也可以在window下和Linux下制作动态库:
经实验发现:
在Window下:
如果源文件中有_declspec(dllexport)关键字则生成dll和lib。如果源文件中不带有_declspec(dllexport)关键字则只生成dll而没有lib。
在Linux下:
识别不了_declspec(dllexport)关键字(这个关键字在win32运行时下有效),在没有_declspec(dllexport)关键字的情况下只生成.so文件。
总结一下:
Window与Linux执行文件格式不同,在创建动态库的时候有一些差异。
- 在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。
生成导入库文件lib。 - Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。不生成导入库文件。
问题2:动态链接库显式加载与隐式加载区别
动态链接库有两种加载方式:隐式加载和显示加载。
隐式加载又叫载入时加载,指在主程序载入内存时搜索DLL,并将DLL载入内存。隐式加载也会有静态链接库的问题,如果程序稍大,加载时间就会过长,用户不能接受。
显式加载又叫运行时加载,指主程序在运行过程中需要DLL中的函数时再加载。显式加载是将较大的程序分开加载的,程序运行时只需要将主程序载入内存,软件打开速度快,用户体验好。
Window和Linux下显式链接和隐式链接动态库有不同的方式:
Window下::
用loadlibrary就是显示链接,用lib就属于隐式链接。两种方法对于你的程序调用动态库时没有任何区别,只是你在编程时,步骤是不一样的。显式调用麻烦了点,但可以没有相应的lib库;隐式调用,使用起来比较简单,有函数的声明就可以了,但必须有lib库。
两种方式的具体方法:
一、动态库的隐示调用:
在工程中直接链接静态输入库XXX.lib,然后即可像调用其它源文件中的函数一样调用DLL中的函数了。
二、动态库的显式调用:
显式调用动态库步骤:
1、创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数相吻合。
2、通过 Win32 API 函数LoadLibrary()显式的调用DLL,此函数返回DLL 的实例句柄。
3、通过 Win32 API 函数GetProcAddress()获取要调用的DLL 的函数地址,把结果赋给自定义函数的指针类型。
4、使用函数指针来调用 DLL 函数。
5、最后调用完成后,通过 Win32 API 函数FreeLibrary()释放DLL 函数
Linux下:
两种方式的具体方法:
一、如果是隐式调用,则与静态库的使用方法差不多,注意需要包含导出函数的头文件。
二、显式调用的方式,不必包含导出函数头文件,但是有显式调用相关的函数:
手动加载动态链接库dlopen dlsym dlcolose
1. 打开动态链接库:
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
该函数返回操作句柄,如:
void *pHandle = dlopen(strSoFilePath, RTLD_LAZY);
2. 取动态对象地址:
#include<dlfcn.h>
void *dlsym(void *pHandle, char *symbol);
dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。
使用这个函数不但可以获取函数地址,也可以获取变量地址。比如,假设在so中
定义了一个void mytest()函数,那在使用so时先声明一个函数指针:
void (*pMytest)(),然后使用dlsym函数将函数指针pMytest指向mytest函数,
pMytest = (void (*)())dlsym(pHandle, “mytest”);
3. 关闭动态链接库:
#include <dlfcn.h>
int dlclose(void *handle);
该函数将该.so的引用计数减一,当引用计数为0时,将它从系统中卸载。
4. 动态库错误函数:
#include <dlfcn.h>
const char *dlerror(void);
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时 表示没有错误信息。
在取到函数执行地址后,就可以在动态库的使用程序里根据动态库提供的函数接口调用动态库里的函数。在编写调用动态库的程序的Makefile文件时,需要加入编译选-ldl。
从void *dlsym(void *handle, char *symbol); 的参数可以看出,该函数只传两个参数:一个指向so的handle和一个函数的symbol,所以so里面的函数应该不允许重,否则根据一个symbol不能确定指向那个函数。
总结:
动态链接库如果没有显式加载,则其与静态库没有什么区别,Window下和Linux下显式加载和隐式加载的方式不同,且形式也不同(Window隐式加载需要导入库lib而Linux下不需要导入库。)