windows驱动开发-内核调度(二)

这篇文档记录剩下的内核调度对象。

信号灯

任何驱动程序都可以使用信号量对象在其驱动程序创建的线程和其他驱动程序例程之间同步操作。 例如,当驱动程序没有未完成的 I/O 请求时,驱动程序专用线程可能会将自身置于等待状态,并且驱动程序的调度例程可能会在将 IRP 排入队列后将信号量设置为“已信号”状态。

在请求 I/O 操作的线程上下文中运行的最高级别驱动程序的调度例程可能使用信号灯来保护调度例程之间共享的资源。 用于同步 I/O 操作的较低级别的驱动程序调度例程也可能使用信号灯来保护该调度例程子集之间共享的资源或与驱动程序创建的线程共享的资源。

任何使用信号灯对象的驱动程序都必须在等待或释放信号量之前调用 KeInitializeSemaphore 。 下图演示了具有线程的驱动程序如何使用信号灯对象:

如上图所示,此类驱动程序必须为信号灯对象提供存储,该信号量应驻留。 驱动程序可以使用驱动程序创建 的设备对象的设备扩展 、控制器扩展或驱动程序分配的非分页池。

当驱动程序的 AddDevice 例程调用 KeInitializeSemaphore 时,它必须将指针传递给驱动程序的信号灯对象的常驻存储。 此外,调用方必须为信号量对象指定 Count ,如上图所示,该对象确定信号量) 的初始状态 非零。

调用方还必须指定信号灯 的 Limit ,可以是以下任一项:

  • Limit = 1:当此信号灯设置为“已信号”状态时,等待信号灯设置为“已信号”状态的单个线程将有资格执行,并且可以访问信号量保护的任何资源。这种类型的信号灯也称为 二进制信号灯 ,因为线程对信号灯保护的资源具有或没有独占访问权限。
  • Limit > 1: 当此信号灯设置为“已信号”状态时,等待信号量对象设置为“已信号”状态的一些线程将有资格执行,并且可以访问信号量保护的任何资源。这种类型的信号量称为 计数信号灯 ,因为将信号量设置为“已信号”状态的例程还指定有多少等待线程可以将其状态从等待更改为“就绪”。 此类等待线程的数量可以是初始化信号灯时设置的 “限制 ”,也可以是低于此预设 限制的某个数量。

很少有设备或中间驱动程序具有单个驱动程序创建的线程;有一组线程可能等待信号灯被获取或释放,甚至更少。 系统提供的驱动程序很少使用信号灯对象,而在这些驱动程序中,使用二元信号灯的驱动程序甚至更少。 尽管二进制信号灯在功能上可能类似于互斥对象,但二进制信号灯不提供针对 SMP 计算机中运行的系统线程的互斥对象具有的死锁的内置保护。

加载具有已初始化信号灯的驱动程序后,它可以同步保护共享资源的信号量上的操作。 例如,具有管理 IRP 排队的设备专用线程的驱动程序(例如系统软盘控制器驱动程序)可能会在信号灯上同步 IRP 队列,如上图所示:

  • 1. 线程调用 KeWaitForSingleObject ,其中包含指向初始化信号灯对象的驱动程序提供的存储的指针,使自身处于等待状态;
  • 2. 需要设备 I/O 操作的 IRP 开始出现。 驱动程序的调度例程将每个此类 IRP 插入到旋转锁定控制下的互锁队列中,并使用指向信号量对象的指针调用 KeReleaseSemaphore,这是驱动程序确定的线程优先级提升 (增量,如上图) 所示,调整为 1,在每个 IRP 排队时添加到信号灯的 Count 中, 并将布尔等待设置为 FALSE。 非零信号灯计数将信号量对象设置为“已信号”状态,从而将等待线程的状态更改为“就绪”;
  • 3. 当处理器可用时,内核会立即调度线程以执行:也就是说,当前没有其他具有更高优先级的线程处于就绪状态,并且没有内核模式例程可在更高的 IRQL 上运行;
  • 线程在旋转锁定控制下从互锁队列中删除 IRP,将其传递给其他驱动程序例程以供进一步处理,并再次调用 KeWaitForSingleObject 。 如果信号灯仍设置为信号状态 (即,其计数将保持非零值,表示驱动程序的联锁队列) 中存在更多 IRP,内核再次将线程的状态从“等待”更改为“就绪”;
  • 通过以这种方式使用计数信号量,此类驱动程序线程“知道”每当该线程运行时,都会从互锁队列中删除 IRP;

