一些DLL的运行机制

来源:互联网 发布:傲飞数据整合工具 编辑:程序博客网 时间:2024/04/27 21:40
创建DLL常常比创建应用程序更容易,因为DLL往往包含一组应用程序可以使用的自主函数。在DLL中通常没有用来处理消息循环或创建窗口的支持代码。 DLL只是一组源代码模块,每个模块包含了应用程序(可执行文件)或另一个DLL将要调用的一组函数。当所有源代码文件编译后,它们就像应用程序的可执行 文件那样被链接程序所链接。但是,对于一个DLL来说,你必须设定该连链程序的/DLL开关。这个开关使得链接程序能够向产生的DLL文件映像发出稍有不 同的信息,这样,操作系统加载程序就能将该文件映像视为一个DLL而不是应用程序。 

  在应用程序(或另一个DLL)能够调用DLL中的函数之前,DLL文件映像必须被映射到调用进程的地址空间中。若要进行这项操作,可以使用两种方法中的一种,即加载时的隐含链接或运行期的显式链接。隐含链接将在本章的后面部分介绍,显式链接将在第20章中介绍。 

  一旦DLL的文件映像被映射到调用进程的地址空间中,DLL的函数就可以供进程中运行的所有线程使用。实际上,DLL几乎将失去它作为DLL的全部特征。对于进程中的线程来说,DLL的代码和数据看上去就像恰巧是在进程的地址空间中的额外代码和数据一样。当一个线程调用DLL函数时,该DLL函数要查看线程的堆栈,以便检索它传递的参数,并将线程的堆栈用于它需要的任何局部变量。此外,DLL中函数的代码创建的任何对象均由调用线程所拥有,而DLL本身从来不拥有任何东西。对于此,我的理解是:dll提供的实际上只有一串命令,局部变量都由各自的线程去分配和占有,主进程调用线程提供的函数的时候,只是读取命令,然后操作各自堆栈里的变量数据,只要dll提供的函数里面不加锁或者使用全局变量

  例如,如果VirtualAlloc函数被DLL中的一个函数调用,那么将从调用线程的进程地址空间中保留一个地址空间的区域,该地址空间区域将始终处于保留状态,因为系统并不跟踪DLL中的函数保留该区域的情况。保留区域由进程所拥有,只有在线程调用VirtualFree函数或者进程终止运行时才被释放。 

  如你所知,可执行文件的全局变量和静态变量不能被同一个可执行文件的多个运行实例共享。Windows98能够确保这一点,方法是在可执行文件被映射到进程的地址空间时为可执行文件的全局变量和静态变量分配相应的存储器。Windows2000确保这一点的方法是使用第13章介绍的写入时拷贝(copy-on-write)机制。DLL中的全局变量和静态变量的处理方法是完全相同的。当一个进程将DLL的映像文件映射到它的地址空间中去时,系统将同时创建全局数据变量和静态数据变量的实例。 

   注意 必须注意的是,单个地址空间是由一个可执行模块和若干个DLL模块组成的。这些模块中,有些可以链接到静态版本的C/C++运行期库,有些可以链 接到一个DLL版本的C/C++运行期库,而有些模块(如果不是用C/C++编写的话)则根本不需要C/C++运行期库。许多开发人员经常会犯一个常见的 错误,因为他们忘记了若干个C/C++运行期库可以存在于单个地址空间中。请看下面的代码:
VOID EXEFunc(){ 

    PVOID pv = DLLFunc(); 

    //Access the storage pointed to by pv... 

    //Assumes that pv is in EXEs C/C++ run-time heap 

    free(pv); 



PVOID DLLFunc(){ 

    // Allocate block from DLLs C/C++ run-time heap 

    return(malloc(100)); 


那么你是怎么看待这个问题的呢?上面这个代码能够正确运行吗?DLL函数分配的内存块是由EXE的函数释放的吗?答案是可能的。上面显示的代码并没有为你 提供足够的信息。如果EXE和DLL都链接到DLL的C/C++运行期库,那么上面的代码将能够很好地运行。但是,如果两个模块中的一个或者两个都链接到 静态C/C++运行期库,那么对free函数的调用就会失败。我经常看到编程人员编写这样的代码,结果都失败了。 

  有一个很方便的方法可以解决这个问题。当一个模块提供一个用于分配内存块的函数时,该模块也必须提供释放内存的函数。让我们将上面的代码改写成下面的样子: 
VOID EXEFunc(){ 

    PVOID pv = DLLFunc(); 

    //Access the storage pointed to by pv... 

    //Assumes that pv is in EXEs C/C++ run-time heap 

    DLLFreeFunc(pv); 



  

PVOID DLLFunc(){ 

    // Allocate block from DLLs C/C++ run-time heap 

    PVOID pv = malloc(100); 

    return(pv); 



  

BOOL DLLFreeFunc(PVOID pv){ 

    //Free block from DLLs C/C++ run-time heap 

    return(free(pv)); 



这个代码是正确的,它始终都能正确地运行。当你编写一个模块时,不要忘记其他模块中的函数也许没有使用C/C++来编写,因此可能无法使用malloc和 free函数进行内存的分配。应该注意不要在代码中使用这些假设条件。另外,在内部调用malloc和free函数时,这个原则对于C++的new和 delete操作符也是适用的。