ReactOS 图形系统分析(20):引擎信号量 — EngSemaphore(semaphor.c))

ReactOS 图形系统分析(20):引擎信号量 --- EngSemaphore(semaphor.c)

1. 概述

semaphor.c 位于 win32ss/gdi/eng/semaphor.c,实现 GDI 引擎的同步原语族(HSEMAPHORE) ------EngCreateSemaphore / EngAcquireSemaphore / EngReleaseSemaphore 等一组 DDI 服务。它是引擎与显示驱动共享的互斥/共享锁接口

本质: 这里的"信号量"不是计数信号量 ,而是 ERESOURCE(内核资源对象)的轻量封装 ------支持排他(写)/共享(读)两种获取模式,并天然支持递归持有(同线程多次获取不自我死锁)。配合**关键区(Critical Region)**使用,保证锁持有期间普通内核 APC 不会打断(避免"锁被中断持有着替换")。

复制代码
┌─────────────────────────────────────────────────────────────┐
│ 使用方                                                       │
│   eng/device.c    ghsemGraphicsDeviceList(图形设备链表锁)    │
│   eng/ldevobj.c   ghsemLDEVList(逻辑设备链表锁)             │
│   eng/pdevobj.c   ghsemPDEV + ppdev->hsemDevLock(每物理设备)│
│   ntgdi/dclife.c / print.c / eng/mouse.c(hsemDevLock 使用)  │
│   reactx/dxg, ntddraw(DirectX 全局句柄表锁 ghsemHmgr 等)    │
├─────────────────────────────────────────────────────────────┤
│ gdi/eng/semaphor.c(本分析对象)                              │
│   HSEMAPHORE = PERESOURCE(ERESOURCE*,不透明句柄)            │
│   ├── EngCreateSemaphore / EngDeleteSemaphore                │
│   ├── EngAcquireSemaphore(排他)/ AcquireSemaphoreShared(共享)│
│   ├── EngReleaseSemaphore                                    │
│   ├── EngIsSemaphoreOwned / OwnedByCurrentThread             │
│   └── EngInitializeSafeSemaphore / DeleteSafeSemaphore       │
│        (ENGSAFESEMAPHORE:延迟初始化 + 引用计数的安全封装)    │
├─────────────────────────────────────────────────────────────┤
│ 底层:ntoskrnl ERESOURCE(ExInitializeResourceLite 等)+      │
│       ExEnterCriticalRegion / KeEnterCriticalRegion          │
└─────────────────────────────────────────────────────────────┘

2. 核心函数实现

2.1 EngCreateSemaphore --- 创建(分配 + 初始化 ERESOURCE)

c 复制代码
HSEMAPHORE EngCreateSemaphore(VOID)
{
    PERESOURCE psem = ExAllocatePoolWithTag(NonPagedPool,
                                            sizeof(ERESOURCE),
                                            GDITAG_SEMAPHORE);   // 非分页池!
    if (!psem) return NULL;
    if (!NT_SUCCESS(ExInitializeResourceLite(psem))) {           // 初始化资源
        ExFreePoolWithTag(psem, GDITAG_SEMAPHORE);
        return NULL;
    }
    return (HSEMAPHORE)psem;                                     // 句柄 = 资源指针
}

关键点: 分配于 NonPagedPool (锁代码路径不允许分页缺页);HSEMAPHORE 实际就是 ERESOURCE*,只是对外不透明。EngDeleteSemaphore 逆操作:ExDeleteResourceLite + 释放池。

2.2 获取 / 释放

c 复制代码
VOID EngAcquireSemaphore(HSEMAPHORE hsem)        // 排他(写锁)
{
    if (hsem == NULL) return;                    // Windows 语义:NULL 忽略
    ExEnterCriticalRegionAndAcquireResourceExclusive((PERESOURCE)hsem);
    W32Thread = PsGetThreadWin32Thread(PsGetCurrentThread());
    if (W32Thread) W32Thread->dwEngAcquireCount++;   // 引擎锁嵌套计数统计
}

VOID EngAcquireSemaphoreShared(HSEMAPHORE hsem)  // 共享(读锁)
{
    ExEnterCriticalRegionAndAcquireResourceShared((PERESOURCE)hsem);
    if (pti) ++pti->dwEngAcquireCount;
}

VOID EngReleaseSemaphore(HSEMAPHORE hsem)
{
    if (hsem == NULL) return;
    if (W32Thread) --W32Thread->dwEngAcquireCount;
    ExReleaseResourceAndLeaveCriticalRegion((PERESOURCE)hsem);
}

