Reactos 第6章 进程间通信(续)

第6章 进程间通信(续)

本章深入剖析Windows/ReactOS中进程间通信的同步对象和通信机制。


6.4 信号量(Semaphore)

信号量是一种用于控制并发访问的同步原语,通过计数器机制限制同时访问某资源的线程数。在进程间通信中,信号量常用于资源池管理和生产者-消费者模式。

6.4.1 KSEMAPHORE结构定义

c 复制代码
typedef struct _KSEMAPHORE {
    DISPATCHER_HEADER Header;      // 调度器头部
    LONG Limit;                    // 最大计数值
} KSEMAPHORE, *PKSEMAPHORE;

结构关系:

复制代码
KSEMAPHORE结构
┌─────────────────────────────────────────────────────────────┐
│                      KSEMAPHORE                          │
│  ┌─────────────────────────────────────────────────────┐  │
│  │              DISPATCHER_HEADER                      │  │
│  │  Type          : SemaphoreObject (类型标识)        │  │
│  │  SignalState   : 当前计数 (可正可负)              │  │
│  │  WaitListHead  : 等待线程列表                    │  │
│  └─────────────────────────────────────────────────────┘  │
│  Limit            : 最大允许的计数值                    │
└─────────────────────────────────────────────────────────────┘

SignalState含义:

SignalState值 含义
> 0 可用资源数量,等待线程可立即获取
= 0 无可用资源,等待线程阻塞
< 0 无可用资源,负数表示等待线程数

6.4.2 NtCreateSemaphore系统调用

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();

    // 参数验证
    if ((MaximumCount <= 0) ||
        (InitialCount < 0) ||
        (InitialCount > MaximumCount))
    {
        return STATUS_INVALID_PARAMETER;
    }

    // 创建信号量对象
    Status = ObCreateObject(PreviousMode,
                            ExSemaphoreObjectType,
                            ObjectAttributes,
                            PreviousMode,
                            NULL,
                            sizeof(KSEMAPHORE),
                            0,
                            0,
                            (PVOID*)&Semaphore);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    // 初始化信号量
    KeInitializeSemaphore(Semaphore, InitialCount, MaximumCount);

    // 插入对象到对象管理器命名空间
    Status = ObInsertObject((PVOID)Semaphore,
                            NULL,
                            DesiredAccess,
                            0,
                            NULL,
                            &hSemaphore);

    // 返回句柄给调用者
    if (NT_SUCCESS(Status))
    {
        *SemaphoreHandle = hSemaphore;
    }

    return Status;
}

源码位置ntoskrnl/ex/sem.c#L69(file:///d:/reactos/ntoskrnl/ex/sem.c#L69)

6.4.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);

    // 获取调度器锁
    OldIrql = KiAcquireDispatcherLock();

    // 保存旧状态并计算新状态
    InitialState = Semaphore->Header.SignalState;
    State = InitialState + Adjustment;

    // 检查是否超过限制
    if ((Semaphore->Limit < State) || (InitialState > State))
    {
        KiReleaseDispatcherLock(OldIrql);
        ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED);
    }

    // 设置新状态
    Semaphore->Header.SignalState = State;

    // 如果之前无可用资源且有等待者,唤醒它们
    if (!(InitialState) && !(IsListEmpty(&Semaphore->Header.WaitListHead)))
    {
        KiWaitTest(&Semaphore->Header, Increment);
    }

    // 处理等待请求
    if (Wait == FALSE)
    {
        KiReleaseDispatcherLock(OldIrql);
    }
    else
    {
        CurrentThread = KeGetCurrentThread();
        CurrentThread->WaitNext = TRUE;
        CurrentThread->WaitIrql = OldIrql;
    }

    return InitialState;
}

源码位置ntoskrnl/ke/semphobj.c#L54(file:///d:/reactos/ntoskrnl/ke/semphobj.c#L54)

6.4.4 跨进程信号量的使用

信号量通过命名对象实现跨进程共享:

复制代码
跨进程信号量共享
┌─────────────────────────────────────────────────────────────────────┐
│                                                                   │
│   进程A创建命名信号量                                               │
│   hSemaphore = CreateSemaphore(NULL, 3, 5, "Global\\ResourcePool");│
│           │                                                       │
│           ▼                                                       │
│   对象管理器在全局命名空间创建信号量对象                            │
│                                                                   │
│   进程B打开同一信号量                                               │
│   hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE,          │
│                              "Global\\ResourcePool");              │
│           │                                                       │
│           ▼                                                       │
│   获取对同一信号量对象的引用                                       │
│                                                                   │
│   两个进程共享同一个信号量计数器                                    │
│   进程A ReleaseSemaphore(1) → 计数+1                              │
│   进程B WaitForSingleObject → 计数-1                              │
│                                                                   │
└─────────────────────────────────────────────────────────────────────┘