在大于 PASSIVE_LEVEL 的 IRQL 上运行的任何标准驱动程序例程都不能等待任何调度程序对象的非零间隔,而不会关闭系统,但是此类例程可以在 IRQL 小于或等于 DISPATCH_LEVEL 的情况下调用 KeReleaseSemaphore 。

互斥体

顾名思义,互斥对象是一种同步机制,旨在确保对一组内核模式线程之间共享的单个资源进行互斥访问。 只有使用执行工作线程的高级驱动程序,例如文件系统驱动程序 (FSD) 才可能使用互斥对象。

可能具有驱动程序创建的线程或工作线程回调例程的最高级别驱动程序也会使用互斥对象。 但是,任何具有可分页线程或工作线程回调例程的驱动程序都必须非常小心地管理其互斥对象的获取、等待和发布。

互斥对象具有内置功能,仅线程可以互斥、无死锁方式访问 SMP 计算机中的共享资源,互斥对象提供系统内核模式,内核一次将互斥体的所有权分配给单个线程。

获取互斥体的所有权会阻止 (APC) 传递正常的内核模式异步过程调用。 除非内核发出APC_LEVEL软件中断以运行特殊内核 APC(例如 I/O 管理器的 IRP 完成例程,该例程将结果返回到 I/O 操作的原始请求者)时,线程才不会被 APC 抢占。

线程可以获取它已拥有的互斥对象 (递归所有权) ,但在线程完全释放其所有权之前,递归获取的互斥对象不会设置为 Signaled 状态。 此类线程必须在获得所有权后多次显式释放互斥体,然后另一个线程才能获取互斥体。

内核绝不允许拥有互斥锁的线程在不首先释放互斥体并将其设置为“已信号”状态的情况下转换到用户模式。 如果拥有互斥体的任何 FSD 创建或驱动程序创建的线程在释放互斥体的所有权之前尝试将控制权返回到 I/O 管理器,则内核会关闭系统。

任何使用互斥对象的驱动程序都必须调用 KeInitializeMutex 一次,然后才能等待或释放其互斥对象。 下图演示了两个系统线程如何使用互斥体对象:

如上图所示,使用互斥对象的驱动程序必须为互斥对象提供存储,该互斥对象必须是驻留的。 驱动程序可以使用驱动程序创建 的设备对象的设备扩展 、控制器扩展或驱动程序分配的非分页池。

当驱动程序调用 KeInitializeMutex,通常从其 AddDevice 例程 时,它必须将指向驱动程序存储的指针传递给互斥对象,内核会将其初始化为 Signaled 状态。

在这样一个最高级别的驱动程序初始化后,它可以管理对共享资源的互斥访问,如上图所示。 例如,用于固有同步操作和线程的驱动程序调度例程可能会使用互斥体来保护驱动程序创建的 IRP 队列。

由于 KeInitializeMutex始终将互斥对象的初始状态设置为 Signaled (如上图所示) :

  1. 调度例程使用 Mutex 指针对 KeWaitForSingleObject 进行的初始调用会将当前线程立即进入就绪状态,授予互斥锁的线程所有权,并将互斥状态重置为 Not-Signaled。 调度例程恢复运行后,就可以安全地将 IRP 插入互斥保护的队列中;
  2. 当第二个线程 (另一个调度例程、驱动程序提供的工作线程回调例程或驱动程序创建的线程) 使用 Mutex 指针调用 KeWaitForSingleObject 时,第二个线程将进入等待状态;
  3. 当调度例程按照步骤 1 中所述完成对 IRP 的排队时,它会使用 Mutex 指针和布尔等待值调用 KeReleaseMutex,该值指示它打算在 KeReleaseMutex 返回控件后立即调用 KeWaitForSingleObject (还是 KeWaitForMutexObject) ;
  4. 假设调度例程在步骤 3 中释放了互斥体的所有权, Wait 设置为 FALSE),则 KeReleaseMutex 将互斥体设置为 Signaled 状态。 互斥体当前没有所有者,因此内核确定另一个线程是否正在等待该互斥体。 如果是这样,内核会使第二个线程 () 互斥体所有者看到步骤 2,可能会将线程的优先级提升到最低实时优先级值,并将其状态更改为就绪;
  5. 当处理器可用时,内核会立即调度第二个线程以供执行:也就是说,当前没有其他具有更高优先级的线程处于就绪状态,并且没有内核模式例程可在更高的 IRQL 上运行。 第二个线程 (调度例程排队 IRP 或驱动程序的工作线程回调例程或驱动程序创建的线程取消排队 IRP) 现在可以安全地访问受互斥保护的 IRP 队列,直到它调用 KeReleaseMutex;

