Windows运行机理——线程的机制(3)

来源:互联网 发布:手机点单软件 编辑:程序博客网 时间:2024/06/02 02:16
作者:Rookie.Zhang  来源:博客园  发布时间:2005-06-09 08:54  阅读:740 次  原文链接  [收藏]  

当Windows 95进程工作时,不用跟踪进程ID。实际上,大部分相关进程API函数期望一个HANDLE参数,通常称做hProcess。hProcess与某些事情(Win16任务数据库)没有直接的关联,与进程ID不一样,可有多重独特的hProcess值,但都属于同一个进程。

KERNEL32对象句柄

句柄渗透着Win32 API。一个句柄就是当需做某件事情时,从操作系统返回给API函数的一个魔数(Magic Value)。理论上讲一句柄值对应用程序是无意义的,只有操作系统知道如何去解释它(几乎所有Win16程序的句柄值可被解释为选择器值或指针)。

当用KERNEL32 API工作时,大部分句柄属于调用KERNEL32的句柄。KERNEL32句柄有专门属性,比如可传递给对象WaitforSingleObject这样的函数。KERNEL32的对象句柄包括进程柄、线程柄、文件柄、Mutex柄等。

一个KERNEL32句柄只有在进程自身内部有效,企图将一个进程柄用于另一个进程是没有意义的。尽管句柄在理论上是透明的,但对一应用程序而言,将一句柄转换成有用的对象指针是可能的。

Windows 95中最基本的进程函数是CreateProcess,这是模拟Win16 WinExec和LoadModule函数,且这两个函数仍存在于Windows 95中,但其内部有些改变。如果需要查询或操作后来的进程,则应使用CreateProcess,即可反馈给你一个hProcess HANLE。

因为WinExec和LoadModule没有hProcess和HANDLE的概念,所以不能返回hProcess。实际上,这两个函数调用CreateProcess以后,立即关闭了CreateProcess返回的hProcess,这样做的目的是防止为那些联系紧密且无必要的进程分配系统资源。

请记住,关闭一个处理并不意味着结束这个进程,相反你可通过特殊处理到该进程进行访问,当进程结束和所有的处理被关闭时,操作系统仔细地清除相关进程资源。

除了产生一进程获取一个hProcess外,另一个方法是有效的进程ID去调用OpenProcess。用hProcess可以做一些基本的进程查询和操作。在进程控制的范围,一个程序可以用TerminateProcess中止另一个进程,用SetPriorityClass影响另一个进程的执行优先权。

学习一下Windows mirror KERNEL是很有趣的,在进程的任务区,每一个Win32进程有16位任务数据库(TDB),并把TDB连接到TDB链上。如果你用TOOLHELP浏览这个任务表,则会看到除了这个16位任务外,每一个正在运行着的Win32程序也有一个TDB,TDB有8个字节的文件名,可重新调用。

除了TDB以外,对16位或32位进程而言,Windows中的所有TDB(包括Win32进程的TDB)还有一个PSP。和Windows 3.x不一样,Window 95 TDB中的PSP没有必要跟着TDB立即进入内存,在TDB和PSP之间的100h字节存放当前目录区,这个区可有效地保存Window 95支持的足够大的长文件名和路径名目录,Windows 3.x中当前目录存放在TDB内一个只有65字长的区域内。

同步机制

 

1. 进程与线程同步

 

同步的意思是一个程序保证在不适宜地被切换时,不会出问题,虽然Windows 3.1有多任务,但没有真正的同步基础,因为这些多任务是协作多过调用API函数(如GetMessage和PeekMessage)。如果一个程序调用了GetMessage或Peekmessage,则意思是说“现在我处在可中断状态”。

Win32程序没有这样的协作多任务。它们必须做好随时被CPU切换掉的准备,一个真正的Win32程序不会耗尽CPU时间等待某些事件发生,Win32 API有四个主要的同步对象:

Ø         Event                           事件

Ø         Seqmaphore                 信号器

Ø         Mutexes                       互斥

Ø         Critical Section             临界段

除Critical Section外,其余是系统全局对象,并且与不同进程及相同进程中的线程一起工作,这样,同步机也可以用于分离进程的同步活动(同一进程内部的线程除外)。

 

