题记:
此文主要对windows和Linux下对动态链接库调用的一些简单说明,比如多个应用调用同一个动态链接库(后面简称DLL,希望不要误解)时,DLL是一个应用启动一个(可能描述不太恰当,但意思希望能理解)还是调用的同一个内在地址?DLL中的静态区是一个一份还是共用静态区?函数是一个一份还是共用?这些都是我们在编写DLL与调用时需要思考的地方。
正文:
首先,我们先谈谈windows下用调用动态链接库 。我用的IDE是开源跨平台的codeblocks,然后创建一个动态链接库项目。我在里面就写了两个简单的
DLL_EXPORT函数。我在里面定义了一些静态变量和普通变量。用来测试静态变量和普通变量等是如何存储的(是共享还是各自有一个copy),这里说下最终结果吧,我会在后面把代码附上,大家可以下载。结果是,静态是各自copy一份,供各自调用,而函数等则理共享的。所以他们的函数的入口地址都是完全一样,因为是共享的。
下面是动态链接库中main.h的代码
#define __MAIN_H__
#include < windows.h >
/* To use this exported function of dll, include this header
* in your project.
*/
#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#ifdef __cplusplus
extern " C "
{
#endif
int DLL_EXPORT Max( int a, int b);
int DLL_EXPORT StaticFuc();
#ifdef __cplusplus
}
#endif
#endif // __MAIN_H__
下面是main.cpp
// a sample exported function
int DLL_EXPORT Max( int a, int b)
{
return a > b ? a:b;
}
int DLL_EXPORT StaticFuc()
{
static int d = 20 ;
return d ++ ;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// attach to process
// return FALSE to fail DLL load
break ;
case DLL_PROCESS_DETACH:
// detach from process
break ;
case DLL_THREAD_ATTACH:
// attach to thread
break ;
case DLL_THREAD_DETACH:
// detach from thread
break ;
}
return TRUE; // succesful
}
下面是InvokeDLL中的main.cpp文件,用来调用动态链接库,并验证
#include < stdio.h >
typedef int ( * pMax)( int a, int b);
typedef int ( * pStaFuc)();
int main()
{
HINSTANCE hDLL;
hDLL = LoadLibrary( " D:\\cplus\\DLLDemo\\bin\\Debug\\DLLDemo.dll " );
pMax Max;
{
Max = (pMax)GetProcAddress(hDLL, " Max " );
int a = Max( 0 , 1 );
printf( " function address: 0x%X \n " ,(unsigned int ) & Max);
printf( " %d \n " ,a);
}
pStaFuc StaFuc;
{
StaFuc = (pStaFuc)GetProcAddress(hDLL, " StaticFuc " );
int retVal = StaFuc();
printf( " function address: 0x%X \n " ,(unsigned int ) & StaFuc);
printf( " %d \n " ,retVal);
}
FreeLibrary(hDLL);
return 0;
}
下面是截图:
下面是同时开启三个InvokeDLL2.exe和一个InvokeDLL.exe
从这个运行结果是可以看出调用动态链接库的原理。
Linux,
linux下的动态链接库在/lib目录下,以.so(shared object共享对象)作后缀的文件,这就是linux系统应用的动态链接库。而静态函数库是以.a作后缀。
首先,我建立一个名为testso.c文件,在C文件中我定义了两上函数,一个是Strlen用来获取字符串长度,另外一个是Display用来打印一行记录,代码如下:
2 Filename : testso.c
3 Description :
4 Author : Rockay.lau
5 Date : 2011-07-05
6 URL :Http://rockay.cnblogs.com
7 *********************************** */
8
9 #include < stdio.h >
10 #include < assert.h >
11
12 int Strlen( char * pStr)
13 {
14 unsigned long len;
15 assert(NULL != pStr);
16 len = 0 ;
17 while ( * pStr ++ )
18 {
19 len ++ ;
20 }
21 return len;
22 }
23
24 void Display()
25 {
26 printf( " Hello Rockay!\n " );
27 }
代码编写完后,我们用命令生成动态链接库文件,命令如下:
gcc -fpic -shared -o libtest.so testso.c
运行效果如下图:
这里我们再写一个invoke.c的文件,这里主要用来调用前面的动态链接库,代码如下:
2 Filename : invoke.c
3 Description :
4 Author : Rockay.lau
5 Date : 2011-07-05
6 URL :Http://rockay.cnblogs.com
7 *********************************** */
8
9 #include < stdio.h >
10 #include < dlfcn.h >
11
12 #define sofile "./libtest.so"
13
14 int main()
15 {
16 void ( * pPrintFun)( void );
17 int ( * pStrFun)( char * );
18
19 char str[] = { " rockay lau " };
20
21 void * pdlHandle;
22 char * pszErr;
23
24 pdlHandle = dlopen(sofile,RTLD_LAZY);
25 if ( ! pdlHandle)
26 {
27 printf( " Load standard object failed! \n " );
28 }
29
30 pszErr = dlerror();
31 if (pszErr != NULL)
32 {
33 printf( " Error: %s \n " ,pszErr);
34 }
35
36 pPrintFun = dlsym(pdlHandle, " Display " );
37 pszErr = dlerror();
38 if (pszErr == NULL)
39 {
40 pPrintFun();
41 }
42
43 pStrFun = dlsym(pdlHandle, " Strlen " );
44 pszErr = dlerror();
45 if (pszErr == NULL)
46 {
47 int len = pStrFun(str);
48 printf( " string length: %d \n " ,len);
49 }
50
51 return 0 ;
52 }
然后输入如下命令:
gcc -ldl -o invoke invoke.c
运行效果如下图:
这样,一个简单的linux下的动态链接库调用完成,关于静态变量和函数地址调用是否与windows下相同,则留给大家去验证一下。这里就不再详细描述!
最后,如果你写的动态链接库使用平台硬件环境比较差时,那么动态链接库尽量少用静态的东西。那样可以节省很多很多的空间。道听途说No***某某手机公司就是这样规定的。