当前位置: 首页 > news >正文

Windows:异常安全的内核对象

在维护项目时,遇到了关于多个进程使用同一个内核对象,但是由于某个进程崩溃或者出现了某些异常情况,导致信号未能正确释放形成死锁,故在这里记录一下关于内核对象的知识以及异常安全做法

Windows 内核对象概述

Windows 内核对象是由内核层管理的系统资源,用于实现进程、线程、同步、文件操作等核心功能。常见的内核对象包括:

  • 同步对象:事件(Event)、互斥体(Mutex)、信号量(Semaphore)
  • 句柄对象:文件(File)、管道(Pipe)、线程(Thread)、进程(Process)
  • 其他对象:定时器(Timer)、内存映射文件(Memory-Mapped File)等
核心特性:
  1. 句柄访问:用户模式通过句柄(HANDLE)操作内核对象,句柄是进程私有的索引。
  2. 生命周期:内核对象的销毁由引用计数决定,最后一个句柄关闭时释放资源(CloseHandle)。
  3. 跨进程共享:命名的内核对象可通过名称在不同进程间共享(如命名互斥体)。

示例代码:同步内核对象的使用

以下示例演示 事件(Event)互斥体(Mutex)信号量(Semaphore) 的典型用法,使用 C 语言和 Windows API。

1. 事件对象(Event)

用于线程间的同步通信,通过触发(Signaled)和非触发(Nonsignaled)状态控制线程执行。