6.4.5 完整示例代码

c 复制代码
#include <windows.h>
#include <stdio.h>

#define MAX_CONCURRENT_THREADS 3

// 跨进程信号量示例
void CrossProcessSemaphoreExample()
{
    // 创建命名信号量
    HANDLE hSemaphore = CreateSemaphore(
        NULL,                   // 默认安全属性
        MAX_CONCURRENT_THREADS, // 初始计数
        MAX_CONCURRENT_THREADS, // 最大计数
        L"Global\\MySemaphore"  // 全局名称
    );

    if (hSemaphore == NULL)
    {
        printf("CreateSemaphore failed: %d\n", GetLastError());
        return;
    }

    // 检查是否已存在
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        printf("Semaphore already exists (other process created it)\n");
    }

    // 创建工作线程
    const int numThreads = 5;
    HANDLE hThreads[numThreads];
    
    for (int i = 0; i < numThreads; i++)
    {
        hThreads[i] = CreateThread(NULL, 0,
            [](LPVOID hSem) -> DWORD {
                HANDLE hSemaphore = (HANDLE)hSem;
                
                // 获取信号量(最多3个线程同时执行)
                printf("Thread %d waiting for semaphore...\n", 
                       GetCurrentThreadId());
                
                WaitForSingleObject(hSemaphore, INFINITE);
                
                printf("Thread %d acquired semaphore\n", 
                       GetCurrentThreadId());
                
                // 模拟工作
                Sleep(2000);
                
                printf("Thread %d releasing semaphore\n", 
                       GetCurrentThreadId());
                
                // 释放信号量
                ReleaseSemaphore(hSemaphore, 1, NULL);
                
                return 0;
            }, hSemaphore, 0, NULL);
    }

    // 等待所有线程完成
    WaitForMultipleObjects(numThreads, hThreads, TRUE, INFINITE);

    // 清理
    for (int i = 0; i < numThreads; i++)
        CloseHandle(hThreads[i]);
    CloseHandle(hSemaphore);
}

// 资源池管理示例
void ResourcePoolExample()
{
    const int POOL_SIZE = 5;
    HANDLE hSemaphore = CreateSemaphore(NULL, POOL_SIZE, POOL_SIZE, NULL);

    // 模拟资源获取
    void AcquireResource()
    {
        WaitForSingleObject(hSemaphore, INFINITE);
        // 使用资源...
    }

    void ReleaseResource()
    {
        ReleaseSemaphore(hSemaphore, 1, NULL);
    }

    CloseHandle(hSemaphore);
}

// 生产者-消费者模式示例
void ProducerConsumerWithSemaphore()
{
    const int BUFFER_SIZE = 10;
    
    // 空闲槽位信号量
    HANDLE hEmptySlots = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL);
    // 已填充槽位信号量
    HANDLE hFullSlots = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL);
    // 互斥体保护缓冲区
    HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

    // 生产者
    void Producer()
    {
        for (int i = 0; i < 20; i++)
        {
            WaitForSingleObject(hEmptySlots, INFINITE);  // 等待空闲槽位
            WaitForSingleObject(hMutex, INFINITE);
            
            // 放入数据
            printf("Produced: %d\n", i);
            
            ReleaseMutex(hMutex);
            ReleaseSemaphore(hFullSlots, 1, NULL);  // 增加已填充槽位
        }
    }

    // 消费者
    void Consumer()
    {
        for (int i = 0; i < 20; i++)
        {
            WaitForSingleObject(hFullSlots, INFINITE);  // 等待已填充槽位
            WaitForSingleObject(hMutex, INFINITE);
            
            // 取出数据
            printf("Consumed: %d\n", i);
            
            ReleaseMutex(hMutex);
            ReleaseSemaphore(hEmptySlots, 1, NULL);  // 增加空闲槽位
        }
    }

    // 创建线程
    HANDLE hProducer = CreateThread(NULL, 0, [](LPVOID) -> DWORD {
        Producer(); return 0; }, NULL, 0, NULL);
    HANDLE hConsumer = CreateThread(NULL, 0, [](LPVOID) -> DWORD {
        Consumer(); return 0; }, NULL, 0, NULL);

    WaitForSingleObject(hProducer, INFINITE);
    WaitForSingleObject(hConsumer, INFINITE);

    CloseHandle(hProducer);
    CloseHandle(hConsumer);
    CloseHandle(hEmptySlots);
    CloseHandle(hFullSlots);
    CloseHandle(hMutex);
}

6.5 互斥门(Mutant)

互斥门(Mutant)是一种支持递归获取的互斥同步原语,通过所有权机制确保同一时刻只有一个线程可以访问共享资源。