如果线程以递归方式获取互斥对象的所有权,则该线程必须显式调用 KeReleaseMutex 的次数与它在互斥上等待的次数一样多,以便将互斥对象设置为 Signaled 状态。 例如,如果线程使用同一 Mutex 指针调用 KeWaitForSingleObject,然后调用 KeWaitForMutexObject,则在获取 mutex 时,它必须调用 KeReleaseMutex 两次,以便将互斥对象设置为 Signaled 状态。

使用 Wait 参数设置为 TRUE 调用 KeReleaseMutex 表示调用方打算在从 KeReleaseMutex 返回时立即调用 KeWait Xxx 支持例程。

对于将 Wait 参数设置为 KeReleaseMutex,请考虑以下准则:

  • 在 IRQL PASSIVE_LEVEL运行的可分页线程或可分页驱动程序例程不应调用将 Wait 参数设置为 TRUE 的 KeReleaseMutex。 如果调用方恰好在 调用 KeReleaseMutex 和 KeWaitXxx对象 之间分页,则此类调用会导致严重页面错误;
  • 在大于 PASSIVE_LEVEL 的IRQL上运行的任何标准驱动程序例程都不能等待任何调度程序对象的非零间隔,而不会关闭系统。 但是,如果此类例程在IRQL小于或等于 DISPATCH_LEVEL 运行时拥有互斥体,则可以调用 KeReleaseMutex ;
互斥体的替代

从 Windows 2000 开始,如果驱动程序要求以 IRQL <= APC_LEVEL 运行的代码采用低开销互斥形式,则可以使用快速互斥。 快速互斥可以保护一次只能由一个线程输入的代码路径。 若要输入受保护的代码路径,线程将获取互斥体。 如果另一个线程已获取互斥体,则当前线程的执行将暂停,直到释放互斥体。 若要退出受保护的代码路径,线程 会释放互斥体。

从 Windows Server 2003 开始,驱动程序还可以使用受保护的互斥体。 受保护的互斥体是快速互斥的替代项,但性能更好。 与快速互斥体一样,受保护的互斥体可以保护一次只能由一个线程输入的代码路径。 但是,与使用快速互斥体的代码相比,使用受保护的互斥体的代码的运行速度要快。

在Windows 8之前的 Windows 版本中,受保护的互斥体的实现方式与快速互斥体不同。 受快速互斥锁保护的代码路径在 IRQL = APC_LEVEL运行。 受受保护的互斥锁保护的代码路径在 IRQL <= APC_LEVEL运行,但禁用了所有 APC。 在这些早期版本的 Windows 中,获取受保护的互斥体比获取快速互斥体要快。 但是,这两种类型的互斥体的行为相同,并受到相同的限制。 具体而言,不应从受快速互斥体或受保护的互斥体保护的代码路径调用在 IRQL = APC_LEVEL 时非法调用的内核例程。

从Windows 8开始,受保护的互斥体作为快速互斥体实现。 在受受保护的互斥体或快速互斥体保护的代码路径中, 驱动程序验证程序 将内核例程的调用视为发生在 IRQL = APC_LEVEL。 与早期版本的 Windows 一样,在APC_LEVEL非法调用在受受保护的互斥体或快速互斥体保护的代码路径中是非法的。

快速互斥

快速互斥体由 FAST_MUTEX 结构表示。 驱动程序为 FAST_MUTEX 结构分配自己的存储,然后调用 ExInitializeFastMutex 例程来初始化该结构。

