设为首页  
联系我们  
加入收藏  
网页制作 冲浪宝典 图形图像 操作系统 软件教学 编程开发 认证考试 安全技术 站长专区 文学驿站 娱乐天地 游戏天地 办公软件
文章搜索
您的位置: 首页 >> 文章首页 >> 编程开发 >> 其他开发语言 >> Modules, Processes, Threads
精品推荐
其他开发语言点击TOP10
·数字小键盘指法练习
·用C语言编通讯录程序(初学者级别的)
·Modem 常用AT指令集
·单片机模拟I2C总线及24C02(I2C EEPROM)读写实例(源代码)
·C++经典电子书下载
·Thinking in C++ 简体中文第二版
·debug和release的区别
·error LNK2001: unresolved external symbol __ftol2 错误解决
·C库函数手册
·一个简单的C语言编译器
编程开发点击TOP10
·数字小键盘指法练习
·ASP.NET 程序中常用的三十三种代码
·用C语言编通讯录程序(初学者级别的)
·我写的Java学生成绩管理系统源代码
·CHK文件恢复工具
·Modem 常用AT指令集
·java笔试题
·异常java.sql.SQLException: Io exception:The Network Adapter could not establish connection
·单片机模拟I2C总线及24C02(I2C EEPROM)读写实例(源代码)
·C++经典电子书下载
精选专题

Modules, Processes, Threads

作者: 来源:网络文章 时间:2005-12-17 18:48:16

Modules, Processes, Threads(4)     记住,上述 8 字节的结构只是操作系统的最小需求而已。没有什么可以阻止编译器在堆栈之中产生更大的结构并且把 EXCEPTIONREGISTRATIONRECORD 放在结构起首。编译器从上述结构中获得的其它字段可以提供足够的信息,使单独一个异常处理函式适用于所有的 __try 区块。微软公司和 Borland 公司的编译器都使用EXCEPTIONREGISTRATIONRECORD 的扩充结构。

说到异常处理函式,到底它长得什么样子?再一次,微软似乎企图隐藏某些信息,但至少 Win32 表头檔提供了一个函式原型。在 EXCPT.H 檔中,你可以看到这样的原型:

EXCEPTION_DISPOSITION __cdecl _except_handler (

struct _EXCEPTION_RECORD *ExceptionRecord,

void * EstablisherFrame,

struct _CONTEXT *ContextRecord,

void * DispatcherContext

);

乍见之下它彷佛太过复杂了。传回值 EXCEPTION_DISPOSITION 其实只不过是个enum,告诉系统说此一函式如何被用来处理异常情况:

typedef enum _EXCEPTION_DISPOSITION {

ExceptionContinueExecution,

ExceptionContinueSearch,

ExceptionNestedException,

ExceptionCollidedUnwind

} EXCEPTION_DISPOSITION;

最后两项很少会遇到。至于第一项 ExceptionContinueExecution 是告诉系统说异常处理函式已经处理了该异常情况,并打算让执行继续下去。ExceptionContinueSearch 则是告诉系统说异常处理例程不打算处理这个异常情况,系统应该继续走访EXCEPTIONREGISTRATIONRECORD 串行, 直到某个处理函式传回ExceptionContinueExecution。把 _except_handler 函式原型重写一遍,看起来就可接受多了:

int _except_handler (

PEXCEPTION_RECORD ExceptionRecord,

PVOID EstablisherFrame,

PCONTEXT ContextRecord,

PVOID DispatcherContext );

我们发现,一个异常处理函式需要四个指标参数,指向异常情况以及机器状态等信息。这个函式传回一个整数,告诉系统它是否把异常处理好了。EXCEPTION_RECORD 结构内含异常代码,以及其它东西。WINNT.H 对此有些说明。CONTEXT 结构内含异常发生时的缓存器内容,WINNT.H 对此也有说明。EstablisherFrame 参数内含一个指标,指向堆栈 -- EXCEPTIONREGISTRATIONRECORD 结构的设定处。DispatcherContext 参数则似乎没有用到。稍早我曾说过,处理函式会被呼叫两次。第一次是系统寻找适当处理者的时候。第二次是为了系统要 "unwinding",而处理函式被认为会进行任何必要的清理工作(像是呼叫堆迭物件的解构式等等)。这两次启动之间的差别在哪里?ExceptionRecord 结构(被第一个参数所指) 内含一个 ExceptionFlags 旗标, 如果 EH_UNWINDING 0x2 ) 或EH_EXIT_UNWIND0x4)旗标并未设立,那么就是前述第一种情况;如果两个旗标之中有一个设立,那么就是前述第二种情况。

