youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:1648回复:15

问个关于运行级别的问题,高手指点一下。

楼主#
更多 发布于:2004-05-31 09:32
常说这个函数运行的一个别上的,是相对什么而言的呢?

比如说的PtReceive中调用一个运行的Passive_level上的函数,这个Passive是相对PtReceive的运行级别而言的吗?
asmsys
驱动老牛
驱动老牛
  • 注册日期2002-03-29
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望17点
  • 贡献值0点
  • 好评度8点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2004-05-31 10:03
不是,这个级别是绝对的。用了分页内存的函数,只能运行在PASSIVE LEVEL。有一种理想的状况,
不使用内存,也不调用别的函数的函数可以在任何级别运行。
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
板凳#
发布于:2004-05-31 12:42
不使用内存的函数? :o :D
asmsys
驱动老牛
驱动老牛
  • 注册日期2002-03-29
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望17点
  • 贡献值0点
  • 好评度8点
  • 原创分0分
  • 专家分0分
地板#
发布于:2004-05-31 13:29
不使用内存的函数? :o :D

这是我经常用的一个函数
htonl PROC

xchg ah,al
rol eax,16
xchg ah,al
ret
htonl ENDP
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
地下室#
发布于:2004-05-31 16:39
要是我没有记错的话,即使是光一个ret,也要从stack把返回地址给pop吧,碰了stack,就碰内存了 :P

阿弥陀佛,调刺,调刺,罪过,罪过 :D
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2004-05-31 16:58
不是,这个级别是绝对的。用了分页内存的函数,只能运行在PASSIVE LEVEL。有一种理想的状况,
不使用内存,也不调用别的函数的函数可以在任何级别运行。
 



这里就是我不懂的地方了,那么假设一个函数运行在passive Level上吧,什么时候打破这个规则?也就是说什么时候它会运行在一个高于PASSIVE LEVEL上(当然这样肯定会蓝屏)
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
6楼#
发布于:2004-06-01 09:05
比如说,你在DPC,调用了一个只能运行在passive level的函数
asmsys
驱动老牛
驱动老牛
  • 注册日期2002-03-29
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望17点
  • 贡献值0点
  • 好评度8点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2004-06-01 09:49
要是我没有记错的话,即使是光一个ret,也要从stack把返回地址给pop吧,碰了stack,就碰内存了 :P

阿弥陀佛,调刺,调刺,罪过,罪过 :D

如果我用C规则调用呢?
asmsys
驱动老牛
驱动老牛
  • 注册日期2002-03-29
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望17点
  • 贡献值0点
  • 好评度8点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2004-06-01 10:06
当一个函数被调用的时候,他的运行级别的决定了。但函数的内部在调用了KeAcquireSpinLock也会提升到DISPATCH。
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2004-06-01 11:43
比如说,你在DPC,调用了一个只能运行在passive level的函数


DPC是指什么?Dispatch Level?

你说的这种调用会出错的吧。
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2004-06-02 20:38
感谢两位老师的回答。虽然我还是不太明白。
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
11楼#
发布于:2004-06-02 20:48
说句实话,我有些迷糊你的问题。
你到底是想问什么呀? :D
youngyt
驱动牛犊
驱动牛犊
  • 注册日期2003-11-23
  • 最后登录2006-12-29
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
12楼#
发布于:2004-06-02 21:12
其实就是我不明白运行级别的用处。说某个函数运行在某个级别上,那么我调用这个函数的时候怎么判断它是否是运行在那个级别上呢?
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
13楼#
发布于:2004-06-02 21:24
这样想:在某时刻,CPU处于某个IRQL,对应于不同的IRQL,有些函数能在此IRQL被调,有些不能。
只能运行于passice level的routine,就是说,当这个routine被调用的时候,CPU只能处于passive level这个IRQL上,否则极可能BSOD

其实原则就是:在大于或等于dispatch level时,不能等待,page fault就是要等待的
arthurtu
驱动巨牛
驱动巨牛
  • 注册日期2001-11-08
  • 最后登录2020-12-19
  • 粉丝0
  • 关注0
  • 积分26分
  • 威望161点
  • 贡献值0点
  • 好评度35点
  • 原创分0分
  • 专家分0分
  • 社区居民
14楼#
发布于:2004-06-02 21:28
DPC是在dispatch level的,还有startio,timer的callback我记得也是,中断在DIRQL,更高了
获得spinlock后也在dispatch level,还有一些同步的函数
asmsys
驱动老牛
驱动老牛
  • 注册日期2002-03-29
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望17点
  • 贡献值0点
  • 好评度8点
  • 原创分0分
  • 专家分0分