6.5.1 KMUTANT结构定义

c 复制代码
typedef struct _KMUTANT {
    DISPATCHER_HEADER Header;      // 调度器头部
    LIST_ENTRY MutantListEntry;    // 线程的互斥体列表项
    PKTHREAD OwnerThread;          // 当前所有者线程
    BOOLEAN Abandoned;             // 是否已被遗弃
    UCHAR ApcDisable;              // APC禁用计数
} KMUTANT, *PKMUTANT;

结构关系:

复制代码
KMUTANT结构
┌─────────────────────────────────────────────────────────────┐
│                      KMUTANT                              │
│  ┌─────────────────────────────────────────────────────┐  │
│  │              DISPATCHER_HEADER                      │  │
│  │  Type          : MutantObject                       │  │
│  │  SignalState   : 锁计数 (>0未锁定, 0锁定, <0递归)  │  │
│  │  WaitListHead  : 等待线程列表                       │  │
│  └─────────────────────────────────────────────────────┘  │
│  MutantListEntry  : 链入所有者线程的互斥体列表         │
│  OwnerThread     : 当前持有互斥体的线程               │
│  Abandoned       : 是否被遗弃                        │
│  ApcDisable      : APC禁用计数                      │
└─────────────────────────────────────────────────────────────┘

6.5.2 NtCreateMutant系统调用

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();

    // 创建互斥体对象
    Status = ObCreateObject(PreviousMode,
                            ExMutantObjectType,
                            ObjectAttributes,
                            PreviousMode,
                            NULL,
                            sizeof(KMUTANT),
                            0,
                            0,
                            (PVOID*)&Mutant);

    if (NT_SUCCESS(Status))
    {
        // 初始化内核互斥体
        KeInitializeMutant(Mutant, InitialOwner);

        // 插入对象到命名空间
        Status = ObInsertObject((PVOID)Mutant,
                                NULL,
                                DesiredAccess,
                                0,
                                NULL,
                                &hMutant);

        if (NT_SUCCESS(Status))
        {
            *MutantHandle = hMutant;
        }
    }

    return Status;
}

源码位置ntoskrnl/ex/mutant.c#L79(file:///d:/reactos/ntoskrnl/ex/mutant.c#L79)

6.5.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;

    // 获取调度器锁
    OldIrql = KiAcquireDispatcherLock();

    // 保存旧状态
    PreviousState = Mutant->Header.SignalState;

    if (Abandon == FALSE)
    {
        // 正常释放,检查所有权
        if (Mutant->OwnerThread != CurrentThread)
        {
            KiReleaseDispatcherLock(OldIrql);
            ExRaiseStatus(Mutant->Abandoned ? STATUS_ABANDONED :
                                              STATUS_MUTANT_NOT_OWNED);
        }

        // 增加信号状态(减少锁计数)
        Mutant->Header.SignalState++;
    }
    else
    {
        // 遗弃处理
        Mutant->Header.SignalState = 1;
        Mutant->Abandoned = TRUE;
    }

    // 如果信号状态变为1(完全释放)
    if (Mutant->Header.SignalState == 1)
    {
        // 从线程的互斥体列表移除
        if (PreviousState <= 0)
        {
            RemoveEntryList(&Mutant->MutantListEntry);
            EnableApc = Mutant->ApcDisable;
        }

        // 清除所有者
        Mutant->OwnerThread = NULL;

        // 唤醒等待者
        if (!IsListEmpty(&Mutant->Header.WaitListHead))
        {
            KiWaitTest(&Mutant->Header, Increment);
        }
    }

    // 处理等待请求
    if (Wait == FALSE)
    {
        KiReleaseDispatcherLock(OldIrql);
    }
    else
    {
        CurrentThread->WaitNext = TRUE;
        CurrentThread->WaitIrql = OldIrql;
    }

    // 重新启用APC
    if (EnableApc) KeLeaveCriticalRegion();

    return PreviousState;
}

源码位置ntoskrnl/ke/mutex.c#L98(file:///d:/reactos/ntoskrnl/ke/mutex.c#L98)

6.5.4 命名互斥体的跨进程同步

复制代码
命名互斥体跨进程同步
┌─────────────────────────────────────────────────────────────────────┐
│                                                                   │
│   场景:防止多个进程同时运行                                       │
│                                                                   │
│   进程A启动时创建命名互斥体                                        │
│   hMutex = CreateMutex(NULL, TRUE, "Global\\SingleInstance");     │
│           │                                                       │
│           ▼                                                       │
│   GetLastError() == ERROR_ALREADY_EXISTS?                         │
│       │                                                           │
│       ├─► YES: 已有实例运行,退出                                 │
│       └─► NO: 成为第一个实例                                     │
│                                                                   │
│   进程B尝试启动                                                    │
│   hMutex = CreateMutex(NULL, TRUE, "Global\\SingleInstance");     │
│           │                                                       │
│           ▼                                                       │
│   GetLastError() == ERROR_ALREADY_EXISTS → 退出                   │
│                                                                   │
│   进程A退出时释放互斥体                                            │
│   ReleaseMutex(hMutex);                                           │
│                                                                   │
└─────────────────────────────────────────────────────────────────────┘

