Reactos 第 5 章 进程与线程 — 5.14 Windows线程间的相互作用

第 5 章 进程与线程 --- 5.14 Windows线程间的相互作用

本节深入剖析 Windows/ReactOS 中线程间相互作用(同步机制)的完整实现。

概述

线程同步是现代操作系统中实现多线程协作的核心机制。由于多个线程可能同时访问共享资源,操作系统必须提供同步原语来协调线程的执行顺序,防止数据竞争和确保程序的正确性。

线程同步的本质是什么?

线程同步是一种协调机制,在保证正确性的前提下,控制多个线程对共享资源的访问顺序。同步原语的设计需要在性能、易用性和安全性之间取得平衡。

想象一个工厂场景:

  • 互斥体(Mutex):车间门口的钥匙,只有拿到钥匙的工人才能进入,其他人必须等待;
  • 信号量(Semaphore):停车场的车位计数器,显示剩余车位数量,车位满了就不能进入;
  • 事件(Event):广播通知系统,通知所有等待的工人某个事件已经发生;
  • 临界区(Critical Section):车间内部的快速通行证,比互斥体更轻量;
  • 等待机制:工人在门口排队等待,直到条件满足才能进入。

本节内容概览

  1. 5.14.0 框架图:Windows线程同步完整架构图;
  2. 5.14.1 线程同步机制概述:同步原语分类、调度器对象结构;
  3. 5.14.2 互斥体(Mutex/Mutant):KMUTANT结构、内核实现、系统调用;
  4. 5.14.3 信号量(Semaphore):KSEMAPHORE结构、计数器机制;
  5. 5.14.4 事件(Event):KEVENT结构、通知事件与同步事件;
  6. 5.14.5 等待机制:KWAIT_BLOCK、KeWaitForSingleObject/MultipleObjects;
  7. 5.14.6 临界区(Critical Section):RTL_CRITICAL_SECTION、用户态实现;
  8. 5.14.7 其他同步机制:FAST_MUTEX、GUARDED_MUTEX、ERESOURCE;
  9. 5.14.8 APC与线程同步:APC队列、Alertable等待;
  10. 5.14.9 线程调度与等待:状态转换、KiSwapThread;
  11. 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 时被唤醒

所有权机制:

  1. 每个互斥体记录当前所有者线程 (OwnerThread)
  2. 只有所有者线程才能释放互斥体
  3. 线程终止时,其拥有的互斥体被遗弃 (Abandoned = TRUE)
  4. 遗弃的互斥体被释放,等待线程收到 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: 为什么临界区比内核互斥体更高效?

答: 临界区采用多层优化策略:

  1. 用户态原子操作:无竞争时完全在用户态完成,无需系统调用
  2. 自旋等待:多处理器环境下,短时间自旋避免阻塞开销
  3. 延迟创建信号量:仅在发生竞争时才创建内核信号量
  4. 递归检查优化:同线程递归获取只需检查字段,无额外开销

内核互斥体每次获取/释放都需要系统调用和调度器锁操作,开销显著更高。

Q3: 为什么通知事件唤醒所有等待者,而同步事件只唤醒一个?

答: 这体现了两种不同的同步语义:

  • 通知事件:用于广播通知,如"数据已准备好"或"配置已更新"。所有等待者都需要知道这个事件,因此唤醒全部。

  • 同步事件:用于资源分配,如"有一个工作项可用"。唤醒一个等待者后自动复位,防止多个线程竞争同一资源。

这种设计避免了用户需要手动管理唤醒逻辑,简化了编程模型。

Q4: 为什么WaitForMultipleObjects有WaitAny和WaitAll两种模式?

答: 这对应两种不同的等待语义:

  • WaitAny:等待"任一条件满足",适用于多路等待。例如等待多个网络连接中的任一个有数据到达。

  • WaitAll:等待"所有条件满足",适用于资源组合。例如同时需要文件锁和内存锁才能执行操作。

WaitAll的实现更复杂,需要检查所有对象的状态,且需要原子性地获取所有对象。

Q5: 为什么APC可以打断等待?

答: APC打断等待是Windows异步编程的核心机制:

  1. 用户APC:允许异步操作完成后通知等待线程,如异步I/O完成。Alertable等待使线程能够响应这些通知。

  2. 内核APC:允许内核执行紧急操作,如线程挂起、内存管理。内核APC打断等待确保这些操作能及时执行。

这种设计使Windows能够高效处理异步操作,同时保持线程调度的灵活性。

Q6: 为什么FAST_MUTEX不支持递归?

