用户模式与内核模式下的线程同步
首先,先引用一下《深入理解计算机系统 2nd》第8章 “异常控制流”关于用户模式和内核模式的一段话,理清一下关于用户模式和内核模式的概念:
为了使操作系统内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。
处理器通常是用某一个控制寄存器中的一个模式位(mode bit)来提供这个功能的,该寄存器描述了进程当前享有的特权。当设置了模式位时,进程就运行在内核模式中(有时叫做超级用户模式)。一个运行在内核模式的进程可以执行指令集的任何指令,并且可以访问系统中任何存储器位置。
没有设置模式位时,进程就运行在用户模式中。用户模式中的进程不允许执行特权指令(privileged instruction),比如停止处理器、改变模式位、或者发起一个I/O操作。也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。任何这样的尝试都会导致致使的保护故障。反之,用户程序必须通过系统调用接口间接地访问内核代码和数据。
运行应用程序代码的进程初始时是在用户模式中的。进程从用户模式变为内核模式的唯一方式是通过诸如中断、故障或者陷入系统调用这样的异常。当异常发生时,控制传递到异常处理程序,处理器将模式从用户模式变为内核模式。处理程序运行内核模式中,当它返回到应用程序代码时,处理器就把模式从内核模式改回用户模式。
在用户模式下进行线程同步的最大好处就是速度非常快。但是在用户模式下的线程同步也存在一些问题。例如,对Interlocked系列函数只能对一个值进行操作,并且它们从来不会把线程切换到等待状态。虽然,使用关键段可以使线程进入等待状态,但是关键段只能用于同步一个进程内的线程。而相对用户模式来说,内核模式唯一的缺点就是它们的性能,调用一个系统调用,将调用线程从用户模式切换到内核模式是非常费时的。
内核对象的触发与未触发
对于线程同步来说,内核对象中的每一种要么处于触发(singaled)状态,要么处于未触发(singaled)状态。Windows提供了一组等待函数,用来使线程自愿进入等待状态(不消耗CPU时间),直到指定的内核对象被触发为止。
DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMilliseconds) ;
DWORD WaitForMultipleObjects(DWORD dwCount,CONST HANDLE * phObjects,BOOL bWaitAll,DWORD dwMilliseconds) ;
可等待的计时器内核对象
此前已经写过这个的读书笔记,就不再重复了:可等待的计时器与APC调用
线程同步对象速查表
对象 |
何时处于未触发状态 |
何时处于触发状态 |
成功等待的副作用 |
进程 |
进程仍在运行的时候 |
进程终止的时候(ExitProcess,TerminateProcess) |
没有 |
线程 |
线程仍在运行的时候 |
线程终止的时候(ExitThread,TerminateThread |
没有 |
作业 |
作业尚未超时的时候 |
作业超时的时候 |
没有 |
文件 |
有待处理的I/O请求的时候 |
I/O请求完成的时候 |
没有 |
控制台输入 |
没有输入的时候 |
有输入的时候 |
没有 |
文件变更通知 |
文件没有变更的时候 |
文件系统检测到变更的时候 |
重置通知 |
自动重置事件 |
ResetEvent,PulseEvent或等待成功的时候 |
SetEvent/PulseEvent被调用的时候 |
重置事件 |
手动重置事件 |
ResetEvent,PulseEvent |
SetEvent/PulseEvent被调用的时候 |
没有 |
自动重置可等待计时器 |
CancelWaitableTimer或等待成功的时候 |
时间到的时候(SetWaitableTimer) |
重置计时器 |
手动重置可等待计时器 |
CancelWaitableTimer |
时间到的时候(SetWaitableTimer) |
没有 |
信号量 |
等待成功的时候 |
计数大于0的时候(ReleaseSemaphore) |
计数减1 |
互斥量 |
等待成功的时候 |
不为线程占用的时候(ReleaseMutex) |
把所有权交给线程 |
关键段(用户模式) |
等待成功的时候((Try)EnterCritialSection) |
不为线程占用的时候(LeaveCritialSection) |
把所有权交给线程 |
SRWLock(用户模式) |
等待成功的时候(AcquireSRWLock(Exclusive)) |
不为线程占用的时候(ReleaseSRWLock(Exclusive)) |
把所有权交给线程 |
条件变量(用户模式) |
等待成功的时候(SleepConditionVariable*) |
被唤醒的时候(Wake(All)ConditionVariable) |
没有 |