看见好多同事问怎么在linux/Unix上编撰dll(动态链接库)的问题,下边转载一篇来自IBMDeveloperWorks的文章,希望对你们有所帮助。
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AllenWilson()
电子商务设计师,IBM
2001年10月
插件和DLL一般是拿来无须编撰整个新应用程序而添加功能的极好方式。在Linux中,插件和DLL以动态库方式实现。电子商务顾问兼设计师AllenWilson介绍了动态库而且向您演示了怎样在某个应用程序正在运行以后使用动态库来改变该应用程序。
Internet浏览器用户十分熟悉插件的概念。从Web上下载插件,一般这种插件为浏览器的音频、视频以及特殊疗效提供提高支持。通常来讲,在不修改原有应用程序的情况下,插件为现有应用程序提供新功能。
DLL是程序函数,它们在设计和建立应用程序时为该程序所知。设计应用程序的主程序时使用程序框架或底板,这种程序框架或底板在运行时选择性地放入所需的dlllinux操作系统安装,这种dll坐落c盘上同主程序分离的一些文件中。这一打包和动态放入提供了灵活的升级、维护、以及许可策略。
随Linux一起交付的还有几千条命令和应用程序,它们起码都须要libc库函数。假如libc函数与每一个应用程序都打包在一起linux命令,这么c盘中将会出现几千个相同函数的副本。Linux建立这种应用程序,以使用一般所需的系统库的单个系统级副本,而不浪费c盘空间。Linux甚至做得更好,每位须要公共系统库函数的进程使用单个的系统级内的副本,一次性将该副本放入到显存并为各进程所共享。
在Linux中,插件和dll以动态库方式实现。本文的余下部份是在应用程序运行以后使用动态库修改该应用程序的示例。
Linux动态链接
Linux中的应用程序以以下两种形式之一链接到外部函数:要么在建立时与静态库(lib*.a)静态地链接,但是将库代码包含在该应用程序的可执行文件里;要么在运行时与共享库(lib*.so)动态地链接。通过动态链接放入器,将动态库映射进应用程序的可执行显存中。在启动应用程序之前,动态链接放入器将所需的共享目标库映射到应用程序的显存,或则使用系统共享的目标并为应用程序解析所需的外部引用。现今应用程序就可以运行了。
作为示例,下边有一个演示Linux中对动态链接库的缺省使用的小程序:
main()
printf("Helloworld
");
当使用gcc编译hello.c时,就创建了一个名为a.out的可执行文件。通过使用Linux命令ldda.out(该命令复印出共享库的互相依赖性),可以看出所需的共享库是:
libc.so.6=>/lib/libc.so.6(0x4001d000)
/lib/ld-linux.so.2=>/lib/ld-linux.so.2(0x40000000)
使用相同的动态链接放入器在应用程序运行以后将dll映射进应用程序的显存。通过使用Linux动态放入器类库,应用程序控制放入哪一个动态库以及调用库中的哪一个函数,以执行放入和链接以及返回所需入口点的地址。
Linuxdll函数
Linux提供4个库函数(dlopen,dlerror,dlsym和dlclose),一个include文件(dlfcn.h)以及两个共享库(静态库libdl.a和动态库libdl.so),以支持动态链接放入器。这种库函数是:
dlopen将共享目标文件打开而且映射到显存中,而且返回句柄
dlsym返回一个指向被恳求入口点的表针
dlerror返回NULL或则一个指向描述近来错误的ASCII字符串的表针
dlclose关掉句柄而且取消共享目标文件的映射
动态链接放入器类库dlopen须要在文件系统中查找共享目标文件以打开文件并创建句柄。有4种形式用以指定文件的位置:
dlopencall中的绝对文件路径
在LD_LIBRARY_PATH环境变量手指定的目录中
在/etc/ld.so.cache手指定的库列表之中
先在/usr/lib之中,之后在/lib之中
dll示例:小的C程序和dlTest
动态链接放入器示例程序是一个小的C程序,该程序被设计拿来练习dl类库。该程序基于每位人都编撰过的一个C程序,它将“HelloWorld”打印到控制台上。最初复印的消息是“HeLlOWoRlD”。该测试程序链接到再度复印该消息的两个函数上:第一次都用小写字符,第二次都用大写字符。
以下是该程序的概要:
定义dllinclude文件dlfcn.h和所需的变量。起码须要这种变量:
到共享库文件的句柄
指向被映射函数入口点的表针
指向错误字符串的表针
复印初始消息,“HeLlOWoRlD”。
使用绝对路径“/home/dlTest/UPPERCASE.so”和选项RTLD_LAZY,dlopen打开UPPERCASEdll的共享目标文件并返回句柄。
选项RTLD_LAZY延后解析dll的外部引用,直至dll被执行。
选项RTLD_NOW在dlopen返回之前解析所有的外部引用。
dlsym返回入口点printUPPERCASE的地址。
调用printUPPERCASE而且复印更改过的消息“HELLOWORLD”。
dlclose关掉到UPPERCASE.so的句柄linux动态链接库软件,但是从显存中取消dll映射。
dlopen使用基于环境变量LD_LIBRARY_PATH的相对路径查找共享目标路径,来打开lowercasedll的共享目标文件lowercase.so,但是返回句柄。
dlsym返回入口点printLowercase的地址。
调用printLowercase而且复印更改过的信息“helloworld”。
dlclose关掉到lowercase.so的句柄,但是从显存中取消dll映射。
注意,每次调用dlopen、dlsym或dlclose以后,调用dlerror以获取最后的错误信息,但是复印该错误信息字符串。以下是dlTest的测试运行:
dlTest2-Originalmessage
HeLlOWoRlD
dlTest3-OpenLibrarywithabsolutepathreturn-(null)-
dlTest4-FindsymbolprintUPPERCASEreturn-(null)-
HELLOWORLD
dlTest5-printUPPERCASEreturn-(null)-
dlTest6-Closehandlereturn-(null)-
dlTest7-OpenLibrarywithrelativepathreturn-(null)-
dlTest8-FindsymbolprintLowercasereturn-(null)-
helloworld
dlTest9-printLowercasereturn-(null)-
dlTest10-Closehandlereturn-(null)-
完整的dlTest.c、UPPERCASE.c和lowercase.c源代码清单在本文前面的清单里。
建立dlTest
启用运行时动态链接须要三步:
将dll编译为位置无关代码
创建dll共享目标文件
编译主程序并同dl库相链接
编译UPPERCASE.c和lowercase.c的gcc命令包含-fpic选项。选项-fpic和-fPIC造成生成的代码是位置无关的linux动态链接库软件,重建共享目标库须要位置无关。-fPIC选项形成位置无关的代码,这类代码支持大偏斜。用于UPPERCASE.o和lowercase.o的第二个gcc命令,带有-shared选项,该选项形成适宜于动态链接的共享目标文件a*.so。
用于编译和执行dltest的ksh脚本如下:
#!/bin/ksh
#Buildsharedlibrary
#set-x
clear
#Sharedlibraryfordlopenabsolutepathtest
if[-fUPPERCASE.o];thenrmUPPERCASE.o
fi
gcc-c-fpicUPPERCASE.c
if[-fUPPERCASE.so];thenrmUPPERCASE.so
fi
gcc-shared-lc-oUPPERCASE.soUPPERCASE.o
#Sharedlibraryfordlopenrelativepathtest
exportLD_LIBRARY_PATH=`pwd`
if[-flowercase.o];thenrmlowercase.o
fi
gcc-c-fpiclowercase.c
if[-flowercase.so];thenrmlowercase.so
fi
gcc-shared-lc-olowercase.solowercase.o
#Rebuildtestprogram
if[-fdlTest];thenrmdlTest
fi
gcc-odlTestdlTest.c-ldl
echoCurrentLD_LIBRARY_PATH=$LD_LIBRARY_PATH
dlTest
结束语
创建能在运行时被动态链接到Linux系统上的应用程序的共享目标代码是一项十分简单的练习。应用程序通过使用对动态链接放入器的dlopen、dlsym和dlclose函数调用来获取对共享目标文件的访问。dlerror以字符串的方式返回任何错误,这种错误信息字符串描述dl函数遇到的最后一个错误。在运行时,主应用程序使用绝对路径或相对于LD_LIBRARY_PATH的相对路径找到共享目标库,但是恳求所需的dll入口点的地址。当须要时,也可对dll进行间接函数调用,最后,关掉到共享目标文件的句柄,但是从显存中取消该目标文件映射,使之不可用。
使用附加选项-fpic或-fPIC编译共享目标代码,以形成位置无关的代码,使用-shared选项将目标代码放进共享目标库中。
Linux中的共享目标代码库和动态链接放入器向应用程序提供了额外的功能。降低了c盘上和显存里的可执行文件的大小。可以在须要时,放入可选的应用程序功能,可以在无须重新建立整个应用程序的情况下修正缺陷,但是应用程序可以包含第三方的插件。