第400页 建造DLL
hao
读过 Win32多线程程序设计
- 章节名:建造DLL
- 页码:第400页
DLL 的运作方式和 Unix 中的 shared library 有很大的不同。虽然最后的 结果类似,但其机制并不相似。DLL 是一个链接实体,有它自己的实际 生存事实。DLL 有自己的数据,独立于进程之外;有它自己的模块识别 代码(ID),可以被静态链接,也可以被动态链接(译注)。 DLL 可以被静态链接也可以被动态链接”,意思是指DLL 的 explicitly linking 和 implicitly linking 两种方式。前者是在程序中很明显地以 LoadLibrary() 将 DLL 载入,后者是在链接时期静态链接 DLL 的一个“替身”,也就是其 import library(输入函数库,或者说是导入库)。 引自 建造DLL DLL 的通告消息(Notifications)
如果你使用 C runtime library(在DLL的源码中),那么你应该使用 DllMain() 名称。但如果你没有使用 C runtime library(这是很希罕的情况),你可以利用链接器选项 /ENTRY 定义你自己喜欢的函数名称。 任何时候,当一个进程载入或卸载一个DLL 时,DllMain() 函数会被调用。线程也是一样。当一个进程开始执行时,它所用到的每一个 DLL 的DllMain() 都会被系统调用之,并获得 DLL_PROCESS_ATTACH 消息。如果是线程开始执行,进程所用到的每一个 DLL 的 DllMain() 也都会被系统调用之,并获得 DLL_THREAD_ATTACH 消息。 DllMain() 分别被以 DLL_PROCESS_ATTACH 和 DLL_THREAD_ATTACH 各调用一次。这令我 惊讶,因为我明明有两个线程。噢,原来 Win32 定义的这个机制,使得每一 个程序的第一个线程调用 DllMain() 时,是以 DLL_PROCESS_ATTACH 调 用之。所有后续的线程才是以 DLL_THREAD_ATTACH 调用之。 第二个重点是,DllMain() 系在新线程的 context 中被调用。这一点非常 重要,因为你需要一个 context,才能够使用线程局部存储(TLS) 引自 建造DLL 抑制通告消息(Disabling Notifications)
关于“DllMain() 在线程的 context 中被调用”这件事,有一些分歧。很 明显地,程序 MAIN1 从未直接调用 DllMain()。DllMain() 是自动被调用, 你可以视之为 CreateProcess()(那是 Windows 调用的)或 CreateThread()(那 是 MAIN1 调用的)的副作用。现在我们看看,如果有5个、10 个、甚至 20 个 DLLs 被附着到进程之中的情况。每当你启动一个新线程,每一个 DLLs 的 DllMain() 都会被调用。突然之间你发现多了好多预期之外的负担。 为了避免这个问题,Win32 提供了一个 DisableThreadLibraryCalls() 函 数。该函数只在 Windows NT 中才有,在 Windows 95 中没有效用。你可以 一一指定不需要通告消息的 DLLs。如果一个程序常常产生新的线程,这么一 点小小的最优化操作会节省不少的负担唷。 引自 建造DLL 初始化失败
最后还有一个关于通告消息的特性是你必须知道的。如果 DllMain() 收到 DLL_PROCESS_ATTACH 时因不能够正确初始化而传回 FALSE,DllMain() 还是会收到 DLL_PROCESS_DETACH。因此,你必须非常小心地在 FALSE 被传回之前,将每一个有价值的变量初始化为一个已知状态,如此一来, DLL_PROCESS_DETACH 的处理函数才不会因为乱指的指针或是不合理的 数组索引而当掉。 引自 建造DLL 通告消息(Notification)摘要
由于上述讨论的那些题目,你必须因此非常小心地安排你的 DllMain() 函 数。理想中你最好是像我那样,写一些简单的驱动程序,确保 ATTACH 和 DETACH 通告消息都能够被正确地处理——甚至即使它们遗漏掉了或是乱了 次序。 i 如果进程调用 LoadLibrary 时,有一个以上的线程正在运行,那么 DLL_THREAD_ATTACH 不会针对每一个线程送出。(译注:只有调用 LoadLibrary 的那个线程才会发出) i DllMain() 不接受第一个线程的 DLL_THREAD_ATTACH, 而以 DLL_PROCESS_ATTACH 取代之。 i DllMain() 不接收任何因 TerminateThread() 而结束之线程的 DLL_THREAD_DETACH 通告消息。如果程序调用 exit(1) 或 ExitProcess() 结束自己,这种情况就会发生。 引自 建造DLL DLL 进入点的依序执行(Serialization)特性
我们还没有完成对 DLL 进入点的讨论。Win32 设计之初,下面的问题必 须解决: 1. 线程1调用 LoadLibrary(),会送出 DLL_PROCESS_ATTACH。 2. 当 DLL 正在处理 DLL_PROCESS_ATTACH , 线程2 调用 LoadLibrary(),于是送出 DLL_THREAD_ATTACH。 3. DllMain()开始处理DLL_THREAD_ATTACH——在DLL_PROCESS _ATTACH 未完成之前。 一个 race condition 于焉诞生,操作系统不能给你任何帮助。为了解决这 个问题,Win32 必须依序调用所有 DLLs 的 DllMain() 函数。在一个进程之 中,一次只有一个线程能够执行一个 DLL 的 DllMain()。事实上,每一个线 程依次调用每一个附着的 DLLs 的 DllMain() 函数。 偶尔你会进入一种副作用之中。如果你在 DllMain() 内产生一个线程,该 线程可能没有办法在其他数个 DllMain() 完成之前顺利初始化它自己。因此, 你不可能在 DllMain() 中启动一个线程,等待它初始化,然后才继续执行下去。 若想要生产 DLLs 以便安全地在多线程环境中使用,下面是几个大方针: i 不要使用全局变量。用来储存 TLS 槽(slot)者例外。 i 不要使用静态变量。 i 如有必要,尽量使用 TLS(Thread Local Storage)。 i 如有必要,尽量使用你的堆栈。 引自 建造DLL
77人阅读
hao对本书的所有笔记 · · · · · ·
-
第305页 MFC中的线程
如果要在 MFC 程序中产生一个线程,而该线程将调用 MFC 函数或使用 MFC 的任何数据, 那么你...
-
第365页 进程之间的通讯
虽然我们已经讨论过,使用多重线程比使用多重进程的好处,但还是可 能因为某些理由使你决定使...
-
第400页 建造DLL
> 查看全部8篇
说明 · · · · · ·
表示其中内容是对原文的摘抄