6.5.5 完整示例代码

c 复制代码
#include <windows.h>
#include <stdio.h>

// 单实例应用程序示例
BOOL EnsureSingleInstance()
{
    // 创建命名互斥体
    HANDLE hMutex = CreateMutex(
        NULL,           // 默认安全属性
        TRUE,           // 初始拥有者
        L"Global\\MySingleInstanceApp"  // 全局名称
    );

    if (hMutex == NULL)
    {
        printf("CreateMutex failed: %d\n", GetLastError());
        return FALSE;
    }

    // 检查是否已存在
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        printf("Another instance is already running\n");
        CloseHandle(hMutex);
        return FALSE;
    }

    // 成功获取互斥体,继续运行
    printf("First instance running\n");
    
    // 程序运行期间持有互斥体
    // ...
    
    // 退出时释放
    ReleaseMutex(hMutex);
    CloseHandle(hMutex);
    
    return TRUE;
}

// 跨进程互斥示例
void CrossProcessMutexExample()
{
    // 创建命名互斥体
    HANDLE hMutex = CreateMutex(
        NULL,                   // 默认安全属性
        FALSE,                  // 初始不拥有
        L"Global\\SharedResource" // 全局名称
    );

    if (hMutex == NULL)
    {
        printf("CreateMutex failed: %d\n", GetLastError());
        return;
    }

    // 等待互斥体
    printf("Waiting for mutex...\n");
    DWORD result = WaitForSingleObject(hMutex, 5000);

    switch (result)
    {
        case WAIT_OBJECT_0:
            printf("Acquired mutex\n");
            
            // 执行临界区操作
            printf("Performing shared operation...\n");
            Sleep(3000);
            
            printf("Releasing mutex\n");
            ReleaseMutex(hMutex);
            break;
            
        case WAIT_TIMEOUT:
            printf("Timeout waiting for mutex\n");
            break;
            
        case WAIT_ABANDONED:
            printf("Mutex was abandoned by another process\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);
}

6.6 事件(Event)

事件是一种用于线程间通知的同步原语,支持通知事件和同步事件两种模式,广泛用于进程间的状态同步。

6.6.1 KEVENT结构定义

c 复制代码
typedef struct _KEVENT {
    DISPATCHER_HEADER Header;      // 调度器头部
} KEVENT, *PKEVENT;

事件是最简单的同步对象,仅包含调度器头部。

类型区分:

类型 SignalState含义 唤醒行为 适用场景
通知事件 1=有信号,0=无信号 唤醒所有等待者,保持信号状态 广播通知
同步事件 1=有信号,0=无信号 唤醒一个等待者,自动复位 资源分配

6.6.2 NtCreateEvent系统调用

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();

    // 验证事件类型
    if ((EventType != NotificationEvent) &&
        (EventType != SynchronizationEvent))
    {
        return STATUS_INVALID_PARAMETER;
    }

    // 创建事件对象
    Status = ObCreateObject(PreviousMode,
                            ExEventObjectType,
                            ObjectAttributes,
                            PreviousMode,
                            NULL,
                            sizeof(KEVENT),
                            0,
                            0,
                            (PVOID*)&Event);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    // 初始化事件
    KeInitializeEvent(Event, EventType, InitialState);

    // 插入对象
    Status = ObInsertObject((PVOID)Event,
                            NULL,
                            DesiredAccess,
                            0,
                            NULL,
                            &hEvent);

    if (NT_SUCCESS(Status))
    {
        *EventHandle = hEvent;
    }

    return Status;
}

源码位置ntoskrnl/ex/event.c#L96(file:///d:/reactos/ntoskrnl/ex/event.c#L96)

6.6.3 命名事件与跨进程同步

复制代码
命名事件跨进程同步模式
┌─────────────────────────────────────────────────────────────────────┐
│                                                                   │
│   场景:进程A完成工作后通知进程B                                   │
│                                                                   │
│   进程A创建命名事件(通知事件)                                     │
│   hEvent = CreateEvent(NULL, TRUE, FALSE, "Global\\WorkDone");    │
│                                                                   │
│   进程B打开事件并等待                                               │
│   hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE,                     │
│                      "Global\\WorkDone");                         │
│   WaitForSingleObject(hEvent, INFINITE);                          │
│                                                                   │
│   进程A完成工作后设置事件                                           │
│   SetEvent(hEvent);                                               │
│           │                                                       │
│           ▼                                                       │
│   进程B被唤醒,继续执行                                             │
│                                                                   │
│   进程B处理完成后复位事件                                           │
│   ResetEvent(hEvent);                                             │
│                                                                   │
└─────────────────────────────────────────────────────────────────────┘