设计要点:

  1. Critical RegionExEnterCriticalRegion... / ...AndLeaveCriticalRegion 把"进入关键区"和"获取/释放资源"合成一步。关键区内普通 APC 被屏蔽,保证线程持锁期间状态不被异步打断。
  2. 递归安全 :ERESOURCE 允许同线程递归获取(每次 Release 减递归计数),引擎多处嵌套持有同一把 hsemDevLock 依赖此特性。
  3. 共享模式EngAcquireSemaphoreShared 让多个读者并发进入(设备表面属性读取场景),写者互斥------类似读写锁。
  4. dwEngAcquireCountwin32.h(file:///d:/reactos/win32ss/user/ntuser/win32.h#L68) 中 THREADINFO 字段):跟踪当前线程持有引擎锁的嵌套深度,为未来的死锁/锁泄漏诊断预留(当前仅计数、未消费)。

2.3 查询函数

函数 实现 语义
EngIsSemaphoreOwned ActiveCount > 0 是否被(任意线程)持有
EngIsSemaphoreOwnedByCurrentThread ExIsResourceAcquiredExclusiveLite 当前线程是否持有排他锁(递归检测用)

2.4 ENGSAFESEMAPHORE --- 安全信号量(winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h#L559-L562))

c 复制代码
typedef struct _ENGSAFESEMAPHORE {
  HSEMAPHORE  hsem;
  LONG        lCount;      // 引用计数(初次使用线程数)
} ENGSAFESEMAPHORE;
c 复制代码
BOOL EngInitializeSafeSemaphore(ENGSAFESEMAPHORE *Semaphore)
{
    if (InterlockedIncrement(&Semaphore->lCount) == 1)      // 第一个使用者
    {
        hSem = EngCreateSemaphore();                         // 才真正创建
        if (hSem == 0) { InterlockedDecrement(...); return FALSE; }
        InterlockedExchangePointer(&Semaphore->hsem, hSem);  // 发布
    }
    else
    {
        /* 其他线程等待创建者完成初始化 */
        while (Semaphore->hsem == NULL);                     // 忙等
    }
    return TRUE;
}

用途: 驱动结构体里内嵌的"可能同时被多线程首次访问"的延迟初始化锁------第一次使用时创建,后续使用只增加引用计数。EngDeleteSafeSemaphore 对称地 InterlockedDecrement,归零才删除。

已知缺陷(源码 FIXME 标注):InterlockedExchangePointer 而非 InterlockedCompareExchangePointer------若两线程同时首次进入,可能创建两个 ERESOURCE,后写的覆盖先写的(泄漏一个)。正确写法应 CAS 校验后释放失败者。这是 ReactOS 明确记录的待修复点。


3. 使用场景与全局锁清单

全局/每对象锁 创建位置 保护对象
ghsemGraphicsDeviceList device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L28) 图形设备链表(EngpRegisterGraphicsDevice 增删/枚举)
ghsemLDEVList ldevobj.c(file:///d:/reactos/win32ss/gdi/eng/ldevobj.c#L38) 逻辑设备(LDEVOBJ)链表
ghsemPDEV pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L33) 物理设备(PDEV)全局状态
ppdev->hsemDevLock pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L73) 单个物理设备:模式切换、表面、DC 关联(最常用)
ghsemShareDevLock ntddraw/dxeng.c(file:///d:/reactos/win32ss/reactx/ntddraw/dxeng.c#L182) DirectDraw 共享设备锁
ghsemHmgr dxg/ddhmg.c(file:///d:/reactos/win32ss/reactx/dxg/ddhmg.c#L57) DirectX 全局句柄管理器

典型调用链(设备锁):

复制代码
NtGdiXXX (操作设备表面)
    ↓
ntgdi/dclife.c / print.c / eng/mouse.c
    ↓ EngAcquireSemaphore(ppdev->hsemDevLock)
访问/修改 PDEV 状态(模式、表面关联、指针)
    ↓ EngReleaseSemaphore(ppdev->hsemDevLock)

4. 导出与消费路径

4.1 win32k.spec 导出(win32k.spec(file:///d:/reactos/win32ss/win32k.spec))

复制代码
@ stdcall EngAcquireSemaphore(ptr)            # L8
@ stdcall EngCreateSemaphore()                # L30
@ stdcall EngDeleteSafeSemaphore(ptr)         # L40
@ stdcall EngDeleteSemaphore(ptr)             # L41
@ stdcall EngInitializeSafeSemaphore(ptr)     # L79
@ stdcall EngIsSemaphoreOwned(ptr)            # L80
@ stdcall EngIsSemaphoreOwnedByCurrentThread(ptr) # L81
@ stdcall EngReleaseSemaphore(ptr)            # L115

(注:EngAcquireSemaphoreShared 未导出------仅供引擎内部使用,声明在 eng.h(file:///d:/reactos/win32ss/gdi/eng/eng.h#L10-L17)。)

4.2 DXGTHK 重新导出(用户态 DirectX 驱动)

dxgthk.spec(file:///d:/reactos/win32ss/reactx/dxgthk/dxgthk.spec) 把 EngAcquireSemaphore / EngCreateSemaphore / EngReleaseSemaphore / EngDeleteSemaphore 从 win32k.sys 重新导出到 dxgthk.dll------用户态 DirectX 驱动(如显示驱动 thunk)可通过 dxgthk 调用内核锁。


5. 与《分析_4》标注对照

标注 核对
《_4》第 40 行:semaphor.c =「信号量:EngAcquireSemaphore」 准确。补充:完整 8 个函数族;基于 ERESOURCE 的排他/共享"资源锁"而非计数信号量;EngAcquireSemaphoreShared 仅供内部

6. 源码索引

文件 关键内容
semaphor.c(file:///d:/reactos/win32ss/gdi/eng/semaphor.c) EngCreate/Delete/Acquire/Release/IsOwned/IsOwnedByCurrentThread、AcquireSemaphoreShared、SafeSemaphore 初始化/删除
eng.h(file:///d:/reactos/win32ss/gdi/eng/eng.h#L10-L17) EngAcquireSemaphoreShared 内部声明
winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h#L559-L562) ENGSAFESEMAPHORE 结构
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L28) ghsemGraphicsDeviceList
ldevobj.c(file:///d:/reactos/win32ss/gdi/eng/ldevobj.c#L38) ghsemLDEVList
pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L33-L95) ghsemPDEV、ppdev->hsemDevLock 创建/删除
win32.h(file:///d:/reactos/win32ss/user/ntuser/win32.h#L68) THREADINFO.dwEngAcquireCount
dxgthk.spec(file:///d:/reactos/win32ss/reactx/dxgthk/dxgthk.spec) 向用户态重新导出 Eng* 信号量

关联模块:

  • 底层机制:ntoskrnl ERESOURCE / Critical Region
  • 主要使用者:eng/device.c(file:///d:/reactos/win32ss/gdi/eng/device.c)、eng/ldevobj.c(file:///d:/reactos/win32ss/gdi/eng/ldevobj.c)、eng/pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c)、reactx/(file:///d:/reactos/win32ss/reactx/)
  • 锁保护对象:图形设备链表 / LDEV / PDEV(见《分析_4》2.3 节设备对象体系)

本文档基于 ReactOS 源代码 win32ss/gdi/eng/semaphor.c 及关联模块分析(2026 年 8 月)

相关推荐
cxr8282 小时前
HyperMind Lab M1 架构地基 Implementation Plan <二>
开发语言·人工智能·架构
灵晔君3 小时前
【Linux】进程控制(一)——进程创建、进程终止与进程等待
linux·c语言·算法
特立独行的猫a3 小时前
Tauri v2的Rust应用 → HarmonyOS(鸿蒙 PC)移植30分钟速成指南
开发语言·rust·harmonyos·tauri·移植·鸿蒙pc
迷迭香yy3 小时前
大宗交易折溢价因子怎么挖掘本地化Python全流程实战
开发语言·人工智能·python
xieliyu.3 小时前
UPD协议结构以及开发中注意事项
java·开发语言·笔记·java-ee
Yweir3 小时前
AI大模型开发-Python介绍、版本说明
开发语言·人工智能·python
峥嵘life11 小时前
Android16 311Y3 EAP-TLS 网络连接失败分析与修复总结
android·开发语言·人工智能·php
mqiqe13 小时前
AgentScope Java Harness:4. 双层记忆系统 让 Agent 拥有真正的“长期大脑“
java·开发语言
gugucoding13 小时前
46. 【Java】JUC并发工具:让并发更简单
java·开发语言
Nebula_g14 小时前
JavaSE基础语法:面向对象高级(代码中的成分)
java·开发语言·编程·javase·技术栈·高级语法