首页
社区
课程
招聘
[旧帖] [求助]驱动开发中startio的问题,急急急急急急!!! 0.00雪花
发表于: 2010-10-9 13:11 1333

[旧帖] [求助]驱动开发中startio的问题,急急急急急急!!! 0.00雪花

2010-10-9 13:11
1333
先看一些代码如下:
VOID IoStartPacket(PDEVICE_OBJECT device, PIRP Irp, PULONG key, PDRIVER_CANCEL cancel)
{
  KIRQL oldirql;
  IoAcquireCancelSpinLock(&oldirql);
  IoSetCancelRoutine(Irp, cancel);
  device->CurrentIrp = Irp;
  IoReleaseCancelSpinLock(oldirql);
  device->DriverObject->DriverStartIo(device, Irp);
}

VOID IoStartNextPacket(PDEVICE_OBJECT device, BOOLEAN cancancel)
{
  KIRQL oldirql;
  if (cancancel)
    IoAcquireCancelSpinLock(&oldirql);
  PKDEVICE_QUEUE_ENTRY p = KeRemoveDeviceQueue(&device->DeviceQueue));
  PIRP Irp = CONTAINING_RECORD(p, IRP, Tail.Overlay.DeviceQueueEntry);
  device->CurrentIrp = Irp;
  if (cancancel)
    IoReleaseCancelSpinLock(oldirql);
  device->DriverObject->DriverStartIo(device, Irp);
}

BOOLEAN IoCancelIrp(PIRP Irp)
{
  IoAcquireCancelSpinLock(&Irp->CancelIrql);
  Irp->Cancel = TRUE;
  PDRIVER_CANCEL cancel = IoSetCancelRoutine(Irp, NULL);
  if (cancel)
  {
    (*cancel)(device, Irp);
    return TRUE;
  }
  IoReleaseCancelSpinLock(&Irp->CancelIrql);
  return FALSE;
}
以上是windows系统的一些伪源码。

startio例程:
VOID
  HelloDDKStartIO(
    IN PDEVICE_OBJECT  DeviceObject,
    IN PIRP  Irp
    )
{
        KIRQL oldirql;
        KdPrint(("Enter HelloDDKStartIO\n"));

        //获取cancel自旋锁
        IoAcquireCancelSpinLock(&oldirql);
        if (Irp!=DeviceObject->CurrentIrp||Irp->Cancel)
        {
                //如果当前有正在处理的IRP,则简单的入队列,并直接返回
                //入队列的工作由系统完成,在StartIO中不用负责
                IoReleaseCancelSpinLock(oldirql);
                KdPrint(("Leave HelloDDKStartIO\n"));
                return;
        }else
        {
                //由于正在处理该IRP,所以不允许调用取消例程
                //因此将此IRP的取消例程设置为NULL
                IoSetCancelRoutine(Irp,NULL);
                IoReleaseCancelSpinLock(oldirql);
        }

        KEVENT event;
        KeInitializeEvent(&event,NotificationEvent,FALSE);

        //等3秒
        LARGE_INTEGER timeout;
        timeout.QuadPart = -3*1000*1000*10;

        //定义一个3秒的延时,主要是为了模拟该IRP操作需要大概3秒左右时间
        KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,&timeout);

        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;        // no bytes xfered
        IoCompleteRequest(Irp,IO_NO_INCREMENT);

        //在队列中读取一个IRP,并进行StartIo
        IoStartNextPacket(DeviceObject,TRUE);

        KdPrint(("Leave HelloDDKStartIO\n"));
}
irp取消例程:
VOID
OnCancelIRP(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
        KdPrint(("Enter CancelReadIRP\n"));

        if (Irp==DeviceObject->CurrentIrp)
        {
                //表明当前正在改由StartIo处理
                //但StartIo并没有获取cancel自旋锁之前
                //这时候需要
                KIRQL oldirql = Irp->CancelIrql;

                //释放Cancel自旋锁
                IoReleaseCancelSpinLock(Irp->CancelIrql);

                IoStartNextPacket(DeviceObject,TRUE);

                KeLowerIrql(oldirql);
        }else
        {
                //从设备队列中将该IRP抽取出来
                KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,&Irp->Tail.Overlay.DeviceQueueEntry);
                //释放Cancel自旋锁
                IoReleaseCancelSpinLock(Irp->CancelIrql);
        }

       
        //设置完成状态为STATUS_CANCELLED
        Irp->IoStatus.Status = STATUS_CANCELLED;
        Irp->IoStatus.Information = 0;        // bytes xfered
        IoCompleteRequest( Irp, IO_NO_INCREMENT );

        KdPrint(("Leave CancelReadIRP\n"));
}
问题:根据以上代码,当取消例程工作时若取消的是当前irp,即Irp==DeviceObject->CurrentIrp,那么就要调用IoStartNextPacket函数,而IoStartNextPacket函数又要调用StartI例程,两者彼此递归调用,取消例程便无法从IoStartNextPacket中返回,同时所有队列中的Irp也将被全部处理,这令我很为不解。请高手详为解释取消例程的这一工作过程并解释一下我的想法中的问题所在,多谢!

[课程]Linux pwn 探索篇!

收藏
免费 0
支持
分享
最新回复 (5)
雪    币: 83
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
这个例子中的CancelRoutine仅仅取消了当前的IRP,并且当剩余的IRP都处理完毕后,该取消例程才返回。
要注意的是CancelRoutine和StartIO是并行执行的。

驱动开发接触也不长,共同努力。
2010-10-9 14:51
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
低手路过无能为力
2010-10-9 14:51
0
雪    币: 693
活跃值: (108)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
您说的意思是取消例程调用了IoStartNextPacket例程,然后与StartIo例程彼此递归调用处理完所有的Irp再返回吗?本身就执行的有StartIo,再调用了IoStartNextPacket,等于又调用了一次StartIo,那不是多余吗?
我还是有些不太明白,请详细解释一下吧,多谢!
2010-10-9 15:56
0
雪    币: 83
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
这里有几种情况,需要分别分析,现在只分析2种就够了。
假定存在3个IRP,按顺序分别是IRP1,IRP2,IRP3
CASE 1)执行IRP1过程中,IRP2,IRP3已入队列,调用堆栈如下:
HelloDDKStartIO()   <----执行IRP1,其取消例程已经被清成NULL,因此不可取消。
IoStartPacket()
HelloDDKRead()
此时对IRP1的CancelIO调用无效。

CASE 2)执行IRP1完毕,准备执行IRP2,且IRP3已入队列,调用堆栈如下:
IoStartNextPacket()     <---执行完出对列的处理,但还未调用StartIO例程。
HelloDDKStartIO()
IoStartPacket()
HelloDDKRead()

此时另一条执行线,即Cancel例程执行线,其堆栈如下:
执行到此时,上面的IoStartNextPacket()继续调用HelloDDKStartIO函数,然后在其中因为Irp->Cancel条件满足而直接返回。
由于没有后续的IoStartNextPacket() 被调用,上面的执行线完毕,后续所有的IRP通过Cancel例程执行线来处理。当然这个Cancel例程也可以被另外一个Cancel例程打断。

IoStartNextPacket()
OnCancelIRP()   <---此时满足Irp==DeviceObject->CurrentIrp的条件。
IoCancelIrp()

楼主的疑问在于Case2的执行流程,不知道这样是否好理解点。
2010-10-9 16:29
0
雪    币: 693
活跃值: (108)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
十分感谢,解答的十分精彩详细,希望在以后还能多多的向您学习和交流。
2010-10-9 16:37
0
游客
登录 | 注册 方可回帖
返回
//