wdk中使用自旋锁SpinLock的规则及原因分析

1.尽早释放自旋锁,因为拥有它,其他cpu的活动就要被阻止。

    这个没什么好说的

2.拥有自旋锁时不要引起硬件或软件异常,否则系统会崩溃

3.拥有自旋锁是不要访问任何分页代码或数据。

   2和3得放一起讨论。这里引用MSDN上的一篇文章:Preventing Errors and Deadlocks While Using Spin Locks 这篇文章中罗列了拥有SpinLock时不应该触发的异常。粗看很疑惑,为什么不能在此期间触发异常?那换个说法,在IRQL>=DISPATCH_LEVEL时,不能使用分页内存,这个驱动开发规则大家都能理解和接受吧?其实2和3是这个规则的演化版,看下KeAcquireSpinLock的实现:

VOID
NTAPI
KeAcquireSpinLock(PKSPIN_LOCK SpinLock,
                  PKIRQL OldIrql)
{
    /* Call the fastcall function */
    *OldIrql = KfAcquireSpinLock(SpinLock);
}

KIRQL FASTCALL
KfAcquireSpinLock (
	PKSPIN_LOCK	SpinLock
	)
{
   KIRQL OldIrql;

   ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
   
   OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
   KiAcquireSpinLock(SpinLock);

   return OldIrql;
}



KeAcquireSpinLock调用KfAcquireSpinLock.KfAcquireSpinLock干的第一件事就是提升IRQL到DISPATCH_LEVEL。因此直到释放自旋锁,使得IRQL降低到DISPATCH_LEVEL一下,整个代码段都在DISPATCH_LEVEL级别上运行,因此,这个IRQL级别上需要遵守的规则在KeAcquireSpinLock函数中也得遵守

4.拥有自旋锁时,不能调用IoStartNextPacket和IoCompleteRequest例程,一定不要在取消例程中调用 IoAcquireCancelSpinLock,这会引起系统死锁

   拥有自旋锁时不能调用IoStartNextPacket我觉得有点牵强。IoStartNextPacket和IoStartPacket是一对好朋友,既然IoStartNextPacket不能调用了IoStartPacket也应该被同等对待。目前猜测可能是因为执行IoStartNextPacket时会依次执行设备队列中挂起的Irp请求,这违反了要尽早释放自旋锁的初衷。(这个解释我自己都不怎么信服);

   对于 IoCompleteRequest的禁忌原因,我觉得可能是因为下列情形: 如果某层设备栈设置了完成例程,那么当调用IoCompleteRequest后执行设备栈回滚时,回滚到完成例程中。再进一步假设这个完成例程把下层返回上来的Irp拆成多个子Irp,以同步方式调用IoCallDriver下发这些IRP,这势必会长时间造成等待。这也与尽早释放自旋锁的初衷相违背;

    至于不要在取消例程中调用 IoAcquireCancelSpinLock,这个可以看下IoCancelIrp的实现:

BOOLEAN
NTAPI
IoCancelIrp(IN PIRP Irp)
{
    KIRQL OldIrql;
    PDRIVER_CANCEL CancelRoutine;
    IOTRACE(IO_IRP_DEBUG,
            "%s - Canceling IRP %p\n",
            __FUNCTION__,
            Irp);
    ASSERT(Irp->Type == IO_TYPE_IRP);

    /* Acquire the cancel lock and cancel the IRP */
    IoAcquireCancelSpinLock(&OldIrql);
    Irp->Cancel = TRUE;

    /* Clear the cancel routine and get the old one */
    CancelRoutine = IoSetCancelRoutine(Irp, NULL);
    if (CancelRoutine)
    {
        ...
        CancelRoutine(IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Irp);
        return TRUE;
    }
    IoReleaseCancelSpinLock(OldIrql);
    return FALSE;
}
在调用取消函数之前,IoCancelIrp已经调用过 IoAcquireCancelSpinLock,如果在取消函数中再次调用这个就会因递归调用自旋锁而造成死锁

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值