dll之所以可以节约内存空间,是因为在内存中只加载一次。当多个应用程序要调用它时是动态从内存中加载的,此处的加载其实就是拷贝一份该dll的映像到自己的空间。当一个应用程序当发出加载一个 DLL 的请求时,内核首先检查该 DLL 是否先前已被另一个应用程序加载到内存中去了,如果已经在内存中了则拷贝该dll映像到自己的空间;如果没有则将该dll加载到内存并拷贝该dll一份映像到自己的空间。
      对于进程中的线程来说,dll的代码和数据看上去就像恰巧是在进程的地址空间中的额外代码和数据一样,当一个线程调用dll函数时,该dll函数要查看线程的堆栈,以便检索它传递的参数,并将线程的堆栈用于它需要的任何局部变量。此外,dll中函数的代码创建的任何对象均由调用线程所拥有,而dll本身从来不拥有任何东西。当一个进程将dll的映像文件映射到它的地址空间中去时,系统将同时创建全局数据变量和静态数据变量的实例。
当一个进程被创建的时候,系统将分配进程的地址空间,然后将.exe文件映像和所有需要的dll文件映像映射到进程的地址空间中。然后它创建进程的主线程,并使用该线程调用每个dll的带有DLL_PROCESS_ATTACH值的DllMain函数。当已经映射的dll都对通知消息作出响应后,系统将使进程的主线程开始执行可执行模块的c/c++运行期启动代码,然后执行可执行模块的进入点函数(main,winmain或wwinmain)。
      当在一个进程中创建线程时,系统要查看当前映射到该进程的地址空间中的所有dll文件映像,并调用每个文件映像的带有DLL_THREAD_ATTACH值的DllMain函数。这可以告诉所有的dll执行每个线程的初始化操作。新创建的线程负责执行dll的所有DllMain函数中的代码。只有当所有的dll都有机会处理该通知时,系统才允许新线程开始执行它的线程函数。(当一个新dll被映射到进程的地址空间时,如果该进程内已经有若干个线程正在运行,那么系统将不为现有的线程调用带有DLL_THREAD_ATTACH值的dll的DllMain函数。系统也不为主线程调用带有DLL_THREAD_ATTACH值的dll的DllMain函数。)