虽然我所说的不足以让你写一个自己的操作系统层面的异常情况处置码,但是因为足够让你了解 SEH 是如何运作的了。为了证明我不是在胡乱吹大气,我写了一个SHOWSEH程序,放在书附磁盘之中。SHOWSEH 利用 __try 段落来设立异常处理串链。统统设立好之后,这个程序即走访 SEH 串行并印出每一个节点的内容。SHOWSEH 的输出结果请看3-4。我要你注意数点。第一,请注意 "next rec" 字段总是递增,那是编译器用来放置 EXCEPTIONREGISTRATIONRECORD 的堆栈区域。最前面四笔资料反应出 SHOWSEH.C 中的 __try 段落。第二,请注意前四笔资料有一个不变的 ESP 值。输出画面的最后一行则显示 SHOWSEH.C 之中每一个函式的 ESP 值。offset of __except_handler3: 00401468

next rec handler

======== =======

0063FD90 00401468

0063FDC0 00401468

0063FDF0 00401468

0063FE30 00401468

0063FF68 00401468

FFFFFFFF BFFC2D18

in c(), ESP = 0063FD84

in b(), ESP = 0063FD78

in a(), ESP = 0063FDA8

in main(), ESP = 0063FDD8

最后一件值得注意的事情是,最前面五笔数据的 handler 字段都一样,地址落于SHOWSEH 的程序区域(而非数据区域)中,显示出编译器所产生的码对于每一个 __try段落都使用相同的异常处理函式。刚才我说过了,前四笔是SHOWSEH.C 的四个 __try 形成的。至于第 5 个则是在呼叫 main 之前由执行时期函式库安装的。这些处理例程的位址统统都是 __except_handler3 -- Visual C++ 的执行时期函式库中的一个函式。最后一个异常处理函式是预设的系统处理函式,位于 KERNEL32.DLL 中。

Thread Local Storage(执行绪区域储存空间)

TLS 是一个良好的 Win32 特质,让多执行绪程序设计更容易一些。TLS 是一个机制,经由它,程序可以拥有全域变量,但处于「每一执行绪各不相同」的状态。也就是说,行程中的所有执行绪都可以拥有全域变量,但这些变量其实是特定对某个执行绪才有意义。例如,你可能有一个多执行绪程序,每一个执行绪都对不同的档案写檔(也因此它们使用不同的档案 handle)。这种情况下,把每一个执行绪所使用的档案 handle 储存在 TLS 中,将会十分方便。当执行绪需要知道所使用的 handle,它可以从 TLS 获得。重点在于:执行绪用来取得档案 handle 的那一段码在任何情况下都是相同的,而从 TLS中取出的档案 handle 却各不相同。非常灵巧,不是吗?有全域变数的便利,却又分属各执行绪。

当然,你可以使用串行,让一个档案 handle 与一个 thread ID 产生关系,每一个执行绪有一个节点。用此来模拟 TLS。当执行绪需要知道它使用哪一个档案 handle,它可以从串行中寻找档案 handle。你当然可以把档案 handle 储存在区域变量中(位于执行绪的堆栈)。但是却因此必须把这个 handle 在函式与函式之间传来传去。那多痛苦。TLS 可以利用简单的 alloc/set/get/free 函式消除这些问题。虽然 TLS 很方便,它并不是毫无限制。在Windows NT Windows 95 之中,有 64 DWORD slots 供每一个执行绪使用。这意思是一个行程最多可以有64 个「对各执行绪有不同意义」的DWORDs。为了在每个执行绪中保留一个slot,程序应该呼叫 TlsAlloc。每次呼叫 TlsAlloc 就传回一个可被所有执行绪使用的索引值。这个索引值常常被储存在全域变数中。当执行绪要对一个 slot 写入数据,它使用 TlsSetValue,交待一个 TLS 索引以及一笔数据。稍后当执行绪要取出此值,它呼叫TlsGetValue,再次交待一个 TLS 索引。最后,程序呼叫TlsFree 并交待一个TLS 索引,将slot 释放掉。这么一来当然也就让 slot 不再能够被任何执行绪使用,因为TLS 索引值在各执行绪之间是共通的。

