第 5 章 进程与线程 --- 5.14 Windows线程间的相互作用
本节深入剖析 Windows/ReactOS 中线程间相互作用(同步机制)的完整实现。
概述
线程同步是现代操作系统中实现多线程协作的核心机制。由于多个线程可能同时访问共享资源,操作系统必须提供同步原语来协调线程的执行顺序,防止数据竞争和确保程序的正确性。
线程同步的本质是什么?
线程同步是一种协调机制,在保证正确性的前提下,控制多个线程对共享资源的访问顺序。同步原语的设计需要在性能、易用性和安全性之间取得平衡。
想象一个工厂场景:
- 互斥体(Mutex):车间门口的钥匙,只有拿到钥匙的工人才能进入,其他人必须等待;
- 信号量(Semaphore):停车场的车位计数器,显示剩余车位数量,车位满了就不能进入;
- 事件(Event):广播通知系统,通知所有等待的工人某个事件已经发生;
- 临界区(Critical Section):车间内部的快速通行证,比互斥体更轻量;
- 等待机制:工人在门口排队等待,直到条件满足才能进入。
本节内容概览
- 5.14.0 框架图:Windows线程同步完整架构图;
- 5.14.1 线程同步机制概述:同步原语分类、调度器对象结构;
- 5.14.2 互斥体(Mutex/Mutant):KMUTANT结构、内核实现、系统调用;
- 5.14.3 信号量(Semaphore):KSEMAPHORE结构、计数器机制;
- 5.14.4 事件(Event):KEVENT结构、通知事件与同步事件;
- 5.14.5 等待机制:KWAIT_BLOCK、KeWaitForSingleObject/MultipleObjects;
- 5.14.6 临界区(Critical Section):RTL_CRITICAL_SECTION、用户态实现;
- 5.14.7 其他同步机制:FAST_MUTEX、GUARDED_MUTEX、ERESOURCE;
- 5.14.8 APC与线程同步:APC队列、Alertable等待;
- 5.14.9 线程调度与等待:状态转换、KiSwapThread;
- 5.14.10 设计哲学问答:10个关键设计问题的深入解答。
学习目标
读完本节后,读者应当能够:
- 理解线程同步的核心原理和设计权衡;
- 掌握互斥体、信号量、事件的内核实现;
- 分析等待机制的调度器实现;
- 理解临界区的用户态优化策略;
- 解释APC与等待机制的关系;
- 根据实际需求选择合适的同步原语。
涉及的内核子系统
| 子系统 | 职责 |
|---|---|
| kernel32 | 用户态同步API(CreateMutex、CreateSemaphore、WaitForSingleObject等) |
| ntoskrnl/ke | 内核同步原语(KeInitializeMutex、KeWaitForSingleObject等) |
| ntoskrnl/ex | 执行体同步对象(NtCreateMutant、NtCreateSemaphore等) |
| ntoskrnl/ob | 对象等待(NtWaitForSingleObject、NtWaitForMultipleObjects) |
| ntdll | 临界区实现(RtlInitializeCriticalSection) |
5.14.0 框架图
┌──────────────────────────────────────────────────────────────────────────────────────┐
│ Windows 线程同步完整架构 │
├──────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户态 API 层 │
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
│ │ kernel32.dll │ │
│ │ ├─► 互斥体: CreateMutex, OpenMutex, ReleaseMutex │ │
│ │ ├─► 信号量: CreateSemaphore, OpenSemaphore, ReleaseSemaphore │ │
│ │ ├─► 事件: CreateEvent, OpenEvent, SetEvent, ResetEvent, PulseEvent │ │
│ │ ├─► 等待: WaitForSingleObject, WaitForMultipleObjects │ │
│ │ ├─► 临界区: InitializeCriticalSection, EnterCriticalSection │ │
│ │ └─► 原子操作: InterlockedIncrement, InterlockedExchange │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ NT Native API 层 │
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
│ │ ntdll.dll │ │
│ │ ├─► 互斥体: NtCreateMutant, NtOpenMutant, NtReleaseMutant │ │
│ │ ├─► 信号量: NtCreateSemaphore, NtOpenSemaphore, NtReleaseSemaphore │ │
│ │ ├─► 事件: NtCreateEvent, NtOpenEvent, NtSetEvent, NtResetEvent │ │
│ │ ├─► 等待: NtWaitForSingleObject, NtWaitForMultipleObjects │ │
│ │ ├─► 临界区: RtlInitializeCriticalSection, RtlEnterCriticalSection │ │
│ │ └─► 延迟: NtDelayExecution, NtSignalAndWaitForSingleObject │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 内核态实现层 │
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
│ │ ntoskrnl │ │
│ │ ├─► ex/mutant.c │ │
│ │ │ ├─► NtCreateMutant (79行) │ │
│ │ │ ├─► NtOpenMutant (162行) │ │
│ │ │ ├─► NtQueryMutant (224行) │ │
│ │ │ └─► NtReleaseMutant (296行) │ │
│ │ ├─► ex/sem.c │ │
│ │ │ ├─► NtCreateSemaphore (69行) │ │
│ │ │ ├─► NtOpenSemaphore (161行) │ │
│ │ │ ├─► NtQuerySemaphore (222行) │ │
│ │ │ └─► NtReleaseSemaphore (295行) │ │
│ │ ├─► ex/event.c │ │
│ │ │ ├─► NtCreateEvent (96行) │ │
│ │ │ ├─► NtSetEvent (463行) │ │
│ │ │ ├─► NtResetEvent (394行) │ │
│ │ │ └─► NtPulseEvent (252行) │ │
│ │ ├─► ke/mutex.c │ │
│ │ │ ├─► KeInitializeMutant (22行) │ │
│ │ │ ├─► KeInitializeMutex (67行) │ │
│ │ │ └─► KeReleaseMutant (98行) │ │
│ │ ├─► ke/semphobj.c │ │
│ │ │ ├─► KeInitializeSemaphore (22行) │ │
│ │ │ └─► KeReleaseSemaphore (54行) │ │
│ │ ├─► ke/eventobj.c │ │
│ │ │ ├─► KeInitializeEvent (35行) │ │
│ │ │ ├─► KeSetEvent (160行) │ │
│ │ │ ├─► KeResetEvent (134行) │ │
│ │ │ └─► KePulseEvent (69行) │ │
│ │ ├─► ke/wait.c │ │
│ │ │ ├─► KeWaitForSingleObject (416行) │ │
│ │ │ ├─► KeWaitForMultipleObjects (586行) │ │
│ │ │ ├─► KiWaitTest (21行) │ │
│ │ │ ├─► KiUnwaitThread (89行) │ │
│ │ │ └─► KeDelayExecutionThread (284行) │ │
│ │ └─► ob/obwait.c │ │
│ │ ├─► NtWaitForSingleObject (369行) │ │
│ │ ├─► NtWaitForMultipleObjects (46行) │ │
│ │ └─► NtSignalAndWaitForSingleObject (472行) │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 同步原语分类 │
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ 内核同步对象 │ │ 用户态同步 │ │ 内核内部同步 │ │ │
│ │ │ Mutex/Mutant │ │ Critical Section│ │ FAST_MUTEX │ │ │
│ │ │ Semaphore │ │ Slim Lock │ │ GUARDED_MUTEX │ │ │
│ │ │ Event │ │ InterlockedOps │ │ ERESOURCE │ │ │
│ │ │ Timer │ │ │ │ PUSH_LOCK │ │ │
│ │ │ 需系统调用 │ │ 用户态优化 │ │ 内核专用 │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ 等待机制 │ │ 线程状态 │ │ │
│ │ │ WaitAny/WaitAll │ │ Ready/Running │ │ │
│ │ │ Alertable等待 │ │ Waiting/Termed │ │ │
│ │ │ 超时机制 │ │ 状态转换 │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────┘
5.14.1 线程同步机制概述
5.14.1.1 同步原语分类
Windows 提供的同步原语可以按以下维度分类:
按实现位置分类:
| 类型 | 机制 | 特点 |
|---|---|---|
| 内核同步对象 | Mutex、Semaphore、Event、Timer | 需要系统调用,跨进程可用 |
| 用户态同步 | Critical Section、Slim Lock、Interlocked | 用户态优化,仅限进程内 |
| 内核内部同步 | FAST_MUTEX、GUARDED_MUTEX、ERESOURCE | 内核专用,性能优化 |
按功能分类:
| 类型 | 机制 | 特点 |
|---|---|---|
| 互斥 | Mutex、Critical Section | 一次只允许一个线程访问 |
| 计数 | Semaphore | 控制并发访问数量 |
| 通知 | Event | 通知等待线程事件发生 |
| 原子操作 | Interlocked | 无锁的原子更新 |
5.14.1.2 调度器对象结构
所有内核同步对象都基于 DISPATCHER_HEADER 结构:
c
typedef struct _DISPATCHER_HEADER {
UCHAR Type; // 对象类型 (EventObject, SemaphoreObject, MutantObject等)
UCHAR Absolute; // 绝对时间标志
UCHAR Size; // 对象大小(以ULONG为单位)
UCHAR Inserted; // 是否已插入定时器树
LONG SignalState; // 信号状态
LIST_ENTRY WaitListHead; // 等待线程列表
} DISPATCHER_HEADER, *PDISPATCHER_HEADER;
对象类型定义:
| 类型值 | 对象类型 | 说明 |
|---|---|---|
EventNotificationObject |
通知事件 | 唤醒所有等待线程,保持信号状态 |
EventSynchronizationObject |
同步事件 | 唤醒一个等待线程,自动复位 |
SemaphoreObject |
信号量 | 计数器控制并发 |
MutantObject |
互斥体 | 递归锁,有所有权 |
TimerNotificationObject |
通知定时器 | 到时唤醒所有等待线程 |
TimerSynchronizationObject |
同步定时器 | 到时唤醒一个等待线程 |
ThreadObject |
线程 | 线程终止时信号 |
ProcessObject |
进程 | 进程终止时信号 |
5.14.1.3 同步需求场景分析
同步需求场景
┌─────────────────────────────────────────────────────────────────┐
│ 场景类型 │ 适用同步原语 │
├───────────────────────┼───────────────────────────────────────┤
│ 互斥访问共享资源 │ Mutex, Critical Section │
│ 控制并发线程数 │ Semaphore │
│ 等待条件满足 │ Event │
│ 生产者-消费者模式 │ Semaphore + Event │
│ 读写锁场景 │ ERESOURCE, Slim Reader/Writer Lock │
│ 原子计数更新 │ InterlockedIncrement/Decrement │
│ 等待多个条件 │ WaitForMultipleObjects │
│ 跨进程同步 │ 命名 Mutex/Semaphore/Event │
│ 高性能内核同步 │ FAST_MUTEX, PUSH_LOCK │
└───────────────────────┴───────────────────────────────────────┘
5.14.2 互斥体(Mutex/Mutant)
5.14.2.1 KMUTANT/KMUTEX结构定义
c
typedef struct _KMUTANT {
DISPATCHER_HEADER Header; // 调度器头部
LIST_ENTRY MutantListEntry; // 线程的互斥体列表
PKTHREAD OwnerThread; // 当前所有者线程
BOOLEAN Abandoned; // 是否被遗弃
UCHAR ApcDisable; // APC禁用计数
} KMUTANT, *PKMUTANT;
typedef struct _KMUTEX {
DISPATCHER_HEADER Header;
LIST_ENTRY MutantListEntry;
PKTHREAD OwnerThread;
BOOLEAN Abandoned;
UCHAR ApcDisable; // Mutex固定为1
} KMUTEX, *PKMUTEX;
关键区别:
| 特性 | KMUTANT | KMUTEX |
|---|---|---|
| ApcDisable | 可配置 | 固定为1 |
| 用途 | 用户态互斥体 | 内核态互斥体 |
| 递归 | 支持 | 支持 |
| 所有权 | 有 | 有 |
5.14.2.2 KeInitializeMutant/KeInitializeMutex实现
c
VOID
NTAPI
KeInitializeMutant(IN PKMUTANT Mutant,
IN BOOLEAN InitialOwner)
{
PKTHREAD CurrentThread;
KIRQL OldIrql;
/* Check if we have an initial owner */
if (InitialOwner)
{
/* We also need to associate a thread */
CurrentThread = KeGetCurrentThread();
Mutant->OwnerThread = CurrentThread;
/* We're about to touch the Thread, so lock the Dispatcher */
OldIrql = KiAcquireDispatcherLock();
/* And insert it into its list */
InsertTailList(&CurrentThread->MutantListHead,
&Mutant->MutantListEntry);
/* Release Dispatcher Lock */
KiReleaseDispatcherLock(OldIrql);
}
else
{
/* In this case, we don't have an owner yet */
Mutant->OwnerThread = NULL;
}
/* Now we set up the Dispatcher Header */
Mutant->Header.Type = MutantObject;
Mutant->Header.Size = sizeof(KMUTANT) / sizeof(ULONG);
Mutant->Header.SignalState = InitialOwner ? 0 : 1;
InitializeListHead(&(Mutant->Header.WaitListHead));
/* Initialize the default data */
Mutant->Abandoned = FALSE;
Mutant->ApcDisable = 0;
}
源码位置:ntoskrnl/ke/mutex.c#L22(file:///d:/reactos/ntoskrnl/ke/mutex.c#L22)
信号状态含义:
| SignalState | 含义 |
|---|---|
> 0 |
互斥体未被占用,可获取 |
0 |
互斥体被当前线程占用 |
< 0 |
互斥体被占用,有等待线程 |
5.14.2.3 KeReleaseMutant实现分析
c
LONG
NTAPI
KeReleaseMutant(IN PKMUTANT Mutant,
IN KPRIORITY Increment,
IN BOOLEAN Abandon,
IN BOOLEAN Wait)
{
KIRQL OldIrql;
LONG PreviousState;
PKTHREAD CurrentThread = KeGetCurrentThread();
BOOLEAN EnableApc = FALSE;
ASSERT_MUTANT(Mutant);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Lock the Dispatcher Database */
OldIrql = KiAcquireDispatcherLock();
/* Save the Previous State */
PreviousState = Mutant->Header.SignalState;
/* Check if it is to be abandonned */
if (Abandon == FALSE)
{
/* Make sure that the Owner Thread is the current Thread */
if (Mutant->OwnerThread != CurrentThread)
{
/* Release the lock */
KiReleaseDispatcherLock(OldIrql);
/* Raise an exception */
ExRaiseStatus(Mutant->Abandoned ? STATUS_ABANDONED :
STATUS_MUTANT_NOT_OWNED);
}
/* If the thread owns it, then increase the signal state */
Mutant->Header.SignalState++;
}
else
{
/* It's going to be abandonned */
Mutant->Header.SignalState = 1;
Mutant->Abandoned = TRUE;
}
/* Check if the signal state is only single */
if (Mutant->Header.SignalState == 1)
{
/* Check if it's below 0 now */
if (PreviousState <= 0)
{
/* Remove the mutant from the list */
RemoveEntryList(&Mutant->MutantListEntry);
/* Save if we need to re-enable APCs */
EnableApc = Mutant->ApcDisable;
}
/* Remove the Owning Thread and wake it */
Mutant->OwnerThread = NULL;
/* Check if the Wait List isn't empty */
if (!IsListEmpty(&Mutant->Header.WaitListHead))
{
/* Wake the Mutant */
KiWaitTest(&Mutant->Header, Increment);
}
}
/* Check if the caller wants to wait after this release */
if (Wait == FALSE)
{
/* Release the Lock */
KiReleaseDispatcherLock(OldIrql);
}
else
{
/* Set a wait */
CurrentThread->WaitNext = TRUE;
CurrentThread->WaitIrql = OldIrql;
}
/* Check if we need to re-enable APCs */
if (EnableApc) KeLeaveCriticalRegion();
/* Return the previous state */
return PreviousState;
}
源码位置:ntoskrnl/ke/mutex.c#L98(file:///d:/reactos/ntoskrnl/ke/mutex.c#L98)
5.14.2.4 NtCreateMutant/NtReleaseMutant系统调用
c
NTSTATUS
NTAPI
NtCreateMutant(OUT PHANDLE MutantHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN BOOLEAN InitialOwner)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
HANDLE hMutant;
PKMUTANT Mutant;
NTSTATUS Status;
PAGED_CODE();
/* Check if we were called from user-mode */
if (PreviousMode != KernelMode)
{
/* Enter SEH Block */
_SEH2_TRY
{
/* Check handle pointer */
ProbeForWriteHandle(MutantHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Create the Mutant Object */
Status = ObCreateObject(PreviousMode,
ExMutantObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(KMUTANT),
0,
0,
(PVOID*)&Mutant);
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Initialize the Kernel Mutant */
KeInitializeMutant(Mutant, InitialOwner);
/* Insert the Object */
Status = ObInsertObject((PVOID)Mutant,
NULL,
DesiredAccess,
0,
NULL,
&hMutant);
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Enter SEH for return */
_SEH2_TRY
{
/* Return the handle to the caller */
*MutantHandle = hMutant;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
}
/* Return Status */
return Status;
}
源码位置:ntoskrnl/ex/mutant.c#L79(file:///d:/reactos/ntoskrnl/ex/mutant.c#L79)
5.14.2.5 递归锁特性与所有权机制
互斥体支持递归获取,同一个线程可以多次获取同一个互斥体:
递归锁示例
线程A:
获取 Mutex → SignalState = 0, OwnerThread = A
获取 Mutex → SignalState = -1 (递归计数)
获取 Mutex → SignalState = -2
释放 Mutex → SignalState = -1
释放 Mutex → SignalState = 0
释放 Mutex → SignalState = 1, OwnerThread = NULL
线程B:
等待 Mutex → 进入等待队列
当 SignalState = 1 时被唤醒
所有权机制:
- 每个互斥体记录当前所有者线程 (
OwnerThread) - 只有所有者线程才能释放互斥体
- 线程终止时,其拥有的互斥体被遗弃 (
Abandoned = TRUE) - 遗弃的互斥体被释放,等待线程收到
STATUS_ABANDONED
5.14.2.6 完整示例代码
c
#include <windows.h>
#include <stdio.h>
// 使用命名互斥体进行跨进程同步
void CrossProcessMutexExample()
{
HANDLE hMutex;
DWORD waitResult;
// 创建或打开命名互斥体
hMutex = CreateMutex(NULL, FALSE, L"MySharedMutex");
if (hMutex == NULL)
{
printf("CreateMutex failed: %d\n", GetLastError());
return;
}
// 等待互斥体
waitResult = WaitForSingleObject(hMutex, 5000);
switch (waitResult)
{
case WAIT_OBJECT_0:
printf("Successfully acquired mutex\n");
// 执行需要同步的操作
Sleep(1000);
// 释放互斥体
ReleaseMutex(hMutex);
printf("Mutex released\n");
break;
case WAIT_TIMEOUT:
printf("Timeout waiting for mutex\n");
break;
case WAIT_ABANDONED:
printf("Mutex was abandoned by previous owner\n");
// 仍然获得了互斥体,但需要检查资源状态
ReleaseMutex(hMutex);
break;
default:
printf("Wait failed: %d\n", GetLastError());
}
CloseHandle(hMutex);
}
// 递归锁示例
void RecursiveMutexExample()
{
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
// 第一次获取
WaitForSingleObject(hMutex, INFINITE);
printf("First acquire\n");
// 第二次获取(递归)
WaitForSingleObject(hMutex, INFINITE);
printf("Second acquire (recursive)\n");
// 第一次释放
ReleaseMutex(hMutex);
printf("First release\n");
// 第二次释放
ReleaseMutex(hMutex);
printf("Second release\n");
CloseHandle(hMutex);
}
5.14.3 信号量(Semaphore)
5.14.3.1 KSEMAPHORE结构定义
c
typedef struct _KSEMAPHORE {
DISPATCHER_HEADER Header; // 调度器头部
LONG Limit; // 最大计数
} KSEMAPHORE, *PKSEMAPHORE;
信号量计数含义:
| SignalState | 含义 |
|---|---|
> 0 |
可用资源数量,等待线程可立即获取 |
= 0 |
无可用资源,等待线程阻塞 |
< 0 |
无可用资源,负数表示等待线程数 |
5.14.3.2 KeInitializeSemaphore实现
c
VOID
NTAPI
KeInitializeSemaphore(IN PKSEMAPHORE Semaphore,
IN LONG Count,
IN LONG Limit)
{
/* Simply Initialize the Header */
Semaphore->Header.Type = SemaphoreObject;
Semaphore->Header.Size = sizeof(KSEMAPHORE) / sizeof(ULONG);
Semaphore->Header.SignalState = Count;
InitializeListHead(&(Semaphore->Header.WaitListHead));
/* Set the Limit */
Semaphore->Limit = Limit;
}
源码位置:ntoskrnl/ke/semphobj.c#L22(file:///d:/reactos/ntoskrnl/ke/semphobj.c#L22)
5.14.3.3 KeReleaseSemaphore实现分析
c
LONG
NTAPI
KeReleaseSemaphore(IN PKSEMAPHORE Semaphore,
IN KPRIORITY Increment,
IN LONG Adjustment,
IN BOOLEAN Wait)
{
LONG InitialState, State;
KIRQL OldIrql;
PKTHREAD CurrentThread;
ASSERT_SEMAPHORE(Semaphore);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Lock the Dispatcher Database */
OldIrql = KiAcquireDispatcherLock();
/* Save the Old State and get new one */
InitialState = Semaphore->Header.SignalState;
State = InitialState + Adjustment;
/* Check if the Limit was exceeded */
if ((Semaphore->Limit < State) || (InitialState > State))
{
/* Raise an error if it was exceeded */
KiReleaseDispatcherLock(OldIrql);
ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED);
}
/* Now set the new state */
Semaphore->Header.SignalState = State;
/* Check if we should wake it */
if (!(InitialState) && !(IsListEmpty(&Semaphore->Header.WaitListHead)))
{
/* Wake the Semaphore */
KiWaitTest(&Semaphore->Header, Increment);
}
/* Check if the caller wants to wait after this release */
if (Wait == FALSE)
{
/* Release the Lock */
KiReleaseDispatcherLock(OldIrql);
}
else
{
/* Set a wait */
CurrentThread = KeGetCurrentThread();
CurrentThread->WaitNext = TRUE;
CurrentThread->WaitIrql = OldIrql;
}
/* Return the previous state */
return InitialState;
}
源码位置:ntoskrnl/ke/semphobj.c#L54(file:///d:/reactos/ntoskrnl/ke/semphobj.c#L54)
5.14.3.4 NtCreateSemaphore/NtReleaseSemaphore系统调用
c
NTSTATUS
NTAPI
NtCreateSemaphore(OUT PHANDLE SemaphoreHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN LONG InitialCount,
IN LONG MaximumCount)
{
PKSEMAPHORE Semaphore;
HANDLE hSemaphore;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status;
PAGED_CODE();
/* Check if we were called from user-mode */
if (PreviousMode != KernelMode)
{
/* Enter SEH Block */
_SEH2_TRY
{
/* Check handle pointer */
ProbeForWriteHandle(SemaphoreHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Make sure the counts make sense */
if ((MaximumCount <= 0) ||
(InitialCount < 0) ||
(InitialCount > MaximumCount))
{
return STATUS_INVALID_PARAMETER;
}
/* Create the Semaphore Object */
Status = ObCreateObject(PreviousMode,
ExSemaphoreObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(KSEMAPHORE),
0,
0,
(PVOID*)&Semaphore);
/* Check for Success */
if (NT_SUCCESS(Status))
{
/* Initialize it */
KeInitializeSemaphore(Semaphore, InitialCount, MaximumCount);
/* Insert it into the Object Tree */
Status = ObInsertObject((PVOID)Semaphore,
NULL,
DesiredAccess,
0,
NULL,
&hSemaphore);
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Enter SEH Block for return */
_SEH2_TRY
{
/* Return the handle */
*SemaphoreHandle = hSemaphore;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
}
/* Return Status */
return Status;
}
源码位置:ntoskrnl/ex/sem.c#L69(file:///d:/reactos/ntoskrnl/ex/sem.c#L69)
5.14.3.5 计数器机制详解
信号量的计数器机制:
信号量计数器变化示例
初始状态: Count = 3, Limit = 5
线程A: WaitForSingleObject → Count = 2 (获取成功)
线程B: WaitForSingleObject → Count = 1 (获取成功)
线程C: WaitForSingleObject → Count = 0 (获取成功)
线程D: WaitForSingleObject → 进入等待队列 (Count = 0)
线程E: WaitForSingleObject → 进入等待队列 (Count = 0)
线程A: ReleaseSemaphore(1) → Count = 1, 线程D被唤醒
线程D: 被唤醒 → Count = 0
线程B: ReleaseSemaphore(2) → Count = 2, 线程E被唤醒
线程E: 被唤醒 → Count = 1
尝试 ReleaseSemaphore(10) → STATUS_SEMAPHORE_LIMIT_EXCEEDED
5.14.3.6 完整示例代码
c
#include <windows.h>
#include <stdio.h>
#define MAX_THREADS 3
HANDLE hSemaphore;
HANDLE hThreads[MAX_THREADS];
// 工作线程函数
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
DWORD threadId = GetCurrentThreadId();
// 等待信号量(获取资源)
DWORD waitResult = WaitForSingleObject(hSemaphore, INFINITE);
if (waitResult == WAIT_OBJECT_0)
{
printf("Thread %d: Acquired semaphore\n", threadId);
// 模拟工作
Sleep(2000);
printf("Thread %d: Releasing semaphore\n", threadId);
// 释放信号量
ReleaseSemaphore(hSemaphore, 1, NULL);
}
return 0;
}
void SemaphoreExample()
{
// 创建信号量:初始计数1,最大计数3
hSemaphore = CreateSemaphore(NULL, 1, MAX_THREADS, NULL);
if (hSemaphore == NULL)
{
printf("CreateSemaphore failed: %d\n", GetLastError());
return;
}
// 创建工作线程
for (int i = 0; i < MAX_THREADS; i++)
{
hThreads[i] = CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
}
// 等待所有线程完成
WaitForMultipleObjects(MAX_THREADS, hThreads, TRUE, INFINITE);
// 清理
for (int i = 0; i < MAX_THREADS; i++)
{
CloseHandle(hThreads[i]);
}
CloseHandle(hSemaphore);
}
// 生产者-消费者模式
void ProducerConsumerExample()
{
HANDLE hFull = CreateSemaphore(NULL, 0, 10, NULL); // 已填充槽位
HANDLE hEmpty = CreateSemaphore(NULL, 10, 10, NULL); // 空闲槽位
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
// 生产者
void ProduceItem()
{
WaitForSingleObject(hEmpty, INFINITE); // 等待空闲槽位
WaitForSingleObject(hMutex, INFINITE); // 获取互斥体
// 生产物品并放入缓冲区
// ...
ReleaseMutex(hMutex);
ReleaseSemaphore(hFull, 1, NULL); // 增加已填充槽位
}
// 消费者
void ConsumeItem()
{
WaitForSingleObject(hFull, INFINITE); // 等待已填充槽位
WaitForSingleObject(hMutex, INFINITE); // 获取互斥体
// 从缓冲区取出物品并消费
// ...
ReleaseMutex(hMutex);
ReleaseSemaphore(hEmpty, 1, NULL); // 增加空闲槽位
}
CloseHandle(hFull);
CloseHandle(hEmpty);
CloseHandle(hMutex);
}
5.14.4 事件(Event)
5.14.4.1 KEVENT结构定义
c
typedef struct _KEVENT {
DISPATCHER_HEADER Header; // 调度器头部
} KEVENT, *PKEVENT;
事件是最简单的同步对象,仅包含调度器头部。
5.14.4.2 通知事件 vs 同步事件
| 特性 | 通知事件 (NotificationEvent) | 同步事件 (SynchronizationEvent) |
|---|---|---|
| 唤醒行为 | 唤醒所有等待线程 | 只唤醒一个等待线程 |
| 信号保持 | 保持信号状态直到手动复位 | 自动复位(变为无信号) |
| 适用场景 | 广播通知 | 互斥访问 |
| 类型值 | EventNotificationObject |
EventSynchronizationObject |
通知事件 vs 同步事件行为对比
通知事件:
SetEvent → SignalState = 1
线程A等待 → 被唤醒,SignalState仍为1
线程B等待 → 被唤醒,SignalState仍为1
线程C等待 → 被唤醒,SignalState仍为1
ResetEvent → SignalState = 0
同步事件:
SetEvent → SignalState = 1
线程A等待 → 被唤醒,SignalState自动变为0
线程B等待 → 继续等待(SignalState = 0)
线程C等待 → 继续等待
5.14.4.3 KeInitializeEvent实现
c
VOID
NTAPI
KeInitializeEvent(OUT PKEVENT Event,
IN EVENT_TYPE Type,
IN BOOLEAN State)
{
/* Initialize the Dispatcher Header */
ASSERT((Type == NotificationEvent) || (Type == SynchronizationEvent));
Event->Header.Type = EventNotificationObject + Type;
Event->Header.Size = sizeof(KEVENT) / sizeof(ULONG);
Event->Header.SignalState = State;
InitializeListHead(&(Event->Header.WaitListHead));
}
源码位置:ntoskrnl/ke/eventobj.c#L35(file:///d:/reactos/ntoskrnl/ke/eventobj.c#L35)
5.14.4.4 KeSetEvent/KeResetEvent/KePulseEvent实现
KeSetEvent实现:
c
LONG
NTAPI
KeSetEvent(IN PKEVENT Event,
IN KPRIORITY Increment,
IN BOOLEAN Wait)
{
KIRQL OldIrql;
LONG PreviousState;
PKTHREAD Thread;
ASSERT_EVENT(Event);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/*
* Check if this is an signaled notification event without an upcoming wait.
* In this case, we can immediately return TRUE, without locking.
*/
if ((Event->Header.Type == EventNotificationObject) &&
(Event->Header.SignalState == 1) &&
!(Wait))
{
/* Return the signal state (TRUE/Signalled) */
return TRUE;
}
/* Lock the Dispatcher Database */
OldIrql = KiAcquireDispatcherLock();
/* Save the Previous State */
PreviousState = Event->Header.SignalState;
/* Set the Event to Signaled */
Event->Header.SignalState = 1;
/* Check if the event just became signaled now, and it has waiters */
if (!(PreviousState) && !(IsListEmpty(&Event->Header.WaitListHead)))
{
/* Check the type of event */
if (Event->Header.Type == EventNotificationObject)
{
/* Unwait the thread */
KxUnwaitThread(&Event->Header, Increment);
}
else
{
/* Otherwise unwait the thread and unsignal the event */
KxUnwaitThreadForEvent(Event, Increment);
}
}
/* Check what wait state was requested */
if (!Wait)
{
/* Wait not requested, release Dispatcher Database and return */
KiReleaseDispatcherLock(OldIrql);
}
else
{
/* Return Locked and with a Wait */
Thread = KeGetCurrentThread();
Thread->WaitNext = TRUE;
Thread->WaitIrql = OldIrql;
}
/* Return the previous State */
return PreviousState;
}
源码位置:ntoskrnl/ke/eventobj.c#L160(file:///d:/reactos/ntoskrnl/ke/eventobj.c#L160)
KePulseEvent实现:
c
LONG
NTAPI
KePulseEvent(IN PKEVENT Event,
IN KPRIORITY Increment,
IN BOOLEAN Wait)
{
KIRQL OldIrql;
LONG PreviousState;
PKTHREAD Thread;
ASSERT_EVENT(Event);
/* Lock the Dispatcher Database */
OldIrql = KiAcquireDispatcherLock();
/* Save the Old State */
PreviousState = Event->Header.SignalState;
/* Check if we are non-signaled and we have stuff in the Wait Queue */
if (!PreviousState && !IsListEmpty(&Event->Header.WaitListHead))
{
/* Set the Event to Signaled */
Event->Header.SignalState = 1;
/* Wake the Event */
KiWaitTest(&Event->Header, Increment);
}
/* Unsignal it */
Event->Header.SignalState = 0;
/* Check what wait state was requested */
if (Wait == FALSE)
{
/* Wait not requested, release Dispatcher Database and return */
KiReleaseDispatcherLock(OldIrql);
}
else
{
/* Return Locked and with a Wait */
Thread = KeGetCurrentThread();
Thread->WaitNext = TRUE;
Thread->WaitIrql = OldIrql;
}
/* Return the previous State */
return PreviousState;
}
源码位置:ntoskrnl/ke/eventobj.c#L69(file:///d:/reactos/ntoskrnl/ke/eventobj.c#L69)
5.14.4.5 NtCreateEvent/NtSetEvent系统调用
c
NTSTATUS
NTAPI
NtCreateEvent(OUT PHANDLE EventHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN EVENT_TYPE EventType,
IN BOOLEAN InitialState)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PKEVENT Event;
HANDLE hEvent;
NTSTATUS Status;
PAGED_CODE();
/* Validate the event type */
if ((EventType != NotificationEvent) &&
(EventType != SynchronizationEvent))
{
return STATUS_INVALID_PARAMETER;
}
/* Check if we were called from user-mode */
if (PreviousMode != KernelMode)
{
/* Enter SEH Block */
_SEH2_TRY
{
/* Check handle pointer */
ProbeForWriteHandle(EventHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Create the Object */
Status = ObCreateObject(PreviousMode,
ExEventObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(KEVENT),
0,
0,
(PVOID*)&Event);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Initialize the Event */
KeInitializeEvent(Event, EventType, InitialState);
/* Insert it */
Status = ObInsertObject((PVOID)Event,
NULL,
DesiredAccess,
0,
NULL,
&hEvent);
/* Enter SEH for return */
_SEH2_TRY
{
/* Return the handle to the caller */
*EventHandle = hEvent;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Return Status */
return Status;
}
源码位置:ntoskrnl/ex/event.c#L96(file:///d:/reactos/ntoskrnl/ex/event.c#L96)
5.14.4.6 完整示例代码
c
#include <windows.h>
#include <stdio.h>
// 使用通知事件进行广播
void NotificationEventExample()
{
HANDLE hEvent;
HANDLE hThreads[3];
// 创建通知事件(手动复位)
hEvent = CreateEvent(NULL, TRUE, FALSE, L"MyBroadcastEvent");
// 创建等待线程
for (int i = 0; i < 3; i++)
{
hThreads[i] = CreateThread(NULL, 0,
[](LPVOID) -> DWORD {
printf("Thread waiting for event...\n");
WaitForSingleObject(hEvent, INFINITE);
printf("Thread received event notification!\n");
return 0;
}, NULL, 0, NULL);
}
Sleep(1000); // 让线程进入等待
// 发送广播通知
printf("Broadcasting event...\n");
SetEvent(hEvent);
// 所有线程都被唤醒
WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);
// 手动复位事件
ResetEvent(hEvent);
for (int i = 0; i < 3; i++)
CloseHandle(hThreads[i]);
CloseHandle(hEvent);
}
// 使用同步事件进行单次通知
void SynchronizationEventExample()
{
HANDLE hEvent;
// 创建同步事件(自动复位)
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 线程1等待
HANDLE hThread1 = CreateThread(NULL, 0,
[](LPVOID) -> DWORD {
WaitForSingleObject(hEvent, INFINITE);
printf("Thread 1 woke up\n");
return 0;
}, NULL, 0, NULL);
// 线程2等待
HANDLE hThread2 = CreateThread(NULL, 0,
[](LPVOID) -> DWORD {
WaitForSingleObject(hEvent, INFINITE);
printf("Thread 2 woke up\n");
return 0;
}, NULL, 0, NULL);
Sleep(100);
// 设置事件 - 只唤醒一个线程
SetEvent(hEvent);
Sleep(100);
// 再次设置 - 唤醒另一个线程
SetEvent(hEvent);
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hEvent);
}
// PulseEvent示例
void PulseEventExample()
{
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// 等待线程
HANDLE hThread = CreateThread(NULL, 0,
[](LPVOID) -> DWORD {
DWORD result = WaitForSingleObject(hEvent, 1000);
if (result == WAIT_OBJECT_0)
printf("Thread woke up during pulse\n");
else if (result == WAIT_TIMEOUT)
printf("Thread missed the pulse\n");
return 0;
}, NULL, 0, NULL);
Sleep(50);
// PulseEvent:短暂设置为有信号,然后立即复位
// 只有正在等待的线程会被唤醒
PulseEvent(hEvent);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hEvent);
}
5.14.5 等待机制
等待机制是线程同步的核心,它让线程能够阻塞自己,直到某个条件满足(对象变为有信号状态或超时)。
5.14.5.1 KWAIT_BLOCK结构定义
c
typedef struct _KWAIT_BLOCK {
LIST_ENTRY WaitListEntry; // 链入对象的等待列表
struct _KTHREAD *Thread; // 所属线程
PVOID Object; // 等待的对象
struct _KWAIT_BLOCK *NextWaitBlock; // 下一个等待块(链表)
USHORT WaitKey; // 等待索引(用于返回值)
UCHAR WaitType; // WaitAny 或 WaitAll
UCHAR SpareByte; // 保留
#if defined(_WIN64)
LONG SpareLong;
#endif
} KWAIT_BLOCK, *PKWAIT_BLOCK;
等待块的作用:
每个等待操作都会创建一个或多个等待块,将线程与等待对象关联起来:
等待块与对象的关系
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 线程A (WaitForMultipleObjects) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ WaitBlock[0] ──────────────► Mutex1.WaitListHead │ │
│ │ WaitBlock[1] ──────────────► Semaphore.WaitListHead │ │
│ │ WaitBlock[2] ──────────────► Event.WaitListHead │ │
│ │ WaitBlock[3] (Timer) ─────► Thread.Timer │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 对象的等待列表 │
│ Mutex1.WaitListHead: │
│ ├─► ThreadA.WaitBlock[0] │
│ ├─► ThreadB.WaitBlock[0] │
│ └─► ThreadC.WaitBlock[2] │
│ │
└─────────────────────────────────────────────────────────────────┘
5.14.5.2 KeWaitForSingleObject实现分析
c
NTSTATUS
NTAPI
KeWaitForSingleObject(IN PVOID Object,
IN KWAIT_REASON WaitReason,
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL)
{
PKTHREAD Thread = KeGetCurrentThread();
PKMUTANT CurrentObject = (PKMUTANT)Object;
PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0];
PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
PKTIMER Timer = &Thread->Timer;
NTSTATUS WaitStatus;
BOOLEAN Swappable;
LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
PLARGE_INTEGER OriginalDueTime = Timeout;
ULONG Hand = 0;
// IRQL检查
if (Thread->WaitNext)
ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
else
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL ||
(KeGetCurrentIrql() == DISPATCH_LEVEL &&
Timeout && Timeout->QuadPart == 0));
// 检查是否已持有锁
if (!Thread->WaitNext) goto WaitStart;
// 已持有锁,初始化等待
Thread->WaitNext = FALSE;
KxSingleThreadWait();
// 主等待循环
for (;;)
{
// 禁用抢占
Thread->Preempted = FALSE;
// 检查是否有内核APC待处理
if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
(Thread->WaitIrql < APC_LEVEL))
{
// 释放调度器锁
KiReleaseDispatcherLock(Thread->WaitIrql);
}
else
{
// 检查对象类型
ASSERT(CurrentObject->Header.Type != QueueObject);
// 如果是互斥体,特殊处理
if (CurrentObject->Header.Type == MutantObject)
{
// 检查信号状态或所有权
if ((CurrentObject->Header.SignalState > 0) ||
(Thread == CurrentObject->OwnerThread))
{
if (CurrentObject->Header.SignalState != MINLONG)
{
// 满足等待,直接返回
KiSatisfyMutantWait(CurrentObject, Thread);
WaitStatus = (NTSTATUS)Thread->WaitStatus;
goto DontWait;
}
else
{
// 超出限制,抛出异常
KiReleaseDispatcherLock(Thread->WaitIrql);
ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
}
}
}
else if (CurrentObject->Header.SignalState > 0)
{
// 其他对象有信号,满足等待
KiSatisfyNonMutantWait(CurrentObject);
WaitStatus = STATUS_WAIT_0;
goto DontWait;
}
// 检查Alertable状态
WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
if (WaitStatus != STATUS_WAIT_0) break;
// 设置超时定时器
if (Timeout)
{
InterruptTime.QuadPart = KeQueryInterruptTime();
if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
{
// 已超时
WaitStatus = STATUS_TIMEOUT;
goto DontWait;
}
Timer->Header.Inserted = TRUE;
}
// 将等待块链入对象的等待列表
InsertTailList(&CurrentObject->Header.WaitListHead,
&WaitBlock->WaitListEntry);
// 处理内核队列
if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
// 设置线程状态为等待
Thread->State = Waiting;
// 将线程加入等待列表
KiAddThreadToWaitList(Thread, Swappable);
// 设置交换忙状态
KiSetThreadSwapBusy(Thread);
// 插入定时器或释放调度器锁
if (Timeout)
KxInsertTimer(Timer, Hand);
else
KiReleaseDispatcherLockFromSynchLevel();
// 执行线程交换
WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
// 检查是否执行了APC
if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
// 重新计算超时时间
if (Timeout)
Timeout = KiRecalculateDueTime(OriginalDueTime, &DueTime, &NewDueTime);
}
WaitStart:
// 设置新的等待
Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
KxSingleThreadWait();
KiAcquireDispatcherLockAtSynchLevel();
}
// 等待完成
KiReleaseDispatcherLock(Thread->WaitIrql);
return WaitStatus;
DontWait:
// 不需要等待,直接返回
KiReleaseDispatcherLockFromSynchLevel();
KiAdjustQuantumThread(Thread);
return WaitStatus;
}
源码位置:ntoskrnl/ke/wait.c#L416(file:///d:/reactos/ntoskrnl/ke/wait.c#L416)
5.14.5.3 KeWaitForMultipleObjects实现分析
c
NTSTATUS
NTAPI
KeWaitForMultipleObjects(IN ULONG Count,
IN PVOID Object[],
IN WAIT_TYPE WaitType,
IN KWAIT_REASON WaitReason,
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL,
OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
{
PKMUTANT CurrentObject;
PKWAIT_BLOCK WaitBlock;
PKTHREAD Thread = KeGetCurrentThread();
PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
PKTIMER Timer = &Thread->Timer;
NTSTATUS WaitStatus = STATUS_SUCCESS;
BOOLEAN Swappable;
PLARGE_INTEGER OriginalDueTime = Timeout;
LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
ULONG Index, Hand = 0;
// 检查等待块数量
if (!WaitBlockArray)
{
if (Count > THREAD_WAIT_OBJECTS)
KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
WaitBlockArray = &Thread->WaitBlock[0];
}
else
{
if (Count > MAXIMUM_WAIT_OBJECTS)
KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
}
ASSERT(Count != 0);
if (!Thread->WaitNext) goto WaitStart;
Thread->WaitNext = FALSE;
KxMultiThreadWait();
// 主等待循环
for (;;)
{
Thread->Preempted = FALSE;
if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
(Thread->WaitIrql < APC_LEVEL))
{
KiReleaseDispatcherLock(Thread->WaitIrql);
}
else
{
Index = 0;
if (WaitType == WaitAny)
{
// WaitAny: 任一对象有信号即可
do
{
CurrentObject = (PKMUTANT)Object[Index];
ASSERT(CurrentObject->Header.Type != QueueObject);
if (CurrentObject->Header.Type == MutantObject)
{
if ((CurrentObject->Header.SignalState > 0) ||
(Thread == CurrentObject->OwnerThread))
{
if (CurrentObject->Header.SignalState != (LONG)MINLONG)
{
KiSatisfyMutantWait(CurrentObject, Thread);
WaitStatus = (NTSTATUS)Thread->WaitStatus | Index;
goto DontWait;
}
else
{
KiReleaseDispatcherLock(Thread->WaitIrql);
ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
}
}
}
else if (CurrentObject->Header.SignalState > 0)
{
KiSatisfyNonMutantWait(CurrentObject);
WaitStatus = Index;
goto DontWait;
}
Index++;
} while (Index < Count);
}
else
{
// WaitAll: 所有对象都必须有信号
do
{
CurrentObject = (PKMUTANT)Object[Index];
ASSERT(CurrentObject->Header.Type != QueueObject);
if (CurrentObject->Header.Type == MutantObject)
{
if ((Thread == CurrentObject->OwnerThread) &&
(CurrentObject->Header.SignalState == (LONG)MINLONG))
{
KiReleaseDispatcherLock(Thread->WaitIrql);
ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
}
else if ((CurrentObject->Header.SignalState <= 0) &&
(Thread != CurrentObject->OwnerThread))
{
break; // 不满足,继续等待
}
}
else if (CurrentObject->Header.SignalState <= 0)
{
break; // 不满足,继续等待
}
Index++;
} while (Index < Count);
// 所有对象都有信号
if (Index == Count)
{
WaitBlock = WaitBlockArray;
do
{
CurrentObject = (PKMUTANT)WaitBlock->Object;
KiSatisfyObjectWait(CurrentObject, Thread);
WaitBlock = WaitBlock->NextWaitBlock;
} while(WaitBlock != WaitBlockArray);
WaitStatus = (NTSTATUS)Thread->WaitStatus;
goto DontWait;
}
}
// 检查Alertable状态
WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
if (WaitStatus != STATUS_WAIT_0) break;
// 设置超时
if (Timeout)
{
InterruptTime.QuadPart = KeQueryInterruptTime();
if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
{
WaitStatus = STATUS_TIMEOUT;
goto DontWait;
}
Timer->Header.Inserted = TRUE;
WaitBlock->NextWaitBlock = TimerBlock;
}
// 将所有等待块链入对象的等待列表
WaitBlock = WaitBlockArray;
do
{
CurrentObject = WaitBlock->Object;
InsertTailList(&CurrentObject->Header.WaitListHead,
&WaitBlock->WaitListEntry);
WaitBlock = WaitBlock->NextWaitBlock;
} while (WaitBlock != WaitBlockArray);
// 设置线程状态
Thread->State = Waiting;
KiAddThreadToWaitList(Thread, Swappable);
KiSetThreadSwapBusy(Thread);
if (Timeout)
KxInsertTimer(Timer, Hand);
else
KiReleaseDispatcherLockFromSynchLevel();
// 执行线程交换
WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
if (Timeout)
Timeout = KiRecalculateDueTime(OriginalDueTime, &DueTime, &NewDueTime);
}
WaitStart:
Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
KxMultiThreadWait();
KiAcquireDispatcherLockAtSynchLevel();
}
KiReleaseDispatcherLock(Thread->WaitIrql);
return WaitStatus;
DontWait:
KiReleaseDispatcherLockFromSynchLevel();
KiAdjustQuantumThread(Thread);
return WaitStatus;
}
源码位置:ntoskrnl/ke/wait.c#L586(file:///d:/reactos/ntoskrnl/ke/wait.c#L586)
5.14.5.4 KiWaitTest唤醒机制
c
VOID
FASTCALL
KiWaitTest(IN PVOID ObjectPointer,
IN KPRIORITY Increment)
{
PLIST_ENTRY WaitEntry, WaitList;
PKWAIT_BLOCK WaitBlock;
PKTHREAD WaitThread;
PKMUTANT FirstObject = ObjectPointer;
NTSTATUS WaitStatus;
// 遍历等待列表
WaitList = &FirstObject->Header.WaitListHead;
WaitEntry = WaitList->Flink;
while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList))
{
// 获取当前等待块
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
WaitThread = WaitBlock->Thread;
WaitStatus = STATUS_KERNEL_APC;
// 检查等待类型
if (WaitBlock->WaitType == WaitAny)
{
// WaitAny: 满足这个等待
WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
KiSatisfyObjectWait(FirstObject, WaitThread);
}
// 解除线程等待
KiUnwaitThread(WaitThread, WaitStatus, Increment);
WaitEntry = WaitList->Flink;
}
}
源码位置:ntoskrnl/ke/wait.c#L18(file:///d:/reactos/ntoskrnl/ke/wait.c#L18)
5.14.5.5 KiUnwaitThread/KiUnlinkThread实现
c
VOID
FASTCALL
KiUnlinkThread(IN PKTHREAD Thread,
IN LONG_PTR WaitStatus)
{
PKWAIT_BLOCK WaitBlock;
PKTIMER Timer;
// 更新等待状态
Thread->WaitStatus |= WaitStatus;
// 从所有对象的等待列表中移除等待块
WaitBlock = Thread->WaitBlockList;
do
{
RemoveEntryList(&WaitBlock->WaitListEntry);
WaitBlock = WaitBlock->NextWaitBlock;
} while (WaitBlock != Thread->WaitBlockList);
// 从线程等待列表移除
if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
// 移除定时器
Timer = &Thread->Timer;
if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
// 增加队列的活跃线程计数
if (Thread->Queue) Thread->Queue->CurrentCount++;
}
VOID
FASTCALL
KiUnwaitThread(IN PKTHREAD Thread,
IN LONG_PTR WaitStatus,
IN KPRIORITY Increment)
{
// 解除线程链接
KiUnlinkThread(Thread, WaitStatus);
// 设置优先级增量
ASSERT(Increment >= 0);
Thread->AdjustIncrement = (SCHAR)Increment;
Thread->AdjustReason = AdjustUnwait;
// 将线程放入就绪队列
KiReadyThread(Thread);
}
源码位置:ntoskrnl/ke/wait.c#L53(file:///d:/reactos/ntoskrnl/ke/wait.c#L53)
5.14.5.6 WaitAny vs WaitAll区别
| 特性 | WaitAny | WaitAll |
|---|---|---|
| 满足条件 | 任一对象有信号 | 所有对象都有信号 |
| 返回值 | 满足的对象索引 | STATUS_WAIT_0 |
| 对象状态 | 只消耗一个对象的信号 | 消耗所有对象的信号 |
| 适用场景 | 等待多个事件中的任一个 | 等待多个资源同时可用 |
WaitAny vs WaitAll 行为对比
WaitAny (bWaitAll = FALSE):
对象: Mutex(有信号), Semaphore(无信号), Event(有信号)
结果: 立即返回,WaitStatus = 0 (Mutex的索引)
Mutex变为无信号,Semaphore和Event不变
WaitAll (bWaitAll = TRUE):
对象: Mutex(有信号), Semaphore(无信号), Event(有信号)
结果: 阻塞等待,直到Semaphore变为有信号
满足后: 所有对象都变为无信号
5.14.5.7 超时处理机制
c
// 超时时间转换
PLARGE_INTEGER BaseFormatTimeOut(PLARGE_INTEGER Time, DWORD dwMilliseconds)
{
if (dwMilliseconds == INFINITE)
return NULL; // 无限等待
// 将毫秒转换为100纳秒单位(负数表示相对时间)
Time->QuadPart = -(LONG)(dwMilliseconds * 10000);
return Time;
}
// 超时检查
if (Timeout)
{
InterruptTime.QuadPart = KeQueryInterruptTime();
if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
{
WaitStatus = STATUS_TIMEOUT;
goto DontWait;
}
Timer->Header.Inserted = TRUE;
}
// APC后重新计算超时(相对时间需要重新计算)
Timeout = KiRecalculateDueTime(OriginalDueTime, &DueTime, &NewDueTime);
5.14.5.8 等待流程图
┌──────────────────────────────────────────────────────────────────────────────────────┐
│ 等待机制完整流程 │
├──────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ WaitForSingleObject/WaitForMultipleObjects │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ 1. 提升IRQL到SYNCH_LEVEL │ │
│ │ 2. 获取调度器锁 │ │
│ │ 3. 初始化等待块 (WaitBlock) │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ 检查对象信号状态 │ │
│ │ ├─► 有信号: KiSatisfyObjectWait → 返回成功 │ │
│ │ └─► 无信号: 继续等待流程 │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ 检查Alertable状态 │ │
│ │ ├─► 有用户APC待处理: 返回STATUS_USER_APC │ │
│ │ └─► 无APC: 继续等待 │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ 设置等待状态 │ │
│ │ ├─► 将WaitBlock链入对象的WaitListHead │ │
│ │ ├─► 设置Thread.State = Waiting │ │
│ │ ├─► 如有超时: 插入Timer │ │
│ │ └─► 释放调度器锁 │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ KiSwapThread │ │
│ │ ├─► 保存当前线程上下文 │ │
│ │ ├─► 选择下一个就绪线程 │ │
│ │ ├─► 切换到新线程 │ │
│ │ └─► 当前线程阻塞等待 │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ 等待被唤醒 │ │
│ │ ├─► 对象变为有信号: KiWaitTest → KiUnwaitThread │ │
│ │ ├─► 超时到期: Timer到期 → KiUnwaitThread │ │
│ │ ├─► APC待处理: 返回STATUS_KERNEL_APC → 重新等待 │ │
│ │ └─► 用户Alert: 返回STATUS_ALERTED │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ KiReadyThread │ │
│ │ ├─► 将线程放入就绪队列 │ │
│ │ ├─► 设置AdjustIncrement (优先级提升) │ │
│ │ └─► 请求调度中断 │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 线程恢复执行,返回等待状态 │
│ │
└──────────────────────────────────────────────────────────────────────────────────────┘
5.14.5.9 完整示例代码
c
#include <windows.h>
#include <stdio.h>
// WaitForSingleObject示例
void WaitForSingleObjectExample()
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 创建工作线程
HANDLE hThread = CreateThread(NULL, 0,
[](LPVOID hEvent) -> DWORD {
printf("Worker thread started\n");
Sleep(2000);
printf("Worker thread setting event\n");
SetEvent((HANDLE)hEvent);
return 0;
}, hEvent, 0, NULL);
// 等待工作线程完成
DWORD result = WaitForSingleObject(hEvent, 5000);
switch (result)
{
case WAIT_OBJECT_0:
printf("Event was signaled\n");
break;
case WAIT_TIMEOUT:
printf("Timeout waiting for event\n");
break;
case WAIT_FAILED:
printf("Wait failed: %d\n", GetLastError());
break;
}
CloseHandle(hThread);
CloseHandle(hEvent);
}
// WaitForMultipleObjects (WaitAny) 示例
void WaitForAnyExample()
{
HANDLE hEvents[3];
for (int i = 0; i < 3; i++)
hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
// 模拟随机事件触发
SetEvent(hEvents[1]); // 触发第二个事件
// 等待任一事件
DWORD result = WaitForMultipleObjects(3, hEvents, FALSE, INFINITE);
if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + 3)
{
printf("Event %d was signaled\n", result - WAIT_OBJECT_0);
}
for (int i = 0; i < 3; i++)
CloseHandle(hEvents[i]);
}
// WaitForMultipleObjects (WaitAll) 示例
void WaitForAllExample()
{
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
HANDLE hSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
HANDLE handles[3] = { hMutex, hSemaphore, hEvent };
// 等待所有对象(需要它们都有信号)
DWORD result = WaitForMultipleObjects(3, handles, TRUE, 5000);
if (result == WAIT_OBJECT_0)
{
printf("All objects were signaled\n");
// 所有对象现在都被我们持有
ReleaseMutex(hMutex);
ReleaseSemaphore(hSemaphore, 1, NULL);
}
else if (result == WAIT_TIMEOUT)
{
printf("Timeout - not all objects signaled\n");
}
CloseHandle(hMutex);
CloseHandle(hSemaphore);
CloseHandle(hEvent);
}
// Alertable等待示例
void AlertableWaitExample()
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 使用WaitForSingleObjectEx进行Alertable等待
DWORD result = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
switch (result)
{
case WAIT_OBJECT_0:
printf("Object signaled\n");
break;
case WAIT_IO_COMPLETION:
printf("APC was executed\n");
break;
case WAIT_TIMEOUT:
printf("Timeout\n");
break;
}
CloseHandle(hEvent);
}
5.14.6 临界区(Critical Section)
临界区是一种用户态优化的互斥机制,比内核互斥体更轻量,适用于进程内的线程同步。
5.14.6.1 RTL_CRITICAL_SECTION结构定义
c
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo; // 调试信息
LONG LockCount; // 锁计数 (-1表示未锁定)
LONG RecursionCount; // 递归计数
HANDLE OwningThread; // 当前所有者线程
HANDLE LockSemaphore; // 内核信号量(用于等待)
ULONG_PTR SpinCount; // 自旋次数(多处理器优化)
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
typedef struct _RTL_CRITICAL_SECTION_DEBUG {
USHORT Type;
USHORT CreatorBackTraceIndex;
struct _RTL_CRITICAL_SECTION *CriticalSection;
LIST_ENTRY ProcessLocksList; // 进程锁列表
ULONG EntryCount; // 进入次数
ULONG ContentionCount; // 竞争次数
union {
ULONG_PTR WineDebugString;
ULONG_PTR Spare[1];
struct {
ULONG Flags;
USHORT CreatorBackTraceIndexHigh;
USHORT SpareWORD;
};
};
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG;
LockCount含义:
| LockCount | 含义 |
|---|---|
-1 |
临界区未被占用 |
0 |
临界区被占用,无等待线程 |
> 0 |
临界区被占用,有等待线程 |
5.14.6.2 InitializeCriticalSection实现
c
VOID
NTAPI
RtlInitializeCriticalSection(IN PRTL_CRITICAL_SECTION CriticalSection)
{
NTSTATUS Status;
// 尝试初始化(可能失败)
Status = RtlInitializeCriticalSectionAndSpinCount(CriticalSection, 0);
if (!NT_SUCCESS(Status))
{
// 失败时抛出异常
RtlRaiseStatus(Status);
}
}
NTSTATUS
NTAPI
RtlInitializeCriticalSectionAndSpinCount(
IN PRTL_CRITICAL_SECTION CriticalSection,
IN ULONG SpinCount)
{
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
// 设置初始状态
CriticalSection->LockCount = -1;
CriticalSection->RecursionCount = 0;
CriticalSection->OwningThread = 0;
CriticalSection->LockSemaphore = 0;
CriticalSection->SpinCount = SpinCount;
// 创建调试信息
DebugInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*DebugInfo));
if (DebugInfo)
{
DebugInfo->Type = 0;
DebugInfo->CreatorBackTraceIndex = 0;
DebugInfo->CriticalSection = CriticalSection;
DebugInfo->EntryCount = 0;
DebugInfo->ContentionCount = 0;
InitializeListHead(&DebugInfo->ProcessLocksList);
CriticalSection->DebugInfo = DebugInfo;
}
return STATUS_SUCCESS;
}
源码位置:dll/ntdll/rtl/critical.c(file:///d:/reactos/dll/ntdll/rtl/critical.c)
5.14.6.3 EnterCriticalSection实现
c
VOID
NTAPI
RtlEnterCriticalSection(IN PRTL_CRITICAL_SECTION CriticalSection)
{
HANDLE Thread = NtCurrentTeb()->ClientId.UniqueThread;
// 检查是否已持有锁(递归)
if (CriticalSection->OwningThread == Thread)
{
// 递归获取,增加计数
CriticalSection->RecursionCount++;
return;
}
// 尝试快速获取(原子操作)
if (InterlockedIncrement(&CriticalSection->LockCount) == 0)
{
// 成功获取,设置所有者
CriticalSection->OwningThread = Thread;
CriticalSection->RecursionCount = 1;
return;
}
// 需要等待
RtlpWaitForCriticalSection(CriticalSection);
}
VOID
NTAPI
RtlpWaitForCriticalSection(IN PRTL_CRITICAL_SECTION CriticalSection)
{
ULONG LoopCount = 0;
HANDLE Thread = NtCurrentTeb()->ClientId.UniqueThread;
NTSTATUS Status;
// 自旋等待(如果有SpinCount)
if (CriticalSection->SpinCount)
{
while (LoopCount < CriticalSection->SpinCount)
{
if (CriticalSection->LockCount < 0)
{
if (InterlockedIncrement(&CriticalSection->LockCount) == 0)
{
CriticalSection->OwningThread = Thread;
CriticalSection->RecursionCount = 1;
return;
}
}
LoopCount++;
// 短暂暂停
NtYieldExecution();
}
}
// 需要阻塞等待
if (!CriticalSection->LockSemaphore)
{
// 创建信号量
Status = NtCreateSemaphore(&CriticalSection->LockSemaphore,
SEMAPHORE_ALL_ACCESS,
NULL, 0, 1);
if (!NT_SUCCESS(Status))
RtlRaiseStatus(Status);
}
// 更新调试信息
if (CriticalSection->DebugInfo)
CriticalSection->DebugInfo->ContentionCount++;
// 等待信号量
Status = NtWaitForSingleObject(CriticalSection->LockSemaphore, FALSE, NULL);
if (!NT_SUCCESS(Status))
RtlRaiseStatus(Status);
// 获取成功
CriticalSection->OwningThread = Thread;
CriticalSection->RecursionCount = 1;
}
5.14.6.4 LeaveCriticalSection实现
c
VOID
NTAPI
RtlLeaveCriticalSection(IN PRTL_CRITICAL_SECTION CriticalSection)
{
HANDLE Thread = NtCurrentTeb()->ClientId.UniqueThread;
// 检查所有权
if (CriticalSection->OwningThread != Thread)
{
// 不是所有者,抛出异常
RtlRaiseStatus(STATUS_INVALID_PARAMETER);
}
// 减少递归计数
CriticalSection->RecursionCount--;
if (CriticalSection->RecursionCount > 0)
{
// 还有递归,只减少计数
InterlockedDecrement(&CriticalSection->LockCount);
return;
}
// 完全释放
CriticalSection->OwningThread = 0;
// 减少锁计数
if (InterlockedDecrement(&CriticalSection->LockCount) >= 0)
{
// 有等待线程,释放信号量
if (CriticalSection->LockSemaphore)
{
NtReleaseSemaphore(CriticalSection->LockSemaphore, 1, NULL);
}
}
}
5.14.6.5 TryEnterCriticalSection实现
c
BOOLEAN
NTAPI
RtlTryEnterCriticalSection(IN PRTL_CRITICAL_SECTION CriticalSection)
{
HANDLE Thread = NtCurrentTeb()->ClientId.UniqueThread;
// 检查是否已持有锁
if (CriticalSection->OwningThread == Thread)
{
CriticalSection->RecursionCount++;
return TRUE;
}
// 尝试获取(不阻塞)
if (InterlockedCompareExchange(&CriticalSection->LockCount, 0, -1) == -1)
{
// 成功获取
CriticalSection->OwningThread = Thread;
CriticalSection->RecursionCount = 1;
return TRUE;
}
// 无法获取
return FALSE;
}
5.14.6.6 用户态优化策略
临界区采用多层优化策略:
临界区获取流程
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 1. 检查是否已持有锁(递归) │
│ ├─► 是: 增加RecursionCount,立即返回 │
│ └─► 否: 继续 │
│ │
│ 2. 尝试原子获取 (InterlockedIncrement) │
│ ├─► 成功: 设置OwningThread,立即返回 │
│ └─► 失败: 继续 │
│ │
│ 3. 自旋等待 (如果有SpinCount) │
│ ├─► 自旋期间成功获取: 立即返回 │
│ └─► 自旋结束: 继续 │
│ │
│ 4. 创建/使用内核信号量 │
│ ├─► NtWaitForSingleObject (阻塞等待) │
│ └─► 获取成功后设置所有者 │
│ │
└─────────────────────────────────────────────────────────────────┘
优化策略说明:
| 策略 | 说明 | 优势 |
|---|---|---|
| 递归检查 | 同线程快速获取 | 无需系统调用 |
| 原子操作 | InterlockedIncrement | 用户态快速获取 |
| 自旋等待 | SpinCount循环 | 多处理器优化,避免阻塞 |
| 内核信号量 | 仅竞争时使用 | 减少系统调用开销 |
5.14.6.7 与内核互斥体的区别
| 特性 | 临界区 (Critical Section) | 内核互斥体 (Mutex) |
|---|---|---|
| 实现位置 | 用户态(ntdll.dll) | 内核态(ntoskrnl) |
| 跨进程 | 不支持 | 支持(命名互斥体) |
| 获取开销 | 低(用户态原子操作) | 高(系统调用) |
| 等待开销 | 自旋 + 信号量 | 内核等待 |
| 递归支持 | 支持 | 支持 |
| 遗弃处理 | 不支持 | 支持(STATUS_ABANDONED) |
| 调试支持 | DebugInfo结构 | 无特殊调试信息 |
| 适用场景 | 进程内高性能同步 | 跨进程同步 |
5.14.6.8 完整示例代码
c
#include <windows.h>
#include <stdio.h>
CRITICAL_SECTION g_cs;
int g_sharedCounter = 0;
// 基本使用示例
void BasicCriticalSectionExample()
{
// 初始化临界区
InitializeCriticalSection(&g_cs);
// 创建工作线程
HANDLE hThreads[5];
for (int i = 0; i < 5; i++)
{
hThreads[i] = CreateThread(NULL, 0,
[](LPVOID) -> DWORD {
for (int j = 0; j < 1000; j++)
{
EnterCriticalSection(&g_cs);
g_sharedCounter++;
LeaveCriticalSection(&g_cs);
}
return 0;
}, NULL, 0, NULL);
}
// 等待所有线程完成
WaitForMultipleObjects(5, hThreads, TRUE, INFINITE);
printf("Final counter: %d\n", g_sharedCounter);
// 清理
for (int i = 0; i < 5; i++)
CloseHandle(hThreads[i]);
DeleteCriticalSection(&g_cs);
}
// 自旋优化示例
void SpinCountExample()
{
CRITICAL_SECTION cs;
// 设置自旋次数(多处理器优化)
InitializeCriticalSectionAndSpinCount(&cs, 4000);
// 使用临界区
EnterCriticalSection(&cs);
// ... 执行操作 ...
LeaveCriticalSection(&cs);
DeleteCriticalSection(&cs);
}
// TryEnterCriticalSection示例
void TryEnterExample()
{
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
// 尝试获取,不阻塞
if (TryEnterCriticalSection(&cs))
{
printf("Successfully entered critical section\n");
LeaveCriticalSection(&cs);
}
else
{
printf("Critical section is busy\n");
}
DeleteCriticalSection(&cs);
}
// 递归锁示例
void RecursiveCriticalSectionExample()
{
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
// 第一次进入
EnterCriticalSection(&cs);
printf("First enter, RecursionCount = %d\n", cs.RecursionCount);
// 第二次进入(递归)
EnterCriticalSection(&cs);
printf("Second enter, RecursionCount = %d\n", cs.RecursionCount);
// 第一次离开
LeaveCriticalSection(&cs);
printf("First leave, RecursionCount = %d\n", cs.RecursionCount);
// 第二次离开
LeaveCriticalSection(&cs);
printf("Second leave, RecursionCount = %d\n", cs.RecursionCount);
DeleteCriticalSection(&cs);
}
// 初始化失败处理示例
void SafeInitExample()
{
CRITICAL_SECTION cs;
NTSTATUS status;
// 使用Ex版本检查初始化是否成功
if (!InitializeCriticalSectionAndSpinCount(&cs, 0))
{
printf("Failed to initialize critical section\n");
return;
}
// 正常使用
EnterCriticalSection(&cs);
// ...
LeaveCriticalSection(&cs);
DeleteCriticalSection(&cs);
}
5.14.7 其他同步机制
除了互斥体、信号量、事件和临界区,Windows还提供了多种内核专用的高性能同步机制。
5.14.7.1 快速互斥体(FAST_MUTEX)
c
typedef struct _FAST_MUTEX {
volatile LONG Count; // 锁计数
PKTHREAD Owner; // 当前所有者
ULONG Contention; // 竞争计数
KEVENT Event; // 等待事件
ULONG OldIrql; // 保存的IRQL
} FAST_MUTEX, *PFAST_MUTEX;
特点:
| 特性 | FAST_MUTEX | KMUTEX |
|---|---|---|
| 递归支持 | 不支持 | 支持 |
| APC禁用 | 禁用普通APC | 可配置 |
| 获取开销 | 低(原子操作) | 高(调度器锁) |
| 适用场景 | 内核高性能互斥 | 通用互斥 |
获取函数:
c
VOID
FASTCALL
ExAcquireFastMutex(IN PFAST_MUTEX FastMutex)
{
// 尝试原子获取
if (InterlockedExchange(&FastMutex->Count, 0) == 1)
{
// 成功获取
FastMutex->Owner = KeGetCurrentThread();
FastMutex->OldIrql = KeRaiseIrqlToSynchLevel();
}
else
{
// 需要等待
FastMutex->Contention++;
KeWaitForSingleObject(&FastMutex->Event, WrMutex, KernelMode, FALSE, NULL);
FastMutex->Owner = KeGetCurrentThread();
FastMutex->OldIrql = KeRaiseIrqlToSynchLevel();
}
}
5.14.7.2 保护互斥体(GUARDED_MUTEX)
c
typedef struct _KGUARDED_MUTEX {
volatile LONG Count; // 锁计数
PKTHREAD Owner; // 当前所有者
ULONG Contention; // 竞争计数
KGATE Gate; // 等待门(比Event更轻量)
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
} KGUARDED_MUTEX, *PKGUARDED_MUTEX;
特点:
- 使用KGATE代替KEVENT,更轻量
- 支持APC禁用控制
- 比FAST_MUTEX更灵活
获取函数:
c
VOID
FASTCALL
KeAcquireGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex)
{
ULONG BitsToRemove, BitsToAdd;
LONG OldValue, NewValue;
GuardedMutex->Contention++;
BitsToRemove = GM_LOCK_BIT;
BitsToAdd = GM_LOCK_WAITER_INC;
for (;;)
{
OldValue = GuardedMutex->Count;
for (;;)
{
if (OldValue & GM_LOCK_BIT)
{
// 锁被占用,尝试释放锁位并添加等待者
NewValue = OldValue ^ BitsToRemove;
NewValue = InterlockedCompareExchange(&GuardedMutex->Count, NewValue, OldValue);
if (NewValue == OldValue) return;
}
else
{
// 锁未被占用,设置锁位
NewValue = OldValue + BitsToAdd;
NewValue = InterlockedCompareExchange(&GuardedMutex->Count, NewValue, OldValue);
if (NewValue == OldValue) break;
}
OldValue = NewValue;
}
// 等待门
KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
BitsToAdd = GM_LOCK_WAITER_WOKEN;
}
}
5.14.7.3 执行体资源(ERESOURCE)
c
typedef struct _ERESOURCE {
LIST_ENTRY SystemResourcesList; // 系统资源列表
POWNER_ENTRY OwnerTable; // 所有者表
SHORT ActiveCount; // 活跃计数
USHORT Flag; // 标志
volatile PKSEMAPHORE SharedWaiters; // 共享等待者信号量
volatile PKEVENT ExclusiveWaiters; // 独占等待者事件
OWNER_ENTRY OwnerEntry; // 所有者条目
ULONG ActiveEntries; // 活跃条目数
ULONG ContentionCount; // 竞争计数
ULONG NumberOfSharedWaiters; // 共享等待者数
ULONG NumberOfExclusiveWaiters; // 独占等待者数
PVOID Address; // 地址/创建者回溯索引
KSPIN_LOCK SpinLock; // 自旋锁
} ERESOURCE, *PERESOURCE;
特点:
- 支持读写锁语义(共享/独占)
- 多个线程可同时共享获取
- 独占获取时阻塞所有共享获取
- 适用于读多写少的场景
获取模式:
| 模式 | 函数 | 说明 |
|---|---|---|
| 共享获取 | ExAcquireResourceSharedLite | 多线程可同时持有 |
| 独占获取 | ExAcquireResourceExclusiveLite | 只有获取者持有 |
| 尝试共享 | ExTryToAcquireResourceSharedLite | 不阻塞,尝试获取 |
| 尝试独占 | ExTryToAcquireResourceExclusiveLite | 不阻塞,尝试获取 |
5.14.7.4 推锁(PUSH_LOCK)
推锁是一种轻量级的读写锁,使用原子操作实现:
c
typedef struct _EX_PUSH_LOCK {
union {
struct {
ULONG_PTR Locked: 1; // 锁定状态
ULONG_PTR Waiting: 1; // 有等待者
ULONG_PTR Shared: 1; // 共享锁
ULONG_PTR NumberOfSharedWaiters: 1;
ULONG_PTR NumberOfExclusiveWaiters: 1;
ULONG_PTR Value: (sizeof(ULONG_PTR) * 8 - 5);
};
ULONG_PTR Ptr;
};
} EX_PUSH_LOCK, *PEX_PUSH_LOCK;
特点:
- 使用位域紧凑存储状态
- 原子操作获取/释放
- 支持共享和独占模式
- 比ERESOURCE更轻量
5.14.7.5 同步机制对比表
同步机制对比
┌──────────────────────────────────────────────────────────────────────────────┐
│ 机制 │ 递归 │ APC │ 跨进程 │ 性能 │ 适用场景 │
├───────────────────┼──────┼──────┼────────┼──────┼───────────────────────────┤
│ Mutex/Mutant │ 支持 │ 可配 │ 支持 │ 低 │ 通用互斥,跨进程同步 │
│ Semaphore │ 否 │ 否 │ 支持 │ 低 │ 计数控制,资源池 │
│ Event │ 否 │ 否 │ 支持 │ 低 │ 事件通知,条件等待 │
│ Critical Section │ 支持 │ 否 │ 否 │ 高 │ 进程内高性能互斥 │
│ FAST_MUTEX │ 否 │ 禁用 │ 否 │ 高 │ 内核高性能互斥 │
│ GUARDED_MUTEX │ 否 │ 可配 │ 否 │ 高 │ 内核互斥,APC控制 │
│ ERESOURCE │ 支持 │ 否 │ 否 │ 中 │ 读写锁,读多写少 │
│ PUSH_LOCK │ 否 │ 否 │ 否 │ 最高 │ 轻量读写锁 │
└───────────────────┴──────┴──────┴────────┴──────┴───────────────────────────┘
5.14.7.6 完整示例代码
c
#include <ntddk.h>
// FAST_MUTEX示例
FAST_MUTEX g_FastMutex;
void FastMutexExample()
{
// 初始化
ExInitializeFastMutex(&g_FastMutex);
// 获取
ExAcquireFastMutex(&g_FastMutex);
// 执行操作
// ...
// 释放
ExReleaseFastMutex(&g_FastMutex);
}
// GUARDED_MUTEX示例
KGUARDED_MUTEX g_GuardedMutex;
void GuardedMutexExample()
{
// 初始化
KeInitializeGuardedMutex(&g_GuardedMutex);
// 获取
KeAcquireGuardedMutex(&g_GuardedMutex);
// 执行操作
// ...
// 释放
KeReleaseGuardedMutex(&g_GuardedMutex);
}
// ERESOURCE示例(读写锁)
ERESOURCE g_Resource;
void ResourceExample()
{
// 初始化
ExInitializeResourceLite(&g_Resource);
// 共享获取(读)
ExAcquireResourceSharedLite(&g_Resource, TRUE);
// 执行读操作
ExReleaseResourceLite(&g_Resource);
// 独占获取(写)
ExAcquireResourceExclusiveLite(&g_Resource, TRUE);
// 执行写操作
ExReleaseResourceLite(&g_Resource);
// 清理
ExDeleteResourceLite(&g_Resource);
}
// PUSH_LOCK示例
EX_PUSH_LOCK g_PushLock;
void PushLockExample()
{
// 初始化
ExInitializePushLock(&g_PushLock);
// 共享获取
ExAcquirePushLockShared(&g_PushLock);
// 执行读操作
ExReleasePushLockShared(&g_PushLock);
// 独占获取
ExAcquirePushLockExclusive(&g_PushLock);
// 执行写操作
ExReleasePushLockExclusive(&g_PushLock);
}
5.14.8 APC与线程同步
异步过程调用(APC)是Windows实现异步操作的核心机制,它与等待机制密切相关。
5.14.8.1 APC队列结构
每个线程有两个APC队列:
c
typedef struct _KAPC_STATE {
LIST_ENTRY ApcListHead[2]; // APC队列:[0]=内核APC,[1]=用户APC
PKPROCESS Process; // 所属进程
BOOLEAN KernelApcInProgress; // 内核APC正在执行
BOOLEAN KernelApcPending; // 内核APC待处理
BOOLEAN UserApcPending; // 用户APC待处理
BOOLEAN ApcQueueable; // 是否可排队APC
} KAPC_STATE, *PKAPC_STATE;
APC类型:
| 类型 | 队列索引 | 执行时机 | 特点 |
|---|---|---|---|
| 内核模式APC | 0 | 玷点返回时 | 可中断等待 |
| 特殊内核APC | 0 | 更高优先级 | 可禁用普通APC |
| 用户模式APC | 1 | Alertable等待返回时 | 用户态执行 |
5.14.8.2 APC与等待机制的关系
APC与等待机制交互
┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ 线程进入等待 (KeWaitForSingleObject) │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 检查 KernelApcPending │ │
│ │ ├─► TRUE 且 SpecialApcDisable=0 且 WaitIrql < APC_LEVEL │ │
│ │ │ → 释放调度器锁,返回 STATUS_KERNEL_APC │ │
│ │ │ → 执行APC后重新等待 │ │
│ │ └─► FALSE: 继续等待流程 │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 检查 Alertable + UserApcPending │ │
│ │ ├─► Alertable=TRUE 且 UserApcPending=TRUE │ │
│ │ │ → 返回 STATUS_USER_APC │ │
│ │ │ → 用户态执行APC后重新等待 │ │
│ │ └─► 否则: 继续等待 │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
5.14.8.3 KiCheckAlertability实现
c
NTSTATUS
FASTCALL
KiCheckAlertability(IN PKTHREAD Thread,
IN BOOLEAN Alertable,
IN KPROCESSOR_MODE WaitMode)
{
// 检查线程是否被Alerted
if (Thread->Alerted[WaitMode])
{
Thread->Alerted[WaitMode] = FALSE;
return STATUS_ALERTED;
}
// 检查用户APC(仅Alertable等待)
if (Alertable)
{
if (Thread->ApcState.UserApcPending)
{
return STATUS_USER_APC;
}
}
return STATUS_WAIT_0; // 继续等待
}
5.14.8.4 特殊APC禁用机制
某些同步原语会禁用APC:
c
// KMUTANT中的ApcDisable字段
typedef struct _KMUTANT {
DISPATCHER_HEADER Header;
LIST_ENTRY MutantListEntry;
PKTHREAD OwnerThread;
BOOLEAN Abandoned;
UCHAR ApcDisable; // APC禁用计数
} KMUTANT;
// KMUTEX固定禁用APC
KeInitializeMutex(&Mutex);
// Mutex.ApcDisable = 1
// KMUTANT可配置
KeInitializeMutant(&Mutant, FALSE);
// Mutant.ApcDisable = 0 (不禁用)
APC禁用机制:
| 同步原语 | ApcDisable | 效果 |
|---|---|---|
| KMUTEX | 1 | 禁用普通内核APC |
| KMUTANT | 可配置 | 用户态互斥体通常不禁用 |
| FAST_MUTEX | 隐式禁用 | 提升IRQL禁用APC |
| GUARDED_MUTEX | 可配置 | KernelApcDisable字段控制 |
5.14.8.5 Alertable等待详解
Alertable等待允许用户APC中断等待:
c
// Alertable等待示例
DWORD result = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
switch (result)
{
case WAIT_OBJECT_0:
// 对象有信号
break;
case WAIT_IO_COMPLETION:
// 用户APC被执行,等待被中断
// APC完成后,通常需要重新等待
break;
case WAIT_TIMEOUT:
// 超时
break;
}
Alertable等待流程:
Alertable等待流程
┌─────────────────────────────────────────────────────────────────┐
│ │
│ WaitForSingleObjectEx(hObject, Timeout, TRUE) │
│ │ │
│ ▼ │
│ NtWaitForSingleObject(hObject, TRUE, Timeout) │
│ │ │
│ ▼ │
│ KeWaitForSingleObject(Object, WaitMode, TRUE, Timeout) │
│ │ │
│ ▼ │
│ KiCheckAlertability(Thread, TRUE, WaitMode) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Thread->Alerted[UserMode]? │ │
│ │ ├─► TRUE: 清除标志,返回STATUS_ALERTED │ │
│ │ └─► FALSE: 继续 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Thread->ApcState.UserApcPending? │ │
│ │ ├─► TRUE: 返回STATUS_USER_APC │ │
│ │ │ → 用户态执行APC │ │
│ │ │ → NtWaitForSingleObject重新等待 │ │
│ │ └─► FALSE: 进入等待 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
5.14.8.6 APC打断等待的示例
c
#include <windows.h>
#include <stdio.h>
// APC回调函数
VOID CALLBACK APCProc(ULONG_PTR dwParam)
{
printf("APC executed with parameter: %lu\n", dwParam);
}
// Alertable等待示例
void AlertableWaitWithAPC()
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 创建定时器线程,在2秒后排队APC
HANDLE hTimerThread = CreateThread(NULL, 0,
[](LPVOID hEvent) -> DWORD {
Sleep(2000);
// 排队用户APC到主线程
QueueUserAPC(APCProc, GetCurrentThread(), 42);
return 0;
}, hEvent, 0, NULL);
// Alertable等待(会被APC打断)
DWORD result = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
switch (result)
{
case WAIT_IO_COMPLETION:
printf("Wait was interrupted by APC\n");
// APC已被执行
break;
case WAIT_OBJECT_0:
printf("Event was signaled\n");
break;
}
CloseHandle(hTimerThread);
CloseHandle(hEvent);
}
// 多次Alertable等待示例
void MultipleAlertableWait()
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 排队多个APC
for (int i = 0; i < 3; i++)
{
QueueUserAPC([](ULONG_PTR param) {
printf("APC %lu executed\n", param);
}, GetCurrentThread(), i);
}
// 每次等待只处理一个APC
for (int i = 0; i < 3; i++)
{
DWORD result = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
if (result == WAIT_IO_COMPLETION)
printf("APC %d processed\n", i + 1);
}
CloseHandle(hEvent);
}
5.14.9 线程调度与等待
线程等待涉及复杂的调度机制,包括状态转换、上下文切换和优先级调整。
5.14.9.1 线程状态转换
c
typedef enum _KTHREAD_STATE {
Initialized, // 已初始化,未开始执行
Ready, // 就绪,等待调度
Running, // 正在运行
Standby, // 待命,即将运行
Terminated, // 已终止
Waiting, // 正在等待
Transition, // 过渡,等待资源
DeferredReady, // 延迟就绪
GateWait // 门等待(Windows Server 2003+)
} KTHREAD_STATE;
状态转换图:
线程状态转换图
┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────┐ │
│ │ Initialized │ ─────────────────────────────────────────► Ready │
│ └─────────────┘ │ │
│ │ │
│ ┌─────────────┐ │ │
│ │ Ready │ ◄──────────────────────────────────────────┤ │
│ └─────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ Standby │ ◄──────────────────────────────────────────┤ │
│ └─────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ Running │ ─────────────────────────────────────────►│ │
│ └─────────────┘ │ │
│ │ │ │
│ ├─► KiSwapThread ───────────────────────────────────►│ │
│ │ │ │
│ │ ┌─────────────┐ │ │
│ └─► │ Waiting │ ◄───────────────────────────────┘ │
│ └─────────────┘ │
│ │ │
│ ├─► KiUnwaitThread ──────────────────────► Ready │
│ │ │
│ ├─► 超时 ─────────────────────────────────► Ready │
│ │ │
│ └─► APC ──────────────────────────────────► Running │
│ │
│ ┌─────────────┐ │
│ │ Terminated │ ◄────────────────────────────────────────── Running │
│ └─────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
5.14.9.2 KiSwapThread实现
c
BOOLEAN
FASTCALL
KiSwapThread(IN PKTHREAD Thread,
IN PKPRCB Prcb)
{
PKTHREAD NextThread;
BOOLEAN PendingApc;
// 设置当前线程交换忙状态
KiSetThreadSwapBusy(Thread);
// 获取下一个线程
NextThread = Prcb->NextThread;
if (NextThread)
{
// 有预选的下一个线程
Prcb->NextThread = NULL;
}
else
{
// 从就绪队列选择
NextThread = KiSelectReadyThread(Prcb);
if (!NextThread)
{
// 无就绪线程,使用空闲线程
NextThread = Prcb->IdleThread;
}
}
// 更新PRCB
Prcb->CurrentThread = NextThread;
NextThread->State = Running;
// 保存当前线程等待IRQL
Thread->WaitIrql = KeGetCurrentIrql();
// 执行上下文切换
PendingApc = KiSwapContext(Thread->WaitIrql, Thread);
return PendingApc;
}
5.14.9.3 KiReadyThread实现
c
VOID
FASTCALL
KiReadyThread(IN PKTHREAD Thread)
{
PKPRCB Prcb = KeGetCurrentPrcb();
KIRQL OldIrql;
// 获取PRCB锁
OldIrql = KiAcquirePrcbLock(Prcb);
// 设置线程状态
Thread->State = Ready;
Thread->WaitTime = KeQueryInterruptTime();
// 处理优先级调整
if (Thread->AdjustReason == AdjustUnwait)
{
// 等待结束时的优先级提升
Thread->Priority += Thread->AdjustIncrement;
if (Thread->Priority > Thread->BasePriority + THREAD_ALERT_INCREMENT)
Thread->Priority = Thread->BasePriority + THREAD_ALERT_INCREMENT;
}
// 将线程放入就绪队列
KiInsertReadyThread(Thread, Prcb);
// 检查是否需要重新调度
if (Thread->Priority > Prcb->CurrentThread->Priority)
{
// 更高优先级线程就绪,请求调度
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
// 释放PRCB锁
KiReleasePrcbLock(OldIrql);
}
5.14.9.4 KiExitDispatcher实现
c
VOID
FASTCALL
KiExitDispatcher(IN KIRQL OldIrql)
{
PKPRCB Prcb = KeGetCurrentPrcb();
PKTHREAD Thread, NextThread;
BOOLEAN PendingApc;
// 确保在同步级别
ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
// 检查延迟就绪列表
KiCheckDeferredReadyList(Prcb);
// 如果调用者IRQL >= DISPATCH_LEVEL,快速退出
if (OldIrql >= DISPATCH_LEVEL)
{
if (Prcb->NextThread && !Prcb->DpcRoutineActive)
{
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
goto Quickie;
}
// 检查是否有预选线程
if (!Prcb->NextThread) goto Quickie;
// 获取PRCB锁
KiAcquirePrcbLock(Prcb);
// 获取线程
NextThread = Prcb->NextThread;
Thread = Prcb->CurrentThread;
// 设置交换忙状态
KiSetThreadSwapBusy(Thread);
// 更新PRCB
Prcb->NextThread = NULL;
Prcb->CurrentThread = NextThread;
NextThread->State = Running;
// 将当前线程放入就绪队列
KxQueueReadyThread(Thread, Prcb);
// 设置等待IRQL
Thread->WaitIrql = OldIrql;
// 执行上下文切换
PendingApc = KiSwapContext(OldIrql, Thread);
if (PendingApc)
{
// 有APC待处理
KeLowerIrql(APC_LEVEL);
KiDeliverApc(KernelMode, NULL, NULL);
}
Quickie:
KeLowerIrql(OldIrql);
}
源码位置:ntoskrnl/ke/wait.c#L199(file:///d:/reactos/ntoskrnl/ke/wait.c#L199)
5.14.9.5 调度器锁机制
c
// 获取调度器锁
KIRQL
KiAcquireDispatcherLock()
{
KIRQL OldIrql;
OldIrql = KeRaiseIrqlToSynchLevel();
KiAcquireSpinLock(&DispatcherLock);
return OldIrql;
}
// 释放调度器锁
VOID
KiReleaseDispatcherLock(IN KIRQL OldIrql)
{
KiReleaseSpinLock(&DispatcherLock);
KeLowerIrql(OldIrql);
}
// 在同步级别获取调度器锁
VOID
KiAcquireDispatcherLockAtSynchLevel()
{
ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
KiAcquireSpinLock(&DispatcherLock);
}
调度器锁的作用:
- 保护调度器对象(DISPATCHER_HEADER)的修改
- 保护线程状态转换
- 保护等待列表操作
- 必须在SYNCH_LEVEL(同步级别)获取
5.14.9.6 等待中的优先级提升
c
// 等待结束时的优先级提升
Thread->AdjustIncrement = (SCHAR)Increment;
Thread->AdjustReason = AdjustUnwait;
// 在KiReadyThread中应用
if (Thread->AdjustReason == AdjustUnwait)
{
Thread->Priority += Thread->AdjustIncrement;
// 限制提升幅度
if (Thread->Priority > Thread->BasePriority + THREAD_ALERT_INCREMENT)
Thread->Priority = Thread->BasePriority + THREAD_ALERT_INCREMENT;
}
优先级提升的目的:
- 防止优先级反转
- 提高I/O完成线程的响应性
- 增加刚唤醒线程的调度机会
5.14.10 设计哲学问答
本节通过问答形式深入探讨线程同步机制的设计决策。
Q1: 为什么Windows有两种互斥体(Mutant和Mutex)?
答: KMUTANT和KMUTEX虽然结构相似,但设计目的不同:
-
KMUTANT:面向用户态,支持递归获取,ApcDisable可配置。用户态互斥体需要支持递归,且不应禁用APC(否则会影响线程的正常执行)。
-
KMUTEX:面向内核态,ApcDisable固定为1。内核互斥体通常用于保护内核数据结构,禁用APC可以防止APC干扰临界区执行。
这种分离设计体现了Windows对不同使用场景的针对性优化。
Q2: 为什么临界区比内核互斥体更高效?
答: 临界区采用多层优化策略:
- 用户态原子操作:无竞争时完全在用户态完成,无需系统调用
- 自旋等待:多处理器环境下,短时间自旋避免阻塞开销
- 延迟创建信号量:仅在发生竞争时才创建内核信号量
- 递归检查优化:同线程递归获取只需检查字段,无额外开销
内核互斥体每次获取/释放都需要系统调用和调度器锁操作,开销显著更高。
Q3: 为什么通知事件唤醒所有等待者,而同步事件只唤醒一个?
答: 这体现了两种不同的同步语义:
-
通知事件:用于广播通知,如"数据已准备好"或"配置已更新"。所有等待者都需要知道这个事件,因此唤醒全部。
-
同步事件:用于资源分配,如"有一个工作项可用"。唤醒一个等待者后自动复位,防止多个线程竞争同一资源。
这种设计避免了用户需要手动管理唤醒逻辑,简化了编程模型。
Q4: 为什么WaitForMultipleObjects有WaitAny和WaitAll两种模式?
答: 这对应两种不同的等待语义:
-
WaitAny:等待"任一条件满足",适用于多路等待。例如等待多个网络连接中的任一个有数据到达。
-
WaitAll:等待"所有条件满足",适用于资源组合。例如同时需要文件锁和内存锁才能执行操作。
WaitAll的实现更复杂,需要检查所有对象的状态,且需要原子性地获取所有对象。
Q5: 为什么APC可以打断等待?
答: APC打断等待是Windows异步编程的核心机制:
-
用户APC:允许异步操作完成后通知等待线程,如异步I/O完成。Alertable等待使线程能够响应这些通知。
-
内核APC:允许内核执行紧急操作,如线程挂起、内存管理。内核APC打断等待确保这些操作能及时执行。
这种设计使Windows能够高效处理异步操作,同时保持线程调度的灵活性。
Q6: 为什么FAST_MUTEX不支持递归?
答: FAST_MUTEX的设计目标是极致性能:
- 递归检查需要额外的字段和逻辑
- 递归锁需要维护递归计数
- 这些都会增加获取/释放的开销
FAST_MUTEX用于内核高性能场景,通常由驱动开发者确保不会递归获取。如果需要递归支持,应使用KMUTEX。
Q7: 为什么ERESOURCE支持读写锁语义?
答: ERESOURCE的设计针对读多写少的场景:
- 多个线程可以同时共享获取(读)
- 独占获取(写)时阻塞所有共享获取
- 这比简单互斥体更高效,因为读操作可以并发执行
典型应用场景包括文件系统缓存、注册表访问等读密集型操作。
Q8: 为什么等待需要调度器锁?
答: 调度器锁保护关键调度数据:
- DISPATCHER_HEADER.WaitListHead:等待列表可能被多个线程同时修改(排队等待和唤醒)
- 线程状态:状态转换必须原子进行,防止调度器看到不一致状态
- 定时器操作:超时定时器的插入和移除需要同步
调度器锁确保等待机制的原子性和一致性。
Q9: 为什么线程等待时会禁用抢占?
答: 等待期间禁用抢占是为了:
- 防止竞争条件:如果等待期间被抢占,其他线程可能修改等待状态
- 确保原子性:等待操作必须原子完成(检查状态、排队、切换)
- 简化实现:禁用抢占避免了复杂的同步逻辑
等待完成后,线程进入就绪队列,抢占恢复正常。
Q10: 为什么Windows有这么多同步原语?
答: 不同同步原语针对不同场景优化:
| 场景 | 适用原语 | 原因 |
|---|---|---|
| 进程内高性能互斥 | Critical Section | 用户态优化,低开销 |
| 跨进程互斥 | Mutex | 支持命名和跨进程 |
| 计数控制 | Semaphore | 支持计数器机制 |
| 事件通知 | Event | 支持广播/单播 |
| 内核高性能互斥 | FAST_MUTEX | 禁用APC,极致性能 |
| 读写场景 | ERESOURCE | 支持共享/独占模式 |
| 轻量读写 | PUSH_LOCK | 位域紧凑,原子操作 |
这种多样性体现了Windows对不同性能和功能需求的针对性设计。
总结
核心要点回顾
-
同步原语分类:内核同步对象(Mutex、Semaphore、Event)、用户态同步(Critical Section)、内核内部同步(FAST_MUTEX、ERESOURCE)
-
调度器对象结构:所有内核同步对象基于DISPATCHER_HEADER,包含Type、SignalState和WaitListHead
-
互斥体特性:支持递归获取、所有权机制、遗弃检测、APC禁用控制
-
信号量机制:计数器控制并发,支持WaitAny/WaitAll,有上限限制
-
事件类型:通知事件唤醒所有等待者,同步事件唤醒一个并自动复位
-
等待机制:KWAIT_BLOCK关联线程与对象,KiWaitTest唤醒等待者,KiUnwaitThread解除等待
-
临界区优化:用户态原子操作、自旋等待、延迟创建信号量
-
APC与等待:内核APC可打断等待,用户APC需要Alertable等待
-
线程调度:状态转换(Running→Waiting→Ready),KiSwapThread切换上下文
-
设计哲学:不同同步原语针对不同场景优化,性能与功能权衡
本章代码索引
| 文件 | 主要函数 | 说明 |
|---|---|---|
| ntoskrnl/ke/mutex.c(file:///d:/reactos/ntoskrnl/ke/mutex.c) | KeInitializeMutant, KeReleaseMutant | 互斥体内核实现 |
| ntoskrnl/ke/semphobj.c(file:///d:/reactos/ntoskrnl/ke/semphobj.c) | KeInitializeSemaphore, KeReleaseSemaphore | 信号量内核实现 |
| ntoskrnl/ke/eventobj.c(file:///d:/reactos/ntoskrnl/ke/eventobj.c) | KeInitializeEvent, KeSetEvent | 事件内核实现 |
| ntoskrnl/ke/wait.c(file:///d:/reactos/ntoskrnl/ke/wait.c) | KeWaitForSingleObject, KeWaitForMultipleObjects, KiWaitTest | 等待机制实现 |
| ntoskrnl/ex/mutant.c(file:///d:/reactos/ntoskrnl/ex/mutant.c) | NtCreateMutant, NtReleaseMutant | 互斥体系统调用 |
| ntoskrnl/ex/sem.c(file:///d:/reactos/ntoskrnl/ex/sem.c) | NtCreateSemaphore, NtReleaseSemaphore | 信号量系统调用 |
| ntoskrnl/ex/event.c(file:///d:/reactos/ntoskrnl/ex/event.c) | NtCreateEvent, NtSetEvent | 事件系统调用 |
| ntoskrnl/ob/obwait.c(file:///d:/reactos/ntoskrnl/ob/obwait.c) | NtWaitForSingleObject, NtWaitForMultipleObjects | 对象等待系统调用 |
| dll/win32/kernel32/client/synch.c(file:///d:/reactos/dll/win32/kernel32/client/synch.c) | WaitForSingleObject, CreateMutex, CreateEvent | 用户态同步API |
| dll/ntdll/rtl/critical.c(file:///d:/reactos/dll/ntdll/rtl/critical.c) | RtlInitializeCriticalSection, RtlEnterCriticalSection | 临界区实现 |
| sdk/include/xdk/ketypes.h(file:///d:/reactos/sdk/include/xdk/ketypes.h) | KWAIT_BLOCK, KMUTANT, KSEMAPHORE, KEVENT | 同步对象结构定义 |
| sdk/include/ndk/rtltypes.h(file:///d:/reactos/sdk/include/ndk/rtltypes.h) | RTL_CRITICAL_SECTION | 临界区结构定义 |
| sdk/include/xdk/extypes.h(file:///d:/reactos/sdk/include/xdk/extypes.h) | FAST_MUTEX, ERESOURCE | 内核同步结构定义 |
本节完。下一节将讨论Windows的进程间通信机制。