本篇内容对应<竹林蹊径>3.4.7对象同步一节。作者对WDF同步域及运行级别的解释停留在纸面,然而,这两个概念却涉及到WDF设备\WDF队列的初始化,因此,不得不探究其背后的原理。

1.同步范围

书上P77提到可以指定2种同步范围:设备同步范围及队列同步范围。首先需要确定什么时候设置同步范围?分别以WdfSynchronizationScopeDevice和WdfSynchronizationScopeQueue为关键字,搜索整个ddk sample代码,在以下文件中找到相关设置:
设备同步范围:
//serial\serial\pnp.c
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE (&attributes,SERIAL_DEVICE_EXTENSION);attributes.EvtCleanupCallback = SerialEvtDeviceContextCleanup;attributes.SynchronizationScope = WdfSynchronizationScopeDevice;status = WdfDeviceCreate(&DeviceInit, &attributes, &device);

队列同步范围:

//general\echo\kmdf\autosync\queue.cWDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_CONTEXT);queueAttributes.SynchronizationScope = WdfSynchronizationScopeQueue;queueAttributes.EvtDestroyCallback = EchoEvtIoQueueContextDestroy;status = WdfIoQueueCreate(Device,&queueConfig,&queueAttributes,&queue);

从上面的代码片可知:在创建device或queue时可以设置同步范围。新的问题出现了:为什么都在创建WDF对象前设置?让我们顺着WdfDeviceCreate源码中层层推进

> WdfDeviceCreate()>   FxDevice::_Create();>        pDevice->Initialize();>           FxDevice::Initialize();>             status = ConfigureConstraints(DeviceAttributes);

最终框架在FxDeviceBase::ConfigureConstraints中对同步范围进行了设置(同时也设置了运行级别):

NTSTATUS
FxDeviceBase::ConfigureConstraints(__in_opt PWDF_OBJECT_ATTRIBUTES ObjectAttributes)
{NTSTATUS            status;WDF_EXECUTION_LEVEL driverLevel;WDF_SYNCHRONIZATION_SCOPE driverScope;if (ObjectAttributes != NULL) {m_ExecutionLevel = ObjectAttributes->ExecutionLevel;m_SynchronizationScope = ObjectAttributes->SynchronizationScope;}//取出父对象的同步范围和运行级别m_Driver->GetConstraints(&driverLevel, &driverScope);//如果Device继承父对象(Driver)的属性,则依葫芦画瓢if (m_ExecutionLevel == WdfExecutionLevelInheritFromParent) {m_ExecutionLevel = driverLevel;}if (m_SynchronizationScope == WdfSynchronizationScopeInheritFromParent) {m_SynchronizationScope = driverScope;}//如果是运行级别是PASSIVE_LEVEL,为驱动同步创建MUTEX(因为MUTEX对象会造成线程等待失去CPU)if (m_ExecutionLevel == WdfExecutionLevelPassive) {m_CallbackLockPtr = new(GetDriverGlobals()) FxCallbackMutexLock(GetDriverGlobals());}else { //如果运行级别是DISPATCH_LEVEL,为驱动同步创建spin_lockm_CallbackLockPtr  = new(GetDriverGlobals()) FxCallbackSpinLock(GetDriverGlobals());}if (NULL == m_CallbackLockPtr) {...}//晦涩的代码m_CallbackLockPtr->Initialize(this);m_CallbackLockObjectPtr = this; //this对于这个函数而言,是指FxDevicestatus = STATUS_SUCCESS;Done:return status;
}

在ConfigureConstraints中,框架首先根据WDF_OBJECT_ATTRIBUTE相关域设置即将被创建的Device的同步范围/运行级别属性。驱动同步往往通过MUTEX/SPIN_LOCK来实现,因此,框架根据coder设置的不同运行级别,创建适合运行在不同LEVEL上的同步对象。

代码中有两行容易被忽视的代码:

    m_CallbackLockPtr->Initialize(this);m_CallbackLockObjectPtr = this;

为什么说它重要?后面第二节会用到[注1]。查看Initialize的定义,发现它是一个纯虚函数:

class FxCallbackLock : public FxGlobalsStump {
...virtualvoidInitialize(FxObject* ParentObject) = 0;
...
};

要找到运行时执行的Initialize函数需要费点功夫:

Step1.m_CallbackLockPtr是定义在class FxDeviceBase中基类指针:

FxCallbackLock* m_CallbackLockPtr;

Step2.在FxDeviceBase::ConfigureConstraints中,基类指针指向了不同派生类对象:

if (m_ExecutionLevel == WdfExecutionLevelPassive) {m_CallbackLockPtr = new(GetDriverGlobals()) FxCallbackMutexLock(GetDriverGlobals());}else { //如果运行级别是DISPATCH_LEVEL,为驱动同步创建spin_lockm_CallbackLockPtr  = new(GetDriverGlobals()) FxCallbackSpinLock(GetDriverGlobals());}

Step3.定位到派生类FxCallbackMutexLock/FxCallbackSpinLock,查看它们如何实现Initialize函数:

class FxCallbackMutexLock : public FxCallbackLock {
...virtualvoidInitialize(FxObject* ParentObject){PFX_DRIVER_GLOBALS fxDriverGlobals;m_Verifier = NULL;fxDriverGlobals = GetDriverGlobals();if (fxDriverGlobals->FxVerifierLock) {//// VerifierLock CreateAndInitialize failure is not fatal,// we just won't track anything//(void) FxVerifierLock::CreateAndInitialize(&m_Verifier,fxDriverGlobals,ParentObject,TRUE);}}
...
};

fxDriverGlobals->FxVerifierLock好像只有当开启Driver Verifier时才会非空,所以可以简单的认为这段if分支不会执行。

2.同步范围的应用

讲到这,还是看不出同步范围对驱动运行的影响,看官莫急,马上见分晓。我们来看看Queue对象的创建。它的调用流程和WdfDevice类似,也经历这样的过程:

 FxIoQueue::_Create();FxIoQueue->Initialize();FxIoQueue::Initialize();status = ConfigureConstraints(DeviceAttributes);

只是在FxIoQueue::ConfigureConstraints中略有不同:

if (m_SynchronizationScope == WdfSynchronizationScopeDevice) {NTSTATUS status;
...AutomaticLockingRequired = TRUE;m_CallbackLockPtr = m_Device->GetCallbackLockPtr(&m_CallbackLockObjectPtr); //<-------重要代码,和[注1]呼应!!!}else if (m_SynchronizationScope == WdfSynchronizationScopeQueue) {AutomaticLockingRequired = TRUE;}if (AutomaticLockingRequired) {//// If automatic locking has been configured, set the lock// on the FxCallback object delegates//m_IoDefault.SetCallbackLockPtr(m_CallbackLockPtr);m_IoStop.SetCallbackLockPtr(m_CallbackLockPtr);m_IoResume.SetCallbackLockPtr(m_CallbackLockPtr);
...m_IoCancelCallbackLockPtr = m_CallbackLockPtr;}else {//// No automatic locking specified//m_IoDefault.SetCallbackLockPtr(NULL);m_IoStop.SetCallbackLockPtr(NULL);
...m_IoCancelCallbackLockPtr = NULL;}

FxIoQueue从父对象中获得同步范围和运行级别,FxIoQueue的父对象是谁?是前面说过FxDevice。还记得在前面[注1]中写到过两行容易被忽视的代码?第一行代码已经解释过,来看下第二行:

    m_CallbackLockPtr->Initialize(this);m_CallbackLockObjectPtr = this;

m_CallbackLockObjectPtr = this中this指针指向谁?应该是调用者FxDevice,即m_CallbackLockObjectPtr指向了FxDevice。

对于框架这样的设置,如果不把眼光放到其他函数中,可能会不知所以然;但是,如果把眼光投到FxIoQueue::ConfigureConstraints中,可能会理解框架的用意。在此之前看看GetCallbackLockPtr实现:
FxCallbackLock*
FxDeviceBase::GetCallbackLockPtr(__out_opt FxObject** LockObject)
{if (LockObject != NULL) {*LockObject = m_CallbackLockObjectPtr;}return m_CallbackLockPtr;
}

FxIoQueue::ConfigureConstraints中执行这句后

m_CallbackLockPtr = m_Device->GetCallbackLockPtr(&m_CallbackLockObjectPtr); //<-------重要代码,和[注1]呼应!!!

进入FxDeviceBase::GetCallbackLockPtr函数时,将用到两个变量:m_CallbackLockObjectPtr/m_CallbackLockPtr。对于FxDevice类和FxIoQueue类他们都有这两个成员变量,那么,请不假思索的告诉我在FxDeviceBase::GetCallbackLockPtr用到的m_CallbackLockObjectPtr/m_CallbackLockPtr变量是哪个类的?显然,调用者是FxDevice m_Device----框架用FxDevice!m_CallbackLockObjectPtr和FxDevice!m_CallbackLockPtr成员变量赋值给FxIoQueue对应成员变量。

为什么要这样做?这句语句是在if (m_SynchronizationScope == WdfSynchronizationScopeDevice)分支中执行的,换言之,当FxIoQueue被设置为设备同步范围时,它使用父对象FxDevice的同步锁/互斥量。当驱动程序中有2个以上的FxIoQueue对象,只有获得FxDevice创建的同步锁/互斥量的FxIoQueue对象才能进一步操作。所以,可以认为设备同步范围的锁颗粒度较大

至于设置为队列同步范围的FxIoQueue对象,在操作前不需要争抢FxDeviceFxDevice创建的同步锁/互斥量。相对于设备同步范围,它的锁颗粒度较小。

WDF框架系列:同步域,运行级别相关推荐

  1. Windows Server 2012系列之三提升域功能级别与降低域功能级别

    Windows Server 2012可以回滚林功能级别和域功能级别,可以回滚到Windows Server 2008 R2或Windows Server 2008,前提是Recycle Bin一定是 ...

  2. cmd长ping记录日志和时间_Gin 框架系列 — 路由中间件:日志记录

    概述 首先同步下项目概况: 上篇文章分享了,规划项目目录和参数验证,其中参数验证使用的是 validator.v8 版本,现已更新到 validator.v9 版本,最新代码查看 github 即可. ...

  3. Wdf框架之WdfObject状态机(3)-前篇

    好久没写关于Wdf框架的博客了,因为有各种琐碎事缠身,得赶在RS4 RTM前把状态机(3)系列完成.WdfObject状态机(2)系列将注意力集中在驱动程序的继承层次上只存在单薄一层WdfDriver ...

  4. Wdf框架之WdfObject状态机(2)

    前一篇博文<Wdf框架之WdfObject状态机(1)>提到调用WdfObjectCreate使框架对象被纳入对象状态机的管理之下.本篇我们看下框架对象的销毁过程,即如何结束它的生命周期, ...

  5. CYQ.Data 数据框架系列索引

    2019独角兽企业重金招聘Python工程师标准>>> 索引基础导航: 1:下载地址:http://www.cyqdata.com/download/article-detail-4 ...

  6. 活动目录系列之三---域控制器常规卸域

    活动目录系列之三---域控制器常规卸域 在介绍正题之前,先补充很多人提出的一个疑问,就是什么情况下计算机名系统不让更改,其实很简单,当在域情况下,用非管理员登陆域,即计算机名就不让更改,运行---CM ...

  7. Linux服务管理---系统运行级别、rpm启动与自启动、源码包的启动...

    版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/twilight_karl/article/details/76714066 系统运行级别 runlevel ...

  8. Linux引导过程与服务控制、排障演示及运行级别(详细图解)

    Linux操作系统引导过程目录 一.Linux操作系统引导过程 1.开机自检 2.MBR 引导 3.GRUB菜单 4.加载Linux内核 5.init进程初始化 二.系统初始化进程init和Syste ...

  9. 详细图文演示——排除启动类故障以及Linux操作系统引导、运行级别和优化启动等相关知识

    详细图文演示--排除启动类故障以及Linux操作系统引导.运行级别和优化启动等相关知识 一.Linux操作系统引导过程 1.开机自检 2.MBR 引导 3.GRUB菜单 4.加载Linux内核 5.i ...

  10. .NET简谈组件程序设计之(上下文与同步域)

    我们继续学习.NET多线程技术,这篇文章的内容可能有点复杂.在打破常理之后,换一种新的思考模型最为头疼.这篇文章里面会涉及到一些不太常见的概念,比如:上下文.同步域等等.我也是最近才接触这些关于组件编 ...

最新文章

  1. pythonurllib模块-python模块之urllib
  2. 【IM】关于无监督降维的理解
  3. 管窥MVVMLight Command参数绑定和事件传递
  4. mysql构建数据立方体_OLAP数据建模工具Workbench的初步使用(数据立方体的建立)
  5. PMI、砺志咨询活动:项目经理软技能征文大赛—15个PDU、免费软技能培训
  6. 【Flink】Flink 实时去重方案 四种方案 MapState 、SQL方式、HyperLogLog、Bitmap
  7. css img 适配尺寸_CSS——img标签图片适配居中问题
  8. 最全Pycharm教程(5)——Python快捷键相关设置
  9. tp前后端不分离源码_Thinkphp5.0+Vue2.0前后台分离框架通使用后端源码
  10. android版《手工接水管》--unity3d制作的
  11. namesilo如何使用BTC支付:
  12. 【单片机】用定时器以间隔500ms在8位数码管上依次显示0、1、2、3、...C、D、E、F,重复
  13. 串口、COM口、UART口, TTL、RS-232、RS-485的区别
  14. win7锁定计算机快捷键6,win7锁定计算机快捷键_win10 锁定计算机
  15. C语言重载宏函数的小技巧
  16. NTC电阻抑制冷机启动浪涌电流
  17. 【剑指Offer】个人学习笔记_15_二进制中1的个数
  18. Spring Boot Freemarker 模板调用Java方法
  19. word中多级列表操作问题
  20. mac 版 docker挂载文件出现Mounts denied问题

热门文章

  1. 面部捕捉技术_一种基于面部捕捉系统的捕捉点定位系统的制作方法
  2. Nova 组件如何协同工作 - 每天5分钟玩转 OpenStack(24)
  3. 电子邮箱地址怎么写?
  4. 群晖万兆文件服务器,NAS进阶 篇三:2019年最具性价比的NAS硬件是什么暨黑群晖万兆搭建经验介绍...
  5. kafka数据丢包原因及解决方案
  6. sparking Streaming version 2.3.2+kafka接收实时流数据找不到hive创建的数据库问题
  7. 海康摄像头录像回放功能
  8. P3376 (最大流 dinic)
  9. hbase metric 监控项
  10. 资治通鉴-5 商鞅的得与失