6.6.4 完整示例代码

c 复制代码
#include <windows.h>
#include <stdio.h>

// 跨进程事件通知示例
void CrossProcessEventNotification()
{
    // 创建命名通知事件
    HANDLE hEvent = CreateEvent(
        NULL,           // 默认安全属性
        TRUE,           // 手动复位(通知事件)
        FALSE,          // 初始无信号
        L"Global\\WorkComplete"  // 全局名称
    );

    if (hEvent == NULL)
    {
        printf("CreateEvent failed: %d\n", GetLastError());
        return;
    }

    // 模拟工作完成
    printf("Performing work...\n");
    Sleep(3000);
    printf("Work complete, signaling event\n");

    // 通知所有等待进程
    SetEvent(hEvent);

    // 保持事件信号状态一段时间
    Sleep(5000);

    // 复位事件
    ResetEvent(hEvent);

    CloseHandle(hEvent);
}

// 同步事件示例(自动复位)
void SynchronizationEventExample()
{
    // 创建同步事件(自动复位)
    HANDLE hEvent = CreateEvent(
        NULL,           // 默认安全属性
        FALSE,          // 自动复位
        FALSE,          // 初始无信号
        NULL            // 无名事件
    );

    // 创建多个等待线程
    const int numThreads = 3;
    HANDLE hThreads[numThreads];

    for (int i = 0; i < numThreads; i++)
    {
        hThreads[i] = CreateThread(NULL, 0,
            [](LPVOID hEv) -> DWORD {
                HANDLE hEvent = (HANDLE)hEv;
                
                printf("Thread %d waiting\n", GetCurrentThreadId());
                WaitForSingleObject(hEvent, INFINITE);
                printf("Thread %d woke up\n", GetCurrentThreadId());
                
                // 事件已自动复位,其他线程继续等待
                return 0;
            }, hEvent, 0, NULL);
    }

    // 设置事件 - 只唤醒一个线程
    Sleep(100);
    printf("Setting event (wake one thread)\n");
    SetEvent(hEvent);

    // 再次设置事件
    Sleep(100);
    printf("Setting event (wake another thread)\n");
    SetEvent(hEvent);

    // 再次设置事件
    Sleep(100);
    printf("Setting event (wake third thread)\n");
    SetEvent(hEvent);

    WaitForMultipleObjects(numThreads, hThreads, TRUE, INFINITE);

    for (int i = 0; i < numThreads; i++)
        CloseHandle(hThreads[i]);
    CloseHandle(hEvent);
}

// 多个事件等待示例
void MultipleEventWaitExample()
{
    HANDLE hEvents[3];
    for (int i = 0; i < 3; i++)
        hEvents[i] = CreateEvent(NULL, TRUE, 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++)
    {
        ResetEvent(hEvents[i]);
        CloseHandle(hEvents[i]);
    }
}

// PulseEvent示例
void PulseEventExample()
{
    HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // 创建等待线程
    HANDLE hThread = CreateThread(NULL, 0,
        [](LPVOID hEv) -> DWORD {
            HANDLE hEvent = (HANDLE)hEv;
            printf("Thread waiting...\n");
            
            DWORD result = WaitForSingleObject(hEvent, 5000);
            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;
        }, hEvent, 0, NULL);

    // 等待线程开始等待
    Sleep(100);

    // PulseEvent:短暂设置为有信号然后立即复位
    printf("Pulsing event...\n");
    PulseEvent(hEvent);

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hEvent);
}

6.7 命名管道(Named Pipe)和邮件槽(Mailslot)

命名管道和邮件槽是Windows提供的两种进程间通信机制,适用于不同的通信场景。

6.7.1 命名管道概述

命名管道是一种面向连接的、可靠的字节流通信机制,支持双向通信。

管道类型:

类型 通信方向 特点
单向管道 服务器→客户端 数据流单向流动
双向管道 双向 服务器和客户端可互相发送数据
字节模式 流模式 无边界的字节流
消息模式 消息边界 每条消息保持独立

6.7.2 CreateNamedPipe实现