线程通过执行以下操作之一获取快速互斥体:

  • 调用 ExAcquireFastMutex 例程。 如果互斥体已被另一个线程获取,则调用线程的执行将暂停,直到互斥锁可用;
  • 调用 ExTryToAcquireFastMutex 例程以尝试在不挂起当前线程的情况下获取快速互斥体。 无论是否已获取互斥体,例程都会立即返回。 如果 ExTryToAcquireFastMutex 成功获取调用方互斥体,则返回 TRUE;否则返回 FALSE;

线程调用 ExReleaseFastMutex 来释放由 ExAcquireFastMutex 或 ExTryToAcquireFastMutex 获取的快速互斥体。

受快速互斥锁保护的代码路径在 IRQL = APC_LEVEL运行。 ExAcquireFastMutex 和 ExTryToAcquireFastMutex 将当前 IRQL 提升为 APC_LEVEL, ExReleaseFastMutex 还原原始 IRQL。 因此,当线程持有快速互斥体时,将禁用所有 APC。

如果保证代码路径始终在 APC_LEVEL 运行,则驱动程序可以改为调用 ExAcquireFastMutexUnsafe 和 ExReleaseFastMutexUnsafe 来获取和释放快速互斥。 这些例程不会更改当前 IRQL,仅当当前 IRQL APC_LEVEL时才能安全使用。

快速互斥体不能以递归方式获取。 如果已持有快速互斥锁的线程尝试获取它,该线程将死锁。 快速互斥只能在 IRQL <= APC_LEVEL 下运行的代码中使用。

受保护的互斥体

从 Windows Server 2003 开始提供的受保护的互斥体与快速互斥体具有相同的功能,但性能更高。

从Windows 8开始,受保护的互斥体和快速互斥体的实现方式相同。

在Windows 8之前的 Windows 版本中,受保护的互斥体的实现方式与快速互斥体不同。 获取快速互斥体会将当前 IRQL 提升为APC_LEVEL,而获取受保护的互斥体会进入受保护的区域,这是一个更快的操作。

受保护的互斥体由 KGUARDED_MUTEX 结构表示。 驱动程序为 KGUARDED_MUTEX 结构分配自己的存储,然后调用 KeInitializeGuardedMutex 例程来初始化该结构。

线程通过执行以下操作之一获取受保护的互斥体:

  • 调用 KeAcquireGuardedMutex。 如果互斥体已被另一个线程获取,则调用线程的执行将暂停,直到互斥锁可用;
  • 调用 KeTryToAcquireGuardedMutex 以尝试在不暂停当前线程的情况下获取受保护的互斥体。 无论是否已获取互斥体,例程都会立即返回。 如果 KeTryToAcquireGuardedMutex 成功获取调用方互斥体,则返回 TRUE ;否则返回 FALSE;

线程调用 KeReleaseGuardedMutex 以释放 KeAcquireGuardedMutex 或 KeTryToAcquireGuardedMutex 获取的受保护的互斥体。

保存受保护的互斥锁的线程在受保护的区域内隐式运行。 KeAcquireGuardedMutex 和 KeTryToAcquireGuardedMutex 进入受保护的区域,而 KeReleaseGuardedMutex 退出该区域。 当线程持有受保护的互斥体时,将禁用所有 APC。

如果保证代码路径在禁用所有 APC 的情况下运行,则驱动程序可以改用 KeAcquireGuardedMutexUnsafe 和 KeReleaseGuardedMutexUnsafe 来获取和释放受保护的互斥体。 这些例程不会进入或退出受保护的区域,只能在已存在的受保护区域中使用,或者在 IRQL = APC_LEVEL 中使用。

无法以递归方式获取受保护的互斥体。 如果已持有受保护的互斥锁的线程尝试获取它,该线程将死锁。 受保护的互斥体只能在 IRQL <= APC_LEVEL 下运行的代码中使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/681104.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Spring Ai 快速创建一个AI会话