15楼#
发布于:2004-06-03 08:36
BBS 水木清华站 -- 文章阅读 [讨论区: DriverDev]

[分类讨论区] [全部讨论区] [返回版面] [返回文摘区] [快速返回]
--------------------------------------------------------------------------------
发信人: flier (小海 [寻找风车中]), 信区: DriverDev
标  题: Win32 核心 DPC 设计思想和实现思路浅析(草稿)
发信站: BBS 水木清华站 (Tue Mar 16 23:50:05 2004), 站内

【 以下文字转载自 MSDN 讨论区 】
发信人: flier (小海 [寻找风车中]), 信区: MSDN
标  题: Win32 核心 DPC 设计思想和实现思路浅析(草稿)
发信站: BBS 水木清华站 (Tue Mar 16 23:49:00 2004), 转信

Win32 核心 DPC 设计思想和实现思路浅析

Flier Lu (flier@nsfocus.com)

    x86架构设计在上是基于中断思想的,因而从DOS到Win32,操作系统中大量使用中断的概念来表达异步操作的行为。但与DOS下独占的情况不同,Win32下需要由系统对多任务进行调度,因此中断响应代码必须尽可能地简单,并且尽快的将控制权交还给系统。虽然这样一来系统调度的响应速度和实现过程方便了,但还是有很多功能需要在中断响应中完成。为此,Win32核心提供了DPC(Deferred Procedure Call)和APC(Asynchronous Procedure Call)两个IRQL特殊的软件中断级别,用于实现延迟和异步的过程调用。
    从IRQL分层来说,DPC和APC是介于较高级别的设备中断和最低级别的Passive中断之间,由操作系统用于完成特殊方法调用的中断级别。与处理硬件操作的设备中断和更高级别的时钟、处理器中断不同,这两级中断纯粹是为了实现功能调用异步性而设计实现的,因此操作系统本身也对它们具有很强的依赖型。APC这里暂且不讨论,以后有机会再写篇文章专门讨论 :)

    DPC在功能上可以理解为ISR(Interrupt Service Routine)的一部分。只是因为ISR为了尽量简单和返回控制权给操作系统,而将一部分功能剥离出来放入相应DPC中,延迟调用。因为DPC的IRQL仅在APC和Passive中断之上,所以系统可以从容地处理完高级别的中断后,再在DPC一级慢慢处理积累起来的相对并不那么紧急功能。
    DPC在使用上可以理解为一个回调函数的封装对象。系统本身或者设备驱动程序,在合适的地方如设备驱动程序的AddDevice函数或DispatchPnP函数处理IRP_MN_START_DEVICE请求时,初始化一个DPC对象;在ISR中判断是否需要进一步处理中断,是则请求将DPC对象插入到系统DPC队列中;系统处理完高IRQL后,会在IRQL DISPATCH_LEVEL级别慢慢处理DPC队列中的DPC对象;每个DPC对象封装的回调函数,会使用同时封装的调用参数,被系统调用,完成在ISR中来不及完成的工作;如果需要进一步的工作,还可以继续请求插入DPC对象到DPC队列中。

    DPC对象从最终用户角度有两种:DpcForIsr和CustomDPC。前者是与设备驱动对象(Device Object)绑定的;后者则由驱动自行维护。但从实现上来说,只有一种DPC对象存在,DpcForIsr所涉及的维护函数,实际上都是对CustomDPC的一个封装而已。

    我们首先来看看初始化DPC对象的实现。KeInitializeDpc函数(ntos\\ke\\dpcobj.c:39)完成具体的DPC对象的初始化,实际上就是填充一个内存结构KDPC(ntos\\inc\\ntosdef.h:331)。

//
// Deferred Procedure Call (DPC) object
//

