write by http://hi.baidu.com/weolar/blog/item...2dbb4bd4f.html
今天看到sudami同学学习起这些东西,好久没搞了,很生疏,所以重新学习了一下,有点小小心得(仅仅是心得,有不少错误,希望大家斧正 ):
在我的理解中,设备对象(drevobj)相当于驱动对象(drvobj)创建的子对象,用来形成设备链,从而接受、处理数据的。设备对象挂到设备链中,接受到了被设备管理器派遣的IRP时(记得某本书上好像说,没有真正所谓的设备管理器,只是一组派遣例程,如IopfCallDriver就是将IRP派遣的。通过hook这个函数能得到很多我们想要的东西),这时设备对象的母对象--驱动对象组建的IRP派遣函数,(即DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;形式的函数)将会接受IRP并处理。而设备堆栈则是每次IRP下发时辅助IRP找到相应派遣函数并存储一些可重用参数的地方。
废话说了这么多,那么我们平时访问、删除文件是怎么回事呢?比如我们删除一个文件,那么系统的流程就是:
(应用层)DeleteFileA -->
DeleteFileW -->
ntdll.ZwSetInformationFil->
(驱动层)NtSetInformationFile->
SrSetInformationFile->
NtfsNtSetInformationFile->
NtfsCommonSetInformationFile->
NtfsSetDispositionInfo ->
MmFlushImageSection ->
MiCleanSection
到了驱动层,windows就是通过IRP来传递了。上面的流程中,nt!NtSetInformationFile->SrSetInformationFile->NtfsNtSetInformationFile->NtfsCommonSetInformationFile 几个此时虽然也有IRP的传送,但都还是直接调用,不是IopfCallDriver的形式。而IRP起到传递参数的作用。它体内保持着将要下发的设备对象。现在来看接下来某个IRP(并非删除文件)在设备链中的传递:
nt!NtFlushBuffersFile->
nt!IopSynchronousServiceTail->
sr!SrPassThrough->
Ntfs!NtfsFsdFlushBuffers->
Ntfs!LfsFlushToLsn->
Ntfs!LfsFlushToLsnPriv->
Ntfs!LfsFlushLbcb->
Ntfs!LfsFlushLfcb->
sr!SrWrite->
Ntfs!NtfsCommonWrite->
Ntfs!NtfsNonCachedIo->
Ntfs!NtfsSingleAsync->
VolSnap!VolSnapWrite->
ftdisk!FtDiskReadWrite->
CLASSPNP!ClassReadWrite->
CLASSPNP!ServiceTransferRequest->
CLASSPNP!SubmitTransferPacket->
atapi!IdePortDispatch(\Driver\atapi \ IdeDeviceP0T0L0-3)->
atapi!IdePortInsertByKeyDeviceQueue->
atapi!StartIo Packet->
HAL->
Io 端口……也就是说:IRP是从Ntfs ->ftdisk (卷设备) -> class(classpnp )-> atapi -> hal -> IO的流向的。
那么\\.\Physical Drive%d 啊,\Device\Harddisk%d\Partition0啊~ 是表示什么呢?通过DeviceTree和winobj可以得知,
\\.\PhysicalDrive 只是Device\Harddisk%d\Partition0的符号连接,
而Device\Harddisk0\Partition0是\Device\Harddisk0\DR0的符号链接。
Device\Harddisk0\Partition1、2……则是Device\HarddiskVolume1、2的符号连接。
那么设备堆栈是怎么回事呢,原来从上到下是这样:
FS DRIVER--->>
Volsnap--->>>
ftdisk(Device\HarddiskVolume1)--->>
partmgr(Ipartmgr )--->>
disk(Device\Harddisk%d\Partition0\ DR0)--->>
acpi--->>>
atapi( IdeDeviceP0T0L0-3)
每层都是attached到上一层,所以形成设备链的。括号内表示驱动对象创建的设备对象,用来形成设备堆栈并接受IRP的。
(所以过滤驱动挂载到任意一层时遍能拦截到数据,这也是微软提倡的“HOOK”方式)
这也就对应了微软的说法,IRP是从文件系统->卷驱动 -> 磁盘驱动-> 类驱动-> 端口驱动-> 微端口驱动的流程的。
而Device\Harddisk%d\Partition0\ DR0则是disk驱动对象创建用来处理ClassReadWrite等IRP的设备对象了。我看了disk的IRP_MJ_READ确实是CLASSPNP!ClassReadWrite,这就可以接受为什么IRP到了disk这层反而到了classpnp中去处理IRP了。
另外,MJ前辈还说过:
1.文件系统设备站和storage设备站使用同一个irp,到了atapi,这个MINIPORT,就换成pkt中的另一个Irp,
2.如果中间没有人重新转发IRP(例如DISKF)的话,从FSD到ATAPI都是用的同一个IRP。IdePortDispatch 没有源代码,而classpnp是开源的(wdk就有),可参考classpnp中的ServiceTransferRequest和SetupReadWriteTransferPacket。
IRP再往下会到哪呢?原来通过在ATAPI中通过IdePortInsertByKeyDeviceQueue将IRP入队列,再往下就是ATAPI的StartIo 了。StartIo 再往下就没有IRP的概念了,便到达HAL(硬件抽象)层,再往下就是端口IO了!
下面是大米同学的图,画的很好,先引用一下^_^:
IRP |
|
| attach dev attach dev
| | |
ntfs/fat32.sys --> dev --> dev --> ... --> dev
| | |
| attach dev attach dev
| |
| ...
|
|
|
| ...
| |
partmgr.sys --> dev --> dev --> ... --> dev
| |
| ...
|
|
| dev ( \driver\partmgr)
| | |
disk.sys --> dev --> dev --> ... --> dev (DR0)
| |
|
| dev ( \driver\partmgr)
| |
| dev (DR0) (\driver\disk)
| | |
atapi.sys -> dev --> dev --> ... --> dev
|
|
...
另外AZY前辈也说过:
读写IRP->IdePortDispatch->IoStartPacket->IdePortStartIo,到这里分两支
有DMA能力的走: BmSetup->BmReceiveScatterGatherList->IdePortAllocateAccessToken->xxx
无DMA能力的走:IdePortAllocateAccessToken->CallIdeStartIoSynchronized->IdeStartIoSynchronized->AtapiStartIo->IO端口xxx
- 标 题:简单说点对文件系统、分层驱动、文件读写的理解
- 作 者:weolar
- 时 间:2009-04-03 22:30
- 链 接:http://bbs.pediy.com/showthread.php?t=85367