文章目录 1、创建SpringBoot项目2、引入依赖3、修改配置文件4、一个简单的会话 前期准备 在OpenAI 注册页面创建帐户并在API 密钥页面生成令牌。 Spring AI 项目定义了一个配置属性&#xff0c;您应该将其设置为从 openai.com 获取的spring.ai.openai.api-key值 代码托管于gite…

计算机SCI期刊,IF=9.657,1区TOP,2周内出版!

一、期刊名称 Neural Networks 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;7.8 中科院分区&#xff1a;1区TOP 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;选择开放出版需支付$3350 三、期刊简介 神…

【AI大模型】AI大模型热门关键词解析与核心概念入门

&#x1f680; 作者 &#xff1a;“大数据小禅” &#x1f680; 文章简介 &#xff1a;本专栏后续将持续更新大模型相关文章&#xff0c;从开发到微调到应用&#xff0c;需要下载好的模型包可私。 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 目…

纯血鸿蒙APP实战开发——Grid和List内拖拽交换子组件位置

Grid和List内拖拽交换子组件位置 介绍 本示例分别通过onItemDrop()和onDrop()回调&#xff0c;实现子组件在Grid和List中的子组件位置交换。 效果图预览 使用说明&#xff1a; 拖拽Grid中子组件&#xff0c;到目标Grid子组件位置&#xff0c;进行两者位置互换。拖拽List中子…

Linux 基础命令、性能监控

一、Linux 基础命令 grep&#xff1a;在文件中执行关键词搜索&#xff0c;并显示匹配的结果。 -c 仅显示找到的行数 -i 忽略大小写 -n 显示行号 -v 反向选择: 仅列出没有关键词的行 (invert) -r 递归搜索文件目录 -C n 打印匹配行的前后 n 行grep login user.cpp # 在…

JavaScript 事件

在 Web 开发中&#xff0c;JavaScript 事件是至关重要的概念之一。通过事件&#xff0c;我们可以实现交互性和动态性&#xff0c;使用户与网页进行互动。本篇博客将介绍 JavaScript 事件的基础知识&#xff0c;并深入探讨一些高级技术。 1. 什么是事件&#xff1f; 事件是指用…

JavaEE 初阶篇-深入了解 HTTP 协议

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 HTTP 协议概述 2.0 HTTP 请求协议 2.1 请求方式的具体体现 3.0 HTTP 响应协议 3.1 常见的状态码及描述 3.2 常见的响应头 4.0 HTTP 协议解析 4.1 简单实现服务器响…

提升工作效率,用ONLYOFFICE打造高效团队协作环境

作为一名深耕技术领域已有六七年的开发者&#xff0c;同时又是断断续续进行技术创作将近六年的一个小小作者&#xff0c;我在工作和日常生活中&#xff0c;使用过各色各样的软件。 而在最近几年&#xff0c;一款名为ONLYOFFICE的开源办公套件逐渐走进并融入我的工作与生活&…

CPU基本知识点

目录 1.概念 2.分类 3.运作原理 4.指令系统 1.概念 CPU&#xff1a;英文Central Processing Unit&#xff0c;即中央处理器。 解释和执行指令的功能单元&#xff0c;它是计算机的中枢神经系统&#xff08;即核心&#xff09;。 是计算机最核心的部件&#xff0c;主要是运算…

Springboot+vue项目影城管理系统

摘 要 本论文主要论述了如何使用JAVA语言开发一个影城管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述影城管理系统的当前背景以及系统开发的目的&…

Tower for Mac:Git管理的新境界

Tower for Mac&#xff0c;让您的Git管理进入新境界&#xff01;这款专为Mac用户打造的Git客户端&#xff0c;凭借其出色的性能和丰富的功能&#xff0c;成为众多开发者的首选工具。 Tower不仅支持常规的Git操作&#xff0c;如提交、推送和拉取&#xff0c;还提供了许多高级功能…

使用CNN或resnet,分别在flower5,flower17,flower102数据集上实现花朵识别分类-附源码-免费

前言 使用cnn和resnet实现了对flower5&#xff0c;flower17&#xff0c;flower102数据集上实现花朵识别分类。也就是6份代码&#xff0c;全部在Gitee仓库里&#xff0c;记得点个start支持谢谢。 本文给出flower17在cnn网络实现&#xff0c;flower102在resnet网络实现的代码。…