答: FAST_MUTEX的设计目标是极致性能:

  • 递归检查需要额外的字段和逻辑
  • 递归锁需要维护递归计数
  • 这些都会增加获取/释放的开销

FAST_MUTEX用于内核高性能场景,通常由驱动开发者确保不会递归获取。如果需要递归支持,应使用KMUTEX。

Q7: 为什么ERESOURCE支持读写锁语义?

答: ERESOURCE的设计针对读多写少的场景:

  • 多个线程可以同时共享获取(读)
  • 独占获取(写)时阻塞所有共享获取
  • 这比简单互斥体更高效,因为读操作可以并发执行

典型应用场景包括文件系统缓存、注册表访问等读密集型操作。

Q8: 为什么等待需要调度器锁?

答: 调度器锁保护关键调度数据:

  1. DISPATCHER_HEADER.WaitListHead:等待列表可能被多个线程同时修改(排队等待和唤醒)
  2. 线程状态:状态转换必须原子进行,防止调度器看到不一致状态
  3. 定时器操作:超时定时器的插入和移除需要同步

调度器锁确保等待机制的原子性和一致性。

Q9: 为什么线程等待时会禁用抢占?

答: 等待期间禁用抢占是为了:

  1. 防止竞争条件:如果等待期间被抢占,其他线程可能修改等待状态
  2. 确保原子性:等待操作必须原子完成(检查状态、排队、切换)
  3. 简化实现:禁用抢占避免了复杂的同步逻辑

等待完成后,线程进入就绪队列,抢占恢复正常。

Q10: 为什么Windows有这么多同步原语?

答: 不同同步原语针对不同场景优化:

场景 适用原语 原因
进程内高性能互斥 Critical Section 用户态优化,低开销
跨进程互斥 Mutex 支持命名和跨进程
计数控制 Semaphore 支持计数器机制
事件通知 Event 支持广播/单播
内核高性能互斥 FAST_MUTEX 禁用APC,极致性能
读写场景 ERESOURCE 支持共享/独占模式
轻量读写 PUSH_LOCK 位域紧凑,原子操作

这种多样性体现了Windows对不同性能和功能需求的针对性设计。


总结

核心要点回顾

  1. 同步原语分类:内核同步对象(Mutex、Semaphore、Event)、用户态同步(Critical Section)、内核内部同步(FAST_MUTEX、ERESOURCE)

  2. 调度器对象结构:所有内核同步对象基于DISPATCHER_HEADER,包含Type、SignalState和WaitListHead

  3. 互斥体特性:支持递归获取、所有权机制、遗弃检测、APC禁用控制

  4. 信号量机制:计数器控制并发,支持WaitAny/WaitAll,有上限限制

  5. 事件类型:通知事件唤醒所有等待者,同步事件唤醒一个并自动复位

  6. 等待机制:KWAIT_BLOCK关联线程与对象,KiWaitTest唤醒等待者,KiUnwaitThread解除等待

  7. 临界区优化:用户态原子操作、自旋等待、延迟创建信号量

  8. APC与等待:内核APC可打断等待,用户APC需要Alertable等待

  9. 线程调度:状态转换(Running→Waiting→Ready),KiSwapThread切换上下文

  10. 设计哲学:不同同步原语针对不同场景优化,性能与功能权衡

本章代码索引

文件 主要函数 说明
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的进程间通信机制。

复制代码
相关推荐
晓py1 小时前
Windows 本地挂载阿里云 ECS,并使用 Claude 操作挂载路径学习文档
windows·学习·阿里云
阿昭L1 小时前
Windows堆dword shoot
windows·安全·漏洞·堆溢出
love530love1 小时前
Anaconda Navigator 升级后图形界面启动失败故障修复实录
人工智能·windows·python·anaconda·navigator
LAM LAB2 小时前
【Quicker】拆分文本,按行读取文本执行循环写入
windows·quicker
许彰午13 小时前
30_Java Stream流操作全解
java·windows·python
星间都市山脉15 小时前
Android STS(Security Test Suite)完整介绍与测试流程
android·java·linux·windows·ubuntu·android studio·androidx
xiaoliuliu1234517 小时前
Sketchpad 5.0.6 几何画板安装版配置教程 Windows版:部署+桌面快捷方式创建指南
windows
惊鸿一博18 小时前
网络端口开放访问权限_Windows 11 上确保防火墙允许指定端口如3001可被访问
网络·windows
软件无线电小方圆20 小时前
Windows下基于PyQt6上位机开发环境搭建
windows