c 复制代码
HANDLE
WINAPI
CreateNamedPipeA(IN LPCSTR lpName,
                 IN DWORD dwOpenMode,
                 IN DWORD dwPipeMode,
                 IN DWORD nMaxInstances,
                 IN DWORD nOutBufferSize,
                 IN DWORD nInBufferSize,
                 IN DWORD nDefaultTimeOut,
                 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
    UNICODE_STRING Name;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE PipeHandle;
    IO_STATUS_BLOCK IoStatus;

    // 转换名称
    RtlInitUnicodeString(&Name, lpName);

    // 初始化对象属性
    InitializeObjectAttributes(&ObjectAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               lpSecurityAttributes ? 
                                   lpSecurityAttributes->lpSecurityDescriptor : NULL);

    // 创建管道
    Status = NtCreateNamedPipeFile(&PipeHandle,
                                   dwOpenMode,
                                   &ObjectAttributes,
                                   &IoStatus,
                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
                                   FILE_CREATE,
                                   FILE_SYNCHRONOUS_IO_NONALERT,
                                   dwPipeMode,
                                   nMaxInstances,
                                   nOutBufferSize,
                                   nInBufferSize,
                                   nDefaultTimeOut);

    if (!NT_SUCCESS(Status))
    {
        SetLastError(BaseNtStatusToWin32Error(Status));
        return INVALID_HANDLE_VALUE;
    }

    return PipeHandle;
}

关键参数:

参数 说明 常见值
lpName 管道名称 \\.\pipe\PipeName
dwOpenMode 打开模式 PIPE_ACCESS_DUPLEX(双向)
dwPipeMode 管道模式 PIPE_TYPE_BYTE/PIPE_TYPE_MESSAGE
nMaxInstances 最大实例数 PIPE_UNLIMITED_INSTANCES

6.7.3 ConnectNamedPipe与等待连接

c 复制代码
BOOL
WINAPI
ConnectNamedPipe(IN HANDLE hNamedPipe,
                 OUT LPOVERLAPPED lpOverlapped)
{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatus;

    if (lpOverlapped)
    {
        // 异步连接
        Status = NtFsControlFile(hNamedPipe,
                                 lpOverlapped->hEvent,
                                 NULL,
                                 NULL,
                                 &IoStatus,
                                 FSCTL_PIPE_LISTEN,
                                 NULL,
                                 0,
                                 NULL,
                                 0);
    }
    else
    {
        // 同步连接
        Status = NtFsControlFile(hNamedPipe,
                                 NULL,
                                 NULL,
                                 NULL,
                                 &IoStatus,
                                 FSCTL_PIPE_LISTEN,
                                 NULL,
                                 0,
                                 NULL,
                                 0);
    }

    if (!NT_SUCCESS(Status))
    {
        SetLastError(BaseNtStatusToWin32Error(Status));
        return FALSE;
    }

    return TRUE;
}

