Skip to content

动态链接库

链接

将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。链接分为静态链接和动态链接

链接可以在编译时由静态编译器来完成,也可以在加载时和运行时由动态链接器来完成。

链接器处理称为目标文件的二进制文件,它有3钟不同的形式:可重定位的可执行的可共享的

可重定位的目标文件由静态链接器合并成一个可执行的目标文件,它可以加载到内存中并执行。

共享目标文件(共享库)是运行时由动态链接器链接和加载的,或者隐含地在调用程序被加载和开始执行时,或者根据需要在程序调用dlopen函数时。

链接器的两个主要任务是符号解析重定位,符号解析将目标文件中的每个全局符号都绑定到一个唯一的定义,而重定位确定每个符号的最终内存地址,并修改对哪些目标的引用。

静态链接

函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。

空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;

更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。

动态链接

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分副本,而是这多个程序在执行时共享同一份副本;

更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。

性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

DLL开发

在应用程序中加载 DLL 时,两种链接方法让你可以调用导出的 DLL 函数。 链接的两个方法是加载时动态链接和运行时动态链接。

加载时动态链接

在加载时动态链接中,应用程序显式调用导出的 DLL 函数,如本地函数。 若要使用加载时动态链接,在编译和链接应用程序时,请提供标头 (.h) 文件和导入库 (.lib) 文件。 当您这样做时,链接器会向系统提供加载 DLL 和在加载时解析导出的 DLL 函数位置所需的信息。

运行时动态链接

在运行时动态链接中,应用程序会调用 LoadLibrary 函数 LoadLibraryEx 或 函数以运行时加载 DLL。 成功加载 DLL 后,使用 函数获取要调用的导出 GetProcAddress DLL 函数的地址。 使用运行时动态链接时,不需要导入库文件。

以下列表介绍了何时使用加载时动态链接以及何时使用运行时动态链接的应用程序条件:

  • 启动性能

如果应用程序的初始启动性能很重要,则应该使用运行时动态链接。

  • 易用性

在加载时动态链接中,导出的 DLL 函数与本地函数类似。 这便于调用这些函数。

  • 应用程序逻辑

在运行时动态链接中,应用程序可以分支以按需要加载不同的模块。 开发多种语言版本时,这一点很重要。

DLL 入口点

创建 DLL 时,可以选择指定入口点函数。 当进程或线程将自己附加到 DLL 或将自己与 DLL 分离时,将调用入口点函数。 可以使用入口点函数初始化数据结构或破坏 DLL 所需的数据结构。 此外,如果应用程序是多线程的,可以使用线程本地存储 (TLS) 分配入口点函数中每个线程专用的内存。 以下代码是 DLL 入口点函数的一个示例。

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
    switch ( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACHED: // A process is loading the DLL.
        break;
        case DLL_THREAD_ATTACHED: // A process is creating a new thread.
        break;
        case DLL_THREAD_DETACH: // A thread exits normally.
        break;
        case DLL_PROCESS_DETACH: // A process unloads the DLL.
        break;
    }
    return TRUE;
}