2. 事件(Event

 

这是同步对象的一种类型,正如其名字的含义,在这个中心周围是一些发生在另一个进程或线程中的特殊活动。当你希望线程暂时挂起时,不会消耗CPU的工作周期。事件很类似于我们常用的消息的概念。如果我们剖析消息的内核肯定会发现,它就是用事件来实现的。

程序可用CreateEvent或OpenEvent对事件获得一个句柄:

HANDLE CreateEvent(

  LPSECURITY_ATTRIBUTES lpEventAttributes,

                           
// pointer to security attributes

  BOOL bManualReset,     
// flag for manual-reset event

  BOOL bInitialState,   
// flag for initial state

  LPCTSTR lpName          
// pointer to event-object name

);

HANDLE OpenEvent(

  DWORD dwDesiredAccess,     
// access flag

  BOOL bInheritHandle,        
// inherit flag

  LPCTSTR lpName               
// pointer to event-object name

);

然后,该程序再调用 WaitForSingleObject,选定事件柄和暂停周期,那么线程就被挂起,一直到其他线程给出事件有关信号后才被再次激活。其他线程指调用SetEvent或PulseEvent所需活动的线程,事件获得这个信号,被挂起的线程即被唤醒并继续执行。

例如,当一个线程要使用另一个线程的排序结果时,你或许希望去使用一个事件。比较糟的方法是执行这个线程并在结束时设置全局变量标志,另一个线程循环检查这个标志是否已设置,这将浪费许多CPU的时间。用事件(Event)做同样的事情则很简单,排序线程在结束时产生一个事件(Event),其他线程调用WaitForSingleObject。这就使得线程被挂起,不浪费CPU周期,当排序线程完成排序时,调用SetEvent唤醒另一个线程继续执行,有效地利用了CPU。

除了WaitForSingleObject外,还有WaitForMultipleObject允许一个线程被挂起,一直到要么满足Event条件,要么有一个等待视窗信息时能恢复,其他挂起的函数一直等到挂起的被满足或I/O操作已经完成时才能,无疑这里体现了灵活性。

 

3. 信号器(Semaphores

 

当你需限制访问特殊资源或限制一段代码到某些线程时,Semaphores非常有用。打一个比喻,就像是餐厅用的餐桌一样,假设这个餐厅有二十个餐桌,当你去时,二十个餐桌都有人在用餐,你就只好等二十个餐桌中有人吃完后才能去用餐,否则你必须等待。在Win32编程中获得Semaphores,就好像得到餐桌的一次控制。

为了利用Semaphores,一个线程调用 CreatSemaphore去获得一个HANDLE给Semaphores。该调用包括同时有多少线程使用资源或代码,如果其他线程在另一个进程中,可调用OpenSemaphore去获得一个可利用的HANDLE,当一个线程需要访问共享资源时,要把资源传递给WaitForSingleObject,如果这个Semaphore没有被等待的所有线程请求,等待功能将简单处理Semaphore的使用数,且线程继续执行。换句话说,如果Semaphore已经超出最大值,则调用等待功能的线程将被挂起。一个线程的含义就是使用一个Semaphore来执行,并用ReleaseSemaphore来释放资源。

4. 互斥(Mutexes

 

这是同步对象的第三种类型,Mutex(互斥)是“mutual exclusion”的缩略语。一个程序或一组程序希望一次只有一个线程去访问一个资源或一段代码时可使用一次互斥。如果一个线程正在使用这个资源,则另一个线程被排斥在同一资源之外。互斥的用法非常类似于信号器,产生、打开和释放信号器函数都有与互斥类似的内容。当一个线程有互斥要求时,可调用WaitForSingleObject/ WaitForMultipleObjects系列中的函数。

用餐桌来比喻的话,就是整个餐厅只有一个餐桌,当有一个人在用餐时,另一个人只能等待用餐。

 

5. 临界段(Critical Sections

 

临界段相当于一个微型的互斥,只能被同一进程中的线程使用。临界段是为了防止多线程同时执行同一段代码。相对其他同步机而言,临界段相对简单和易用,一个临界段可以被认为是仅在单一进程中有效的轻量级互斥。为了使用临界段,一个程序要么分配,要么声明一个CRITICAL_SECTION类型的全局变量。在临界段首次使用之前,其场地需要通过调用InitiazeCriticalSection进行初始化,之后调用EnterCriticalSection将一线程进入临界段了。

临界段使用起来很简单,在Windows 95中,当没有其他线程时,如果一个线程线程调用EnterCriticalSection,则只需在CRITICAL_SECTION结构中调整和设置一些场地即可。只有已经存在临界段的另一个线程把EnterCriticalSection调入VMIN 32 VxD时,才能使该线程挂起。

 

6. WaitForSingleObject/ WaitForMultipleObjects函数

 

至此,已经概述了线程同步的四种基本方法,我想谈论一下同步线程的其他方法。除了事情、信号器和互斥外,WaitForSingleObject/ WaitForMultipleObjects系列函数可接受几种其他的句柄,把一个进程HANDLE传到一个WaitForSingleObject/ WaitForMultipleObjects函数,则会引起调用线程挂起。如果这个进程已经中止,则Wait函数立即返回。同样,把一个线程的HANDLE传到WaitForSingleObject/ WaitFor Multiple Objects,调用线程也将被挂起。

WaitForSingleObject/ WaitForMultipleObjects函数可以挂起的另一个HANDLE是这个文件的变更,之间的变更可以限定一个给定的目录及有选择的子目录。WaitForSingleObject/ WaitForMultipleObjects函数的另外一个HANDLE是一个针对输入装置的HANDLE文件,一旦有未经使用的输入进入输入缓存,Wait函数则返回,并告诉线程继续执行。

转自:http://kb.cnblogs.com/a/170805/


原创粉丝点击