源码位置dll/win32/kernel32/client/file/npipe.c(file:///d:/reactos/dll/win32/kernel32/client/file/npipe.c)

6.7.4 邮件槽概述

邮件槽是一种单向、不可靠的数据报通信机制,支持一对多的广播通信。

邮件槽特点:

  • 单向通信:服务器创建,客户端发送
  • 广播能力:支持域内广播
  • 不可靠:消息可能丢失
  • 大小限制:最大消息大小由服务器设置

6.7.5 CreateMailslot实现

c 复制代码
HANDLE
WINAPI
CreateMailslotW(IN LPCWSTR lpName,
                IN DWORD nMaxMessageSize,
                IN DWORD lReadTimeout,
                IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING MailslotName;
    HANDLE MailslotHandle;
    NTSTATUS Status;
    LARGE_INTEGER DefaultTimeOut;
    IO_STATUS_BLOCK Iosb;

    // 转换名称
    RtlDosPathNameToNtPathName_U(lpName, &MailslotName, NULL, NULL);

    // 初始化对象属性
    InitializeObjectAttributes(&ObjectAttributes,
                               &MailslotName,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               lpSecurityAttributes ? 
                                   lpSecurityAttributes->lpSecurityDescriptor : NULL);

    // 设置超时
    if (lReadTimeout == MAILSLOT_WAIT_FOREVER)
    {
        DefaultTimeOut.QuadPart = 0xFFFFFFFFFFFFFFFFLL;
    }
    else
    {
        DefaultTimeOut.QuadPart = lReadTimeout * -10000LL;
    }

    // 创建邮件槽
    Status = NtCreateMailslotFile(&MailslotHandle,
                                  GENERIC_READ | SYNCHRONIZE | WRITE_DAC,
                                  &ObjectAttributes,
                                  &Iosb,
                                  FILE_WRITE_THROUGH,
                                  0,
                                  nMaxMessageSize,
                                  &DefaultTimeOut);

    RtlFreeHeap(RtlGetProcessHeap(), 0, MailslotName.Buffer);

    if (!NT_SUCCESS(Status))
    {
        SetLastError(BaseNtStatusToWin32Error(Status));
        return INVALID_HANDLE_VALUE;
    }

    return MailslotHandle;
}

源码位置dll/win32/kernel32/client/file/mailslot.c(file:///d:/reactos/dll/win32/kernel32/client/file/mailslot.c)

6.7.6 命名管道与邮件槽对比

特性 命名管道 邮件槽
通信方向 双向 单向(客户端→服务器)
可靠性 可靠(面向连接) 不可靠(数据报)
消息边界 支持字节流/消息模式 消息边界保留
广播能力 不支持 支持域内广播
最大消息大小 无限制(内存限制) 由服务器指定
连接模式 面向连接 无连接
适用场景 进程间通信、RPC 通知广播、状态更新

6.7.7 完整示例代码

c 复制代码
#include <windows.h>
#include <stdio.h>

// 命名管道服务器示例
void NamedPipeServer()
{
    HANDLE hPipe;
    char buffer[1024];
    DWORD bytesRead;

    // 创建命名管道
    hPipe = CreateNamedPipe(
        L"\\\\.\\pipe\\MyPipe",
        PIPE_ACCESS_DUPLEX,           // 双向访问
        PIPE_TYPE_MESSAGE |           // 消息模式
        PIPE_READMODE_MESSAGE |
        PIPE_WAIT,                    // 阻塞模式
        PIPE_UNLIMITED_INSTANCES,     // 无限实例
        1024,                         // 输出缓冲区
        1024,                         // 输入缓冲区
        5000,                         // 默认超时
        NULL);                         // 默认安全属性

    if (hPipe == INVALID_HANDLE_VALUE)
    {
        printf("CreateNamedPipe failed: %d\n", GetLastError());
        return;
    }

    printf("Pipe created, waiting for client...\n");

    // 等待客户端连接
    if (!ConnectNamedPipe(hPipe, NULL))
    {
        printf("ConnectNamedPipe failed: %d\n", GetLastError());
        CloseHandle(hPipe);
        return;
    }

    printf("Client connected\n");

    // 读取客户端消息
    if (!ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, NULL))
    {
        printf("ReadFile failed: %d\n", GetLastError());
        CloseHandle(hPipe);
        return;
    }

    printf("Received: %s\n", buffer);

    // 发送响应
    const char* response = "Hello from server!";
    DWORD bytesWritten;
    WriteFile(hPipe, response, strlen(response) + 1, &bytesWritten, NULL);

    // 断开连接
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);
}

// 命名管道客户端示例
void NamedPipeClient()
{
    HANDLE hPipe;
    char buffer[1024];
    DWORD bytesRead;

    // 连接到命名管道
    while (TRUE)
    {
        hPipe = CreateFile(
            L"\\\\.\\pipe\\MyPipe",
            GENERIC_READ | GENERIC_WRITE,
            0,
            NULL,
            OPEN_EXISTING,
            0,
            NULL);

        if (hPipe != INVALID_HANDLE_VALUE)
            break;

        if (GetLastError() != ERROR_PIPE_BUSY)
        {
            printf("CreateFile failed: %d\n", GetLastError());
            return;
        }

        // 等待管道可用
        WaitNamedPipe(L"\\\\.\\pipe\\MyPipe", NMPWAIT_WAIT_FOREVER);
    }

    printf("Connected to server\n");

    // 发送消息
    const char* message = "Hello from client!";
    DWORD bytesWritten;
    WriteFile(hPipe, message, strlen(message) + 1, &bytesWritten, NULL);

    // 读取响应
    ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, NULL);
    printf("Server response: %s\n", buffer);

    CloseHandle(hPipe);
}

// 邮件槽服务器示例
void MailslotServer()
{
    HANDLE hMailslot;
    char buffer[1024];
    DWORD bytesRead;
    DWORD messageSize;

    // 创建邮件槽
    hMailslot = CreateMailslot(
        L"\\\\.\\mailslot\\MyMailslot",
        0,                           // 无最大消息大小限制
        MAILSLOT_WAIT_FOREVER,       // 无限等待
        NULL);                        // 默认安全属性

    if (hMailslot == INVALID_HANDLE_VALUE)
    {
        printf("CreateMailslot failed: %d\n", GetLastError());
        return;
    }

    printf("Mailslot created, waiting for messages...\n");

    // 读取消息
    while (TRUE)
    {
        if (!ReadFile(hMailslot, buffer, sizeof(buffer), &bytesRead, NULL))
        {
            printf("ReadFile failed: %d\n", GetLastError());
            break;
        }

        printf("Received %d bytes: %s\n", bytesRead, buffer);
    }

    CloseHandle(hMailslot);
}