#include <windows.h>
#include <stdio.h>#define MAX_THREADS 2// 线程函数:等待事件触发
DWORD WINAPI ThreadFunc(LPVOID lpParam) {HANDLE hEvent = (HANDLE)lpParam;printf("线程 %d 等待事件...\n", GetCurrentThreadId());WaitForSingleObject(hEvent, INFINITE); // 阻塞直到事件触发printf("线程 %d 接收到事件,继续执行!\n", GetCurrentThreadId());return 0;
}int main() {HANDLE hEvent = CreateEvent(NULL,                  // 默认安全属性FALSE,                 // 自动重置事件(触发后自动回到非触发状态)FALSE,                 // 初始状态为非触发NULL                   // 未命名事件);HANDLE hThreads[MAX_THREADS];for (int i = 0; i < MAX_THREADS; i++) {hThreads[i] = CreateThread(NULL, 0, ThreadFunc, hEvent, 0, NULL);}Sleep(1000); // 等待线程启动printf("主线程触发事件...\n");SetEvent(hEvent); // 触发事件,唤醒所有等待线程(自动重置事件仅唤醒一个线程)WaitForMultipleObjects(MAX_THREADS, hThreads, TRUE, INFINITE);CloseHandle(hEvent);for (int i = 0; i < MAX_THREADS; i++) CloseHandle(hThreads[i]);return 0;
}
2. 互斥体(Mutex)

确保多个线程/进程对临界区的互斥访问(同一时刻只有一个持有者)。

#include <windows.h>
#include <stdio.h>#define COUNT 5
HANDLE hMutex;// 线程函数:访问临界区
DWORD WINAPI ThreadFunc(LPVOID lpParam) {for (int i = 0; i < COUNT; i++) {WaitForSingleObject(hMutex, INFINITE); // 等待互斥体printf("线程 %d 进入临界区,计数:%d\n", GetCurrentThreadId(), i);Sleep(100); // 模拟临界区操作ReleaseMutex(hMutex); // 释放互斥体}return 0;
}int main() {hMutex = CreateMutex(NULL,                // 默认安全属性FALSE,               // 初始不拥有互斥体NULL                 // 未命名互斥体);HANDLE hThreads[2];for (int i = 0; i < 2; i++) {hThreads[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);}WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);CloseHandle(hMutex);for (int i = 0; i < 2; i++) CloseHandle(hThreads[i]);return 0;
}
3. 信号量(Semaphore)

控制同时访问资源的线程数量(通过初始计数和最大计数限制)。

#include <windows.h>
#include <stdio.h>#define MAX_RESOURCES 3 // 最多3个线程同时访问
HANDLE hSemaphore;// 线程函数:申请信号量
DWORD WINAPI ThreadFunc(LPVOID lpParam) {DWORD id = (DWORD)lpParam;WaitForSingleObject(hSemaphore, INFINITE); // 申请信号量printf("线程 %d 获得资源,当前计数:%d\n", id, id);Sleep(500); // 模拟资源占用ReleaseSemaphore(hSemaphore, 1, NULL); // 释放信号量(计数+1)return 0;
}int main() {hSemaphore = CreateSemaphore(NULL,                // 默认安全属性MAX_RESOURCES,       // 初始可用计数MAX_RESOURCES,       // 最大计数NULL                 // 未命名信号量);HANDLE hThreads[5]; // 5个线程竞争3个资源for (DWORD i = 0; i < 5; i++) {hThreads[i] = CreateThread(NULL, 0, ThreadFunc, (LPVOID)i, 0, NULL);}WaitForMultipleObjects(5, hThreads, TRUE, INFINITE);CloseHandle(hSemaphore);for (DWORD i = 0; i < 5; i++) CloseHandle(hThreads[i]);return 0;
}

关键 API 说明

函数用途
CreateEvent创建事件对象
CreateMutex创建互斥体对象
CreateSemaphore创建信号量对象
WaitForSingleObject等待单个内核对象变为触发状态
WaitForMultipleObjects等待多个内核对象中的任意/全部触发
SetEvent/PulseEvent触发事件(手动重置事件需 PulseEvent
ReleaseMutex释放互斥体(仅持有者可调用)
ReleaseSemaphore释放信号量(增加计数)
CloseHandle关闭句柄(减少内核对象引用计数)

注意事项

  1. 命名对象:跨进程共享时需指定唯一名称(如 CreateMutex(NULL, FALSE, L"Global\\MyMutex"))。 常用于进程间通信
  2. 资源泄漏:必须调用 CloseHandle 释放句柄,避免内核对象无法销毁。
  3. 同步机制选择
    • 事件:适合线程间的通知(如“数据准备完成”)。
    • 互斥体:适合互斥访问(需保证持有者释放)。
    • 信号量:适合限制并发数量(如连接池、线程池)。

异常安全的内核对象管理

在 Windows 系统编程中,使用内核对象时结合异常安全进行管理是非常重要的,这可以确保在程序出现异常时,内核对象能被正确释放,避免资源泄漏。以下是使用 C++ 和 RAII(资源获取即初始化)技术对内核对象进行异常安全管理的示例代码。

示例代码
  1. KernelObjectManager 类模板:这是一个通用的内核对象管理类,使用 RAII 技术确保内核对象在其生命周期结束时被正确释放。通过模板参数 HandleTraits 可以为不同类型的内核对象定制管理行为。
  2. 特性类EventTraitsMutexTraitsSemaphoreTraits 分别定义了事件、互斥体和信号量对象的特性,包括无效值和关闭操作。
  3. 类型别名EventManagerMutexManagerSemaphoreManagerKernelObjectManager 的具体实例化类型,方便使用。
  4. 示例函数eventExamplemutexExamplesemaphoreExample 分别演示了事件、互斥体和信号量对象的创建、使用和释放过程,并且在出现错误时抛出异常。
  5. main 函数:调用示例函数,并捕获可能抛出的异常,确保程序在异常情况下能正常处理。

通过这种方式,即使在程序执行过程中出现异常,内核对象也能在 KernelObjectManager 的析构函数中被正确释放,避免了资源泄漏。

#include <windows.h>
#include <iostream>
#include <stdexcept>// 内核对象管理类模板
template <typename HandleTraits>
class KernelObjectManager {
public:using HandleType = typename HandleTraits::HandleType;// 构造函数,获取内核对象句柄KernelObjectManager(HandleType handle = HandleTraits::InvalidValue()) : m_handle(handle) {}// 析构函数,释放内核对象句柄~KernelObjectManager() {if (m_handle != HandleTraits::InvalidValue()) {HandleTraits::Close(m_handle);}}// 禁止拷贝构造函数KernelObjectManager(const KernelObjectManager&) = delete;// 禁止赋值运算符KernelObjectManager& operator=(const KernelObjectManager&) = delete;// 移动构造函数KernelObjectManager(KernelObjectManager&& other) noexcept : m_handle(other.m_handle) {other.m_handle = HandleTraits::InvalidValue();}// 移动赋值运算符KernelObjectManager& operator=(KernelObjectManager&& other) noexcept {if (this != &other) {if (m_handle != HandleTraits::InvalidValue()) {HandleTraits::Close(m_handle);}m_handle = other.m_handle;other.m_handle = HandleTraits::InvalidValue();}return *this;}// 获取内核对象句柄HandleType get() const {return m_handle;}// 释放内核对象句柄HandleType release() {HandleType temp = m_handle;m_handle = HandleTraits::InvalidValue();return temp;}private:HandleType m_handle;
};// 事件对象特性类
struct EventTraits {using HandleType = HANDLE;static constexpr HandleType InvalidValue() { return nullptr; }static void Close(HandleType handle) {if (!CloseHandle(handle)) {std::cerr << "Failed to close event handle: " << GetLastError() << std::endl;}}
};// 互斥体对象特性类
struct MutexTraits {using HandleType = HANDLE;static constexpr HandleType InvalidValue() { return nullptr; }static void Close(HandleType handle) {if (!CloseHandle(handle)) {std::cerr << "Failed to close mutex handle: " << GetLastError() << std::endl;}}
};// 信号量对象特性类
struct SemaphoreTraits {using HandleType = HANDLE;static constexpr HandleType InvalidValue() { return nullptr; }static void Close(HandleType handle) {if (!CloseHandle(handle)) {std::cerr << "Failed to close semaphore handle: " << GetLastError() << std::endl;}}
};// 事件对象管理类型别名
using EventManager = KernelObjectManager<EventTraits>;
// 互斥体对象管理类型别名
using MutexManager = KernelObjectManager<MutexTraits>;
// 信号量对象管理类型别名
using SemaphoreManager = KernelObjectManager<SemaphoreTraits>;// 示例函数,演示事件对象的使用
void eventExample() {// 创建事件对象EventManager event(CreateEvent(nullptr, FALSE, FALSE, nullptr));if (event.get() == nullptr) {throw std::runtime_error("Failed to create event object");}// 模拟一些操作std::cout << "Event object created successfully." << std::endl;// 触发事件if (!SetEvent(event.get())) {throw std::runtime_error("Failed to set event");}std::cout << "Event triggered." << std::endl;
}// 示例函数,演示互斥体对象的使用
void mutexExample() {// 创建互斥体对象MutexManager mutex(CreateMutex(nullptr, FALSE, nullptr));if (mutex.get() == nullptr) {throw std::runtime_error("Failed to create mutex object");}// 模拟一些操作std::cout << "Mutex object created successfully." << std::endl;// 等待互斥体DWORD result = WaitForSingleObject(mutex.get(), INFINITE);if (result != WAIT_OBJECT_0) {throw std::runtime_error("Failed to wait for mutex");}std::cout << "Mutex acquired." << std::endl;// 释放互斥体if (!ReleaseMutex(mutex.get())) {throw std::runtime_error("Failed to release mutex");}std::cout << "Mutex released." << std::endl;
}// 示例函数,演示信号量对象的使用
void semaphoreExample() {// 创建信号量对象SemaphoreManager semaphore(CreateSemaphore(nullptr, 1, 1, nullptr));if (semaphore.get() == nullptr) {throw std::runtime_error("Failed to create semaphore object");}// 模拟一些操作std::cout << "Semaphore object created successfully." << std::endl;// 等待信号量DWORD result = WaitForSingleObject(semaphore.get(), INFINITE);if (result != WAIT_OBJECT_0) {throw std::runtime_error("Failed to wait for semaphore");}std::cout << "Semaphore acquired." << std::endl;// 释放信号量if (!ReleaseSemaphore(semaphore.get(), 1, nullptr)) {throw std::runtime_error("Failed to release semaphore");}std::cout << "Semaphore released." << std::endl;
}int main() {try {eventExample();mutexExample();semaphoreExample();} catch (const std::exception& e) {std::cerr << "Exception caught: " << e.what() << std::endl;}return 0;
}    
http://www.xdnf.cn/news/84817.html

相关文章:

  • 如何使用压缩文件便捷地管理远程工作文件?
  • 子网划分的学习
  • 深入探索RAG:用LlamaIndex为大语言模型扩展知识,实现智能检索增强生成
  • Linux:线程基础(虚拟地址,分页)
  • 实现鼠标拖拽图片效果
  • 驱动开发硬核特训 · Day 17:深入掌握中断机制与驱动开发中的应用实战
  • 或者某些 M 理论、Loop Quantum Gravity 的空背景设想
  • 【Java面试笔记:基础】8.对比Vector、ArrayList、LinkedList有何区别?
  • L2-1、打造稳定可控的 AI 输出 —— Prompt 模板与格式控制
  • 局域网内,将linux(Ubuntu)的硬盘映射成Windows上,像本地磁盘一样使用
  • Lua 第8部分 补充知识
  • ProxySQL 读写分离规则配置指南
  • exception:com.alibaba.nacos.api.exception.NacosException: user not found! 解决方法
  • 解决Python与Java交互乱码问题:从编码角度优化数据流
  • 云原生 - Service Mesh
  • 【Linux运维涉及的基础命令与排查方法大全】
  • 位运算练习:起床困难综合征(贪心,位运算)(算法竞赛进阶指南学习笔记)
  • 2025-04-22| Docker: --privileged参数详解
  • 【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
  • 黑阈免激活版:智能管理后台,优化手机性能
  • vscode flutter 插件, vscode运行安卓项目,.gradle 路径配置
  • 刷刷刷刷刷sql题
  • Oracle在ERP市场击败SAP
  • JVM考古现场(二十四):逆熵者·时间晶体的永恒之战
  • PHP通讯录网站源码无需sql数据库
  • Vue2-指令语法
  • 直播分享|TinyVue 多端实战与轻量图标库分享
  • Linux嵌入式系统SQlite3数据库学习笔记
  • 实验一 进程控制实验
  • 《构建通用学习体系:从底层逻辑到场景应用》