typedef struct _KDPC {
    CSHORT Type;
    UCHAR Number;
    UCHAR Importance;
    LIST_ENTRY DpcListEntry;
    PKDEFERRED_ROUTINE DeferredRoutine;
    PVOID DeferredContext;
    PVOID SystemArgument1;
    PVOID SystemArgument2;
    PULONG_PTR Lock;
} KDPC, *PKDPC, *RESTRICTED_POINTER PRKDPC;

    Type 表示此内核对象的类型,在KOBJECTS枚举类型(ntos\\inc\\ke.h:122)中定义,缺省为 DpcObject = 0x13。此外WinXP/2003新增了一种ThreadedDpcObject = 0x18
    Number 在多处理器环境下用于指定此DPC对象加入到哪个处理器的DPC队列中,我们等会讨论多处理器时详细描述。缺省为 0
    Importance 表示此DPC对象的重要性,在KDPC_IMPORTANCE枚举类型(ntos\\inc\\ntosdef.h:321)中定义,缺省为 MediumImportance = 1
    DpcListEntry 是用于维护DPC队列的链表指针
    DeferredRoutine 是此DPC对象绑定的回调函数,后面DeferredContext、SystemArgument1和SystemArgument2分别是此回调函数被调用时的参数。如ISR中调用IoRequestDpc时,后面两个参数就用于传递Irp和Context参数给DPC的回调函数。
    Lock 保存此DPC对象所在DPC队列的自旋锁,用于锁定DPC队列,同时也用于判断此DPC对象是否被加入到一个DPC队列中。

    了解了KDPC对象的结构,实际上维护代码就非常简单了。KeInitializeDpc函数将KDPC对象结构初始化为初值;IoInitializeDpcRequest函数则只是对KeInitializeDpc函数的一个简单包装,如下

#define IoInitializeDpcRequest( DeviceObject, DpcRoutine ) (\\
    KeInitializeDpc( &(DeviceObject)->Dpc,                  \\
                     (PKDEFERRED_ROUTINE) (DpcRoutine),     \\
                     (DeviceObject) ) )

    注意WinXP/2003下实际上KeInitializeDpc函数和KeInitializeThreadedDpc函数都是由一个KiInitializeDpc函数完成具体工作的,只是传递的最后一个参数定义的对象类型不同。

    KeInsertQueueDpc函数(ntos\\ke\\dpcobj.c:89)实际上是系统对DPC队列维护的核心函数,其伪代码如下:

BOOLEAN KeInsertQueueDpc (IN PRKDPC Dpc, IN PVOID SystemArgument1,IN PVOID SystemArgument2)
{
  PKSPIN_LOCK Lock;
  KIRQL OldIrql;

  KeRaiseIrql(HIGH_LEVEL, &OldIrql);  // 提升当前IRQL到最高,屏蔽其它中断

  PKPRCB = KeGetCurrentPrcb();        // 获取当前处理器控制块

  // 通过比较Dpc->Lock是否为空,来判断此DPC对象是否已经被加入到DPC队列;
  // 如果DPC对象可以被加入到队列,则将当前处理器控制块的DPC自旋锁复制到Dpc->Lock中
  if ((Lock = InterlockedCompareExchangePointer(&Dpc->Lock, &Prcb->DpcLock, NULL)) == NULL)
  {
    // 更新当前处理器控制块的统计信息
    Prcb->DpcCount += 1;
    Prcb->DpcQueueDepth += 1;

    // 更新DPC对象的参数信息
    Dpc->SystemArgument1 = SystemArgument1;
    Dpc->SystemArgument2 = SystemArgument2;

    // 根据DPC对象优先级,决定将之加入到DPC队列的头部或尾部
    if (Dpc->Importance == HighImportance)
        InsertHeadList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
    else
        InsertTailList(&Prcb->DpcListHead, &Dpc->DpcListEntry);

    // 如果当前处理器没有DPC对象活动或DPC中断请求,则进一步判断是否发出DPC中断请求
    if (Prcb->DpcRoutineActive == FALSE && Prcb->DpcInterruptRequested == FALSE)
    {
      // 如果DPC对象优先级为中高;
      // 或者DPC队列长度超过阈值MaximumDpcQueueDepth;
      // 或者DPC请求速率小于阈值MinimumDpcRate
      if ((Dpc->Importance != LowImportance) ||
          (Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
          (Prcb->DpcRequestRate < Prcb->MinimumDpcRate))
      {
        // 满足触发条件,则发出DPC中断请求
        Prcb->DpcInterruptRequested = TRUE;
        KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
      }
    }
  }
  KeLowerIrql(OldIrql);
  return (Lock == NULL);
}
    这里的几个阈值,在KiInitializeKernel函数(ntos\\ke\\i386\\kernlini.c:246)中,根据全局变量KiMaximumDpcQueueDepth、KiMinimumDpcRate和KiAdjustDpcThreshold确定。而这几个全局变量可以通过注册表项(HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel\\)下的DpcQueueDepth、MinimumDpcRate和AdjustDpcThreshold三个键值来设置。具体的设置方法,请参考MSDN以及性能计数器的Processor\\% DPC Time等动态指数。

    而处理与驱动绑定的DPC对象的IoRequestDpc函数只是KeInsertQueueDpc函数的一个简单包装。

#define IoRequestDpc( DeviceObject, Irp, Context ) ( \\
    KeInsertQueueDpc( &(DeviceObject)->Dpc, (Irp), (Context) ) )

    与KeInsertQueueDpc函数对应的KeRemoveQueueDpc函数(ntos\\ke\\dpcobj.c:272)实际上只是完成简单的将DPC对象从DPC队列中删除的功能。

    最后对DPC对象属性进行修改的KeSetImportanceDpc函数(ntos\\ke\\dpcobj.c:367)和KeSetTargetProcessorDpc函数(ntos\\ke\\dpcobj.c:401)实际上都是直接修改DPC对象结构的相应域。KDPC::Number大于MAXIMUM_PROCESSORS = 32时,用于指定DPC对象的目标CPU。如调用KeSetTargetProcessorDpc(pKDpc, 2)后,pKDpc = MAXIMUM_PROCESSORS + 2。

    在了解了DPC对象和DPC队列的大致维护函数功能后,我们来看看稍微复杂一些的在多处理器下DPC队列的维护流程。

    前面提到KDPC::Number指定了DPC对象所用的处理器号,因此在KeInsertQueueDpc函数开始获取处理器控制块时,需要判断Number是否指向一个处理器,并从全局处理器控制块列表中获取相应的处理器控制块,为代码如下:

if (Dpc->Number >= MAXIMUM_PROCESSORS)  // Number大于MAXIMUM_PROCESSORS时用于指定处理器

{
  Processor = Dpc->Number - MAXIMUM_PROCESSORS;
  Prcb = KiProcessorBlock[Processor];   // 全局唯一的处理器控制块列表

}

else
{
  Prcb = KeGetCurrentPrcb();
}

KiAcquireSpinLock(&Prcb->DpcLock);      // 使用自旋锁保护处理器控制块中的DPC队列


    而在KeInsertQueueDpc函数中判断是否发出DPC中断请求时,也需要做更复杂的逻辑判断。
    对DPC对象目标处理器就是当前处理器的情况,可以和前面单处理器时一样处理,直接发送DPC中断请求;但对于DPC对象目标处理器是其他处理器的情况,就必须使用KiIpiSend函数发送IPI(InterProcessor Interrupt)中断,通知目标处理器执行动作。此IPI中断是介于系统掉电中断(POWER_LEVEL)和时钟中断之间的特殊IRQL,专门用于在多处理器情况下协调多个处理器的工作。
    此外就是在多处理器情况下,各种对DPC队列的操作都需要用此处理器控制块的DPC队列自旋锁保护起来,避免同步问题。

    由此我们可以看到,实际上DPC队列是每个处理器一个的,我们完全可以将某个DPC对象绑定到某个处理器上,实现类似线程亲缘性(Thread Affinity)的效果,优化在多处理器环境下的性能。但这同时也带来一个问题,就是ISR程序可以和DPC回调函数同时被调用,某种程度上也造成了开发复杂度的增加,具体处理方法请参考DDK中相关文档。

    Kernel-Mode Driver Architecture\\Design Guide\\Servicing Interrupts\\DPC Objects and DPCs


--
 .    生命的意义在于   /\\   ____\\ /\\_ \\   /\\_\\    http://flier_lu.blogone.net .                            .
 .        希望         \\ \\  \\___/_\\/\\ \\   \\/_/__     __    _ _★              .
 .        工作          \\ \\   ____\\\\ \\ \\    /\\  \\  /\'__`\\ /\\`\'_\\              .
 .      爱你的人         \\ \\  \\___/ \\ \\ \\___\\ \\  \\/\\ __// \\ \\ \\/              .
 .     和你爱的人         \\ \\___\\    \\ \\_____\\ \\__\\ \\____\\ \\ \\_\\              .
 .        ……             \\/___/     \\/_____/\\/__/\\/____/  \\/_/ @nsfocus.com .


※ 来源:?BBS 水木清华站 smth.org?[FROM: 211.167.254.*]
 

--------------------------------------------------------------------------------
[分类讨论区] [全部讨论区] [返回版面] [返回文摘区] [快速返回]
游客

返回顶部