阅读:3707回复:8
若干关于 file system driver stack
写这个文章的初衷是想知道究竟一个读写文件的irp都是怎样被处理的.....
大家都知道这样的一个读写文件irp是发送给file system的driver的 file system把这个irp交给了下层的device 这个device叫logical volume device,它由device的vbp里面的realdevice指针指出(不一定就会是这个device,而应该是这个device所在的stack的最上层的device).那这个device是个什么东西呢?它是怎么来的呢?按照道理,这个irp应该被发送到disk的驱动去才对?那个这个device是disk的驱动创建的么?如果不是,那中间都有些什么步骤呢? 本文就是回答上面这些问题的. 用softice看看就知道file system转发的irp并不是vpb指向的那个device,而是另外一个叫volsnap的driver创建的device. 再跟踪看看,volsnap创建的device是跟vpb的device在同一个stack里面,而且volsnap创建的device的下一个device就是vpb的device. 好了.至少知道了确实file system driver要把irp转发到vpb指向的device去.那接下来呢?irp又会到哪里? 再跟踪下去.irp到了一个由partmgr.sys创建的device里,看看这个partmgr...它是一个disk driver 的filter(多用注册表,当初我在这里花了好长时间才发现这个东西是一个filter),所以partmgr把这个irp转发到了disk driver.disk driver接着发给了acpi.sys,然后acpi.sys转发给了atapi.sys创建的一个device去,看看这个device的stack,已经是1了,irp就到尽头了,接下来的就不是irp的流程了,而是所谓的port/miniport流程了. 大致的把整个的结构过了一次,里面有很多的疑问,下面要一一解释,最好是能用device tree看看这个过程,看看哪些是pdo哪些是fdo,看看每个device的stack值都是多少,自己模范下看看能不能了解irp的流程. 上面的整个irp的流程是很大的.顶层device的stack达到了8,首先你就会奇怪这么庞大一个device stack是怎么建立起来的? 先说最上面的部分 file system在受到一个mount的fs control的时候,创建了一个无名的device,并且保存了vpb 的 realdevice所在的stack的最上层的device,以后所有的irp都转发到这个保存的指针上面.注意这里并没有attach到那个device的stack上面,而是直接使用指针call driver的.这里会有一个问题,在file system mount以后,再attach到volume device的stack上的device 收不到 file system传递过来的irp,至于这个问题怎么解决,msdn里面有说,把这个filter driver设置成启动时加载,至于为什么这样作了就能行,这个在后面会有解释. 在我的机器上,file system保存的device是由volsnap创建的一个filter device,在这个filter device下面是vbp指向的device,它有名字叫HarddiskVolume1(名字不一定就是这个),这个device是一个pdo,注意它是一个pdo,按照常规 pdo的创建不是由系统主导的,而是由driver主导了,它一般不在adddevice里面创建,而是由driver在需要的时候手动创建的.为什么说到这个细节呢?因为我们没有这个driver(名字叫ftdisk.sys)的源代码,在反汇编的时候,明白了这个以后比较有目标,这个ftdisk.sys的代码是非常简单的,ida反汇编一下就发现harddiskvolumeX这个device并没有attach到什么stack上面(也算是pdo的一般做法),但是这个pdo却保存了两个指针,一个指向另外一个pdo(70h),一个(74h)指向这个pdo的stack的最顶的device,而且以后的irp它都发给了这个device,这个device也就是由partmgr创建的device了. partmgr其实只是一个filter,挂接到disk driver上面的,那么接下来的stack创建就比较简单了.用device tree向前看,partmgr创建的是一个filter,它attach的device叫dr0,是一个fdo,它对应的pdo叫idedevicepotolo-3,这个部分的过程就根据的容易了,disk driver是有源代码的,仔细看看就能明白其中的创建关系.看清楚了,这个pdo的stack值是1,所以irp到这里就算是尽头了. 再向下又是一个fdo,ideport(atapi.sys),它创建了上面的那个pdo,ideport我没有详细的反汇编分析,不过可以推测得出来,它对应得pdo是ide0channal0,而这个pdo又是由pciide创建的,你可能会注意到中间多了一个filter acpi.sys创建的一个device,也许你会注意到这个acpi.sys到处创建filter. 再往下就是pci,acpi_hal这些device了,他们的行为都很正常.不详细说了(其实是我没有详细的研究,只有些想当然的想法,不敢拿出来说) 到此,回顾下整个device stack,可以看到这里涉及到好多的device stack,irp并没有按照一个stack 走到底. 最上面的是file system的device |fs filter| <-比如是filemon |fs volume| <-一般没有名字,由filesystem在mount的时候创建 fs的volume保存有新的stack(partmgr device的stack? 叫这个名字么?)的最上层device的指针,irp转移到这个stack |volsnap 创建的无名device | |ftcontrol创建的storage volume device| 这个device有保存有另外一个stack顶层device的指针,irp再次转移 |partmgr创建的无名filter device| |disk创建的DR0 fdo | |acpi创建的filter | |atapi创建的pdo | 这个就是irp的全流程,它经历了3个device stack. 呼呼 吐口气吧....... 本来这个文章写到这里就算完成了.... 但是我还有些心得要写出来.这些属于是无心插柳的结果.只是在我跟踪上面这些结果的时候额外的收获. 先看device tree里面,pnp方式查看,你会注意到有很多的pdo属于pnpmanager,但是他们却没有对应的fdo.很奇怪吧,再看这些pdo的名字,你会发现partmgr赫然在目.奇怪了,它怎么有个fdo呢? 首先要明确的就是凡是有填充adddevice域的driver都会对应一个pdo,这个是必然的,因为adddevice传递进来的参数就有一个是pdo,还有一个事情就是在某种情况下,即使不填充adddevice域,pnpmanager还是会创建一个pdo.你所看到的大多少没有fdo的pdo都是这样来的.那什么时候pnpmanager创建这些pdo呢?答案就在系统启动的时候. 你也应该要知道,有些驱动并不是由ntoskrnl加载的,有很多的driver是由loader加载的,win2000 把这些叫做 boot driver. ntldr加载必要的driver以后,转移控制权给ntoskrnl的入口函数(kiinitsystem是这样么?),经过一定的步骤以后来到了ioinitsystem函数,这个函数作一些初始化以后开始了pnp系统,这里ntoskrnl创建一个pnpmanager的root device,并且start了这个device,然后发送query bus relation,这个时候pnp manager读取注册表一一创建每个需要创建的service的pdo,构造device tree,然后调用每个boot driver的driver entry函数,然后是他的add device函数(实际情况比我上面描述的要复杂得多). 然后pnp就开始了众所周知的enum 工作,这个是委托给worker线程完成的,大部分情况是使用workitem进行的,这个部分显得非常的复杂,跟踪调试非常的不方便(恕我也没有完全的弄明白,先跳过去,以后有机会再详细的讨论这个部分). 跳过了xxx的步骤以后,pnp enum到了主板的南桥芯片(我的是ich4)设备,创建对应的fdo(没有名字,属于pci这个driver),继续enum,得到ich4里面的mass storage controller,创建pdo也是属于pci的,创建对应的fdo,pciide(中间有个acpi的filter加进来,主要进行电源管理),这个fdo再enum,创建两个ide channal的pdo,再创建对应的fdo,ideport(由atapi提供,还是有acpi的filter加进来),ideport再enum机器上的硬盘pdo,继续加载acpi的filter,加载硬盘的fdo,也就是disk.sys了,再加载upperfilter,partmgr.sys. 接下来的事情关键了,硬盘的fdo告诉pnp它的bus relations要更新(可以查看disk.sys的源代码观察这个部分进行的事情),然后pnp发送一个 query bus relations的irp给硬盘pdo所在的stack,这个stack的最上部分并不是硬盘的pdo,也不是acpi的filter,也不是硬盘的fdo,而是partmgr.sys这个driver,它接受到这个irp的时候,安装一个complete routing,然后把irp向下传递,等到控制权再回到手上的时候,partmgr.sys作了一系列的事情,最最重要的就是它发送了一个IO Control irp到另外的一个driver. 接手这个irp的是ftdisk.sys这个driver(指basic volume的情况,如果是dynamic volume的话呢,接受的是dmio.sys这个driver),这个driver然后创建自己的pdo,并且把这个pdo关联(不是attach)到了partmgr.sys所在的stack,实现了上面两个stack之间的关联.这个pdo也加载了自己的fdo,一个叫volsnap.sys提供. 这个irp完成以后,partmgr完成原来的query bus relations irp,可以看到disk.sys创建了若干个pdo,attach到了自己的fdo上面,再这以后,disk所在的stack的top device就不是partmgr.sys了,而是最后一个由disk创建的pdo.而partmgr在系统里面注册了通知消息,它能在以后的volume change的时候得到通知,然后会发送相应的io control irp到ftdisk,然后ftdisk完成更新. 接下来事情又变得简单了,file system driver加载,在遇到第一次访问磁盘的时候,mount到由ftdisk创建的pdo上面去,再次实现两个stack的关联. 上面就是整个过程的大概流程. 明白了这个过程,要实现一个虚拟xx的就容易了. 说说daemon-tools一类的实现方式吧. 他们基本都提供一个bus driver(d343bus.sys),作为一个 boot driver 由ntldr加载到内存,ntoskrnl在适当的时候会调用它的driverentry以及adddevice函数,这两个函数里面分别加载了fdo和创建了自己的pdo,这个pdo的inf文件里面表示它要加载的driver是一个叫d343port.sys的driver,这个driver再在scsiport的帮助下create了一个pdo.这个pdo返回的id很有趣,SCSI\\CDRom,SCSI\\RAW,这样的话,windows就把它当作了一个cdrom来处理.再向上的fdo,filter都交给windows来操作了. 如果是一个虚拟硬盘呢?你马上就反映过来了,报告pdo的id为Gendisk一类的就ok. 当然实际的实现远不是这么简单的,bus driver,mini port driver都有很多要注意的地方. (以上大部分属于想象推测,没有经过严格的认证,有错的地方请包涵). 到这里基本上这个文章又结束了. 也许有人要问这些东西是怎么来的?当然是用soft ice + 2000代码 + ida来的了,唯一要修改的就是要让softice在所有的外部driver的driverentry调用前正常工作.这个方法在驱网的论坛上能找到 http://www.driverdevelop.com/forum/viewthread.php?tid=66643 [编辑 - 5/28/04 by tiamo] |
|
沙发#
发布于:2004-05-28 19:20
不错!
|
|
板凳#
发布于:2004-05-28 19:30
什么叫两个stack之间的关联????
|
|
|
地板#
发布于:2004-09-12 15:28
楼主为何不做creaker??
|
|
地下室#
发布于:2004-09-12 21:31
不明白楼上那个单词得意思.....
|
|
5楼#
发布于:2004-09-13 09:04
不明白楼上那个单词得意思..... 估计是craker的意思,建议你去做些破坏工作,嘿嘿...... |
|
|
6楼#
发布于:2004-09-13 15:40
这几天怎么了.....
偶的老帖子怎么都给翻上来了..... |
|
7楼#
发布于:2004-09-14 13:25
人出名,猪变壮,呵呵
|
|
8楼#
发布于:2004-09-22 09:11
楼主呀,我问个问题可以吗?
我的问题是有关在Drvier层写公共接口的问题 1、如何定义Driver层的公共接口函数 2、如何注册 3、如何使用这个注册好了的公共函数 希望楼主不要嫌问题采才好。 |
|