// 邮件槽客户端示例
void MailslotClient()
{
    HANDLE hMailslot;
    DWORD bytesWritten;

    // 打开邮件槽
    hMailslot = CreateFile(
        L"\\\\.\\mailslot\\MyMailslot",
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (hMailslot == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile failed: %d\n", GetLastError());
        return;
    }

    // 发送消息
    const char* message = "Hello from mailslot client!";
    WriteFile(hMailslot, message, strlen(message) + 1, &bytesWritten, NULL);

    printf("Sent %d bytes\n", bytesWritten);

    CloseHandle(hMailslot);
}

// 邮件槽广播示例
void MailslotBroadcast()
{
    HANDLE hMailslot;
    DWORD bytesWritten;

    // 使用域广播地址
    hMailslot = CreateFile(
        L"\\\\*\\mailslot\\MyBroadcastSlot",
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (hMailslot == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile failed: %d\n", GetLastError());
        return;
    }

    // 广播消息
    const char* message = "Broadcast message to all servers!";
    WriteFile(hMailslot, message, strlen(message) + 1, &bytesWritten, NULL);

    printf("Broadcast sent\n");

    CloseHandle(hMailslot);
}

总结

核心要点回顾

  1. 信号量(Semaphore):计数器机制控制并发访问,支持跨进程共享,适用于资源池管理

  2. 互斥门(Mutant):递归互斥体,支持所有权机制和遗弃检测,适用于单实例控制

  3. 事件(Event):通知机制,支持通知事件(广播)和同步事件(自动复位)

  4. 命名管道(Named Pipe):面向连接的双向字节流通信,可靠传输

  5. 邮件槽(Mailslot):无连接的单向数据报通信,支持广播

本章代码索引

文件 主要函数 说明
ntoskrnl/ex/sem.c(file:///d:/reactos/ntoskrnl/ex/sem.c) NtCreateSemaphore, NtReleaseSemaphore 信号量系统调用
ntoskrnl/ke/semphobj.c(file:///d:/reactos/ntoskrnl/ke/semphobj.c) KeInitializeSemaphore, KeReleaseSemaphore 信号量内核实现
ntoskrnl/ex/mutant.c(file:///d:/reactos/ntoskrnl/ex/mutant.c) NtCreateMutant, NtReleaseMutant 互斥门系统调用
ntoskrnl/ke/mutex.c(file:///d:/reactos/ntoskrnl/ke/mutex.c) KeInitializeMutant, KeReleaseMutant 互斥门内核实现
ntoskrnl/ex/event.c(file:///d:/reactos/ntoskrnl/ex/event.c) NtCreateEvent, NtSetEvent 事件系统调用
ntoskrnl/ke/eventobj.c(file:///d:/reactos/ntoskrnl/ke/eventobj.c) KeInitializeEvent, KeSetEvent 事件内核实现
dll/win32/kernel32/client/file/npipe.c(file:///d:/reactos/dll/win32/kernel32/client/file/npipe.c) CreateNamedPipe, ConnectNamedPipe 命名管道API
dll/win32/kernel32/client/file/mailslot.c(file:///d:/reactos/dll/win32/kernel32/client/file/mailslot.c) CreateMailslot, GetMailslotInfo 邮件槽API

本节完。

相关推荐
触底反弹1 小时前
拷个 .exe 到新电脑就跑不起来?你缺的不是文件,是对链接的理解
c++·windows·操作系统
W优化大师2 小时前
Windows 更新待处理弹窗一直不消失怎么解决,C 盘空间和后台任务该如何排查
windows·系统优化·磁盘清理·windows11·c盘·系统更新
无风听海2 小时前
在 ASP.NET Core 开发环境中为自定义域名签发受信任的自签名证书—HSTS 启用后的完整实践
windows·后端·asp.net
Ztopcloud极拓云视角3 小时前
我用AI辅助做了一个多端工具:解决2026世界杯回放被剧透的问题
人工智能·windows·个人开发
love530love3 小时前
2026年终极防坑指南:基于 EPGF 架构彻底“本地化” UV 环境与工具
人工智能·windows·python·架构·devops·uv·epgf
虾壳云官方3 小时前
【本地 AI 自动化最新工具】 OpenClaw 2.7.9 Windows 完整部署教程(包含安装包)
人工智能·windows·openclaw·openclaw安装·openclaw一键部署
lzjava20243 小时前
Python的数据结构,推导式、迭代器和生成器
数据结构·windows·python
接着奏乐接着舞4 小时前
springboot mp mybatis plaus
windows·spring boot·mybatis
程序员佳佳4 小时前
四个月长期实测:自建 Milvus、FAISS、原生向量 API 和向量引擎中转方案,到底怎么选?
人工智能·windows·python·gpt·milvus·faiss