虽然 TLS 可以存放单一数值如档案 handle,更常的用途是放置指标,指向执行绪的私有资料。有许多情况,多执行绪程序需要储存一堆数据,而它们又都是与各执行绪相关。许多程序员对此的作法是把这些变量包装为C 结构,然后把结构指针储存在 TLS 中。当新的执行绪诞生,程序就配置一些内存给该结构使用,并且把指针储存在为执行绪保留下来的 TLS 中。一旦执行绪结束,程序代码就释放所有配置来的区块。这种程序风格的最佳示范就是第 10 章的 APISPY32APISPY32.DLL 需要保持一个堆迭,用来传回它所拦截的函式地址(我在这里使用古典的计算器科学术语「堆栈」,事实上我指的是一个结构数组,以及一个堆栈指针)。由于被拦截的程序可能有许多执行绪,APISPY32.DLL 必须针对每一个执行绪保留各自的传回地址。如果每一个执行绪有 64 slots 用来储存执行绪自己的数据,这些空间打哪儿来?稍早我曾说过,每一个 thread database 64 DWORDs TLS 使用。当你以 TLS 函式设定或取出数据,事实上你真正面对的就是那 64 DWORDs。没有任何公开文件告诉我们可以存取其它执行绪的 TLS。让我们更详细地看看这些 TLS 函式。

 

TlsAlloc

由于 TLS 只提供最多 64 slots 给每一个执行绪使用,所以必须有某种方法追踪哪一个slot 已被使用。KERNEL32 使用两个 DWORDs(总共 64 个位)来记录哪一个 slot 是可用的、哪一个 slot 已经被用。这两个 DWORDs 可想象成为一个 64 位数组,如果某个位设立,就表示它对应的 TLS slot 已被使用。这 64 TLS slot 数组存放在 process database 中(可能你会猜想在thread database中,不,不是这样)。记住,当你配置一个 TLS slot,这个 slot 可以在行程所属的任何执行绪中被该索引值参考到。64 位的 TLS slots 数组放在 process database 0x88 0x8C 两个 DWORD 字段。虽然下面的TlsAlloc 函式伪码可能看起来有点复杂,事实上并不会。其中所做的只不过是扫描 64 位数组中的位,看看有没有哪一个是 0。如果找到,就把它改为 1,并传回其数组位置(索引值)。因此,如果第 5 个位是 0TlsAlloc 就把它改为 1 并传回 4(索引值从 0 开始)。

 

TlsSetValue

TlsSetValue 可以把数据放入先前配置到的 TLS slot 中。两个参数分别是TLS slot 索引值以及欲写入的数据内容。函式首先检查数组索引是否合法(小于 64)。Windows 95 的早期版本还会检查此一索引是否的确被配置了,但是在beta3 版本中就只做上述的最简单检查。如果索引值的确小于 64TlsSetValue 就把你指定的数据放入 64 DWORDs 所组成的数组(位于目前的 thread database)的适当位置中。除此之外,TlsSetValue 还更新第二个 64 DWORDs 数组。这个数组内含 EIP 值,TlsSetValue 上一次就是在那里被呼叫。很明显,这些 EIP 是为了除错用的。微软并没有提供什么方法让应用程序取用这块数据。

 

TlsGetValue

这个函式几乎是 TlsSetValue 的一面镜子,最大的差异是它取出数据而非设定数据。和TlsSetValue 一样,这个函式也是先检查 TLS 索引值合法与否。如果是,TlsGetValue 就使用这个索引值找到 64 DWORDs 数组(位于 thread database 中)的对应数据项,并将其内容传回。

 

TlsFree

这个函式将 TlsAlloc TlsSetValue 的努力全部抹消掉。TlsFree 先检验你交给它的索引值是否的确被配置过。如果是,它将对应的 64 TLS slots 位关闭。然后,为了避免那个已经不再合法的内容被使用,TlsFree 巡访行程中的每一个执行绪,把 0 放到刚刚被释放的那个 TLS slot 上头。于是呢,如果有某个 TLS 索引后来又被重新配置,所有用到该索引的执行绪就保证会取回一个0 值,除非它们再呼叫 TlsSetValue

 

 

共5页 9 7 [1] [2] [3] [4] [58 :>

Modules, Processes, Threads 相关文章:
Modules, Processes, Threads 相关软件:
特别声明:本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。
转载请注明来源:http://www.xgdown.com