在仅仅只会编写插件的时候为什么要编写整个应用程序?
插件和 DLL 通常是用来无须编写整个新应用程序而添加功能的极好方法。在 Linux 中,插件和 DLL 以动态库形式实现。电子商务顾问兼设计师 Allen Wilson 介绍了动态库并且向您演示了如何在某个应用程序正在运行之后使用动态库来改变该应用程序。
Internet 浏览器用户非常熟悉插件的概念。从 Web 上下载插件,通常这些插件为浏览器的音频、视频以及特殊效果提供增强支持。一般来讲,在不更改原有应用程序的情况下,插件为现有应用程序提供新功能。
DLL 是程序函数,它们在设计和构建应用程序时为该程序所知。设计应用程序的主程序时使用程序框架或底板,这些程序框架或底板在运行时选择性地装入所需的 dll,这些 dll 位于磁盘上同主程序分离的一些文件中。这一打包和动态装入提供了灵活的升级、维护、以及许可策略。
随 Linux 一起交付的还有几千条命令和应用程序,它们至少都需要 libc 库函数。如果 libc 函数与每一个应用程序都打包在一起,那么磁盘上将会出现几千个相同函数的副本。Linux 构建这些应用程序,以使用通常所需的系统库的单个系统级副本,而不浪费磁盘空间。Linux 甚至做得更好,每个需要公共系统库函数的进程使用单个的系统级内的副本,一次性将该副本装入到内存并为各进程所共享。
在 Linux 中,插件和 dll 以动态库形式实现。本文的余下部分是在应用程序运行之后使用动态库更改该应用程序的示例。
Linux 动态链接
Linux 中的应用程序以以下两种方式之一链接到外部函数:要么在构建时与静态库( lib*.a)静态地链接,并且将库代码包含在该应用程序的可执行文件里;要么在运行时与共享库( lib*.so)动态地链接。通过动态链接装入器,将动态库映射进应用程序的可执行内存中。在启动应用程序之前,动态链接装入器将所需的共享目标库映射到应用程序的内存,或者使用系统共享的目标并为应用程序解析所需的外部引用。现在应用程序就可以运行了。
作为示例,下面有一个演示 Linux 中对动态链接库的缺省使用的小程序:
main()
{
printf("Hello world
");
}
当使用 gcc 编译 hello.c 时,就创建了一个名为 a.out 的可执行文件。通过使用 Linux 命令 ldd a.out(该命令打印出共享库的相互依赖性),可以看出所需的共享库是:
libc.so.6 => /lib/libc.so.6 (0x4001d000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
使用相同的动态链接装入器在应用程序运行之后将 dll 映射进应用程序的内存。通过使用 Linux 动态装入器例程,应用程序控制装入哪一个动态库以及调用库中的哪一个函数,以执行装入和链接以及返回所需入口点的地址。
Linux dll 函数
Linux 提供 4 个库函数(dlopen, dlerror, dlsym 和 dlclose),一个 include 文件(dlfcn.h)以及两个共享库(静态库 libdl.a 和动态库 libdl.so),以支持动态链接装入器。这些库函数是:
◆dlopen 将共享目标文件打开并且映射到内存中,并且返回句柄。
◆dlsym 返回一个指向被请求入口点的指针。
◆dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针。
◆dlclose 关闭句柄并且取消共享目标文件的映射。
动态链接装入器例程 dlopen 需要在文件系统中查找共享目标文件以打开文件并创建句柄。有4种方式用以指定文件的位置:
◆dlopen call 中的绝对文件路径。
◆在 LD_LIBRARY_PATH 环境变量中指定的目录中。
◆在 /etc/ld.so.cache 中指定的库列表之中。
◆先在 /usr/lib 之中,然后在 /lib 之中。
DLL示例:小的C程序和dlTest
动态链接装入器示例程序是一个小的 C 程序,该程序被设计用来练习 dl 例程。该程序基于每个人都编写过的一个 C 程序,它将“Hello World”打印到控制台上。最初打印的消息是“HeLlO WoRlD”。该测试程序链接到再次打印该消息的两个函数上:第一次都用大写字符,第二次都用小写字符。
以下是该程序的概要:
定义 dll include 文件 dlfcn.h 和所需的变量。至少需要这些变量:
到共享库文件的句柄;
指向被映射函数入口点的指针;
指向错误字符串的指针。
打印初始消息,“HeLlO WoRlD”。
使用绝对路径“/home/dlTest/UPPERCASE.so”和选项 RTLD_LAZY,dlopen 打开 UPPERCASE dll 的共享目标文件并返回句柄。