1.存储设备驱动程序的加载
文件系统驱动程序加载完毕,IOManager会进一步加载存储设备驱动程序。下面以硬盘驱动程序为例说明存储设备的加载过程。与任何设备驱动程序一样,存储设备驱动程序必须输出一个DriverEntry函数,这个函数被IOManager调用。当然,在调用该函数之前,IOManager首先应该创建一个设备驱动程序对象,然后以该对象为参数,调用硬盘驱动程序的DriverEntry函数。下面是硬盘驱动程序的DriverEntry函数源代码:
在上述代码中,调用了CreateDevice函数,创建了物理磁盘设备对象。在创建这个设备对象时,指定了设备的属性为DEVICE_TYPE_STORAGE和DEVICE_TYPE_HARDDISK。其中第一个属性(DEVICE_TYPE_STORAGE)非常重要,这会导致IOManager调用文件系统设备对象的CheckPartition函数,对这个存储设备进行检查,以确定其是否为对应的文件系统能够识别的文件卷。
InitPartitions函数对磁盘的分区进行分析。这个函数遍历磁盘MBR中的分区表,对每个可识别的分区,创建一个分区设备对象。这个函数比较长,我们只把创建分区设备对象相关的代码看一下,因为这与文件系统关系最密切:
InitPartitions函数依次检查四个分区表项,对任何记录了合法分区的表项,该函数会创建一个分区设备对象(设备对象属性中包含DEVICE_TYPE_PARTITION属性)。当然,在创建设备对象之前,首先创建设备对象的设备扩展,用于记录分区特定的信息。需要注意的是,这个函数支持扩展分区的处理。
至此,硬盘的设备驱动程序加载过程介绍完了。设备驱动程序加载过程中的最关键动作,就是分析硬盘本身以及硬盘分区情况,并分别创建对应的设备对象。在创建设备对象的时候,需要指定对象的属性(DEVICE_TYPE_STORAGE或DEVICE_TYPE_PARTITION)。这非常重要,会直接驱动IOManager做进一步的检查。
为了帮助读者进一步理解这个过程,我们举一个简单例子。假设一个硬盘,上面有两个分区,一个分区为FAT32,另外一个分区类型为NTFS。这样在硬盘驱动程序加载完成之后,会创建下列三个设备对象:
(1)物理硬盘设备对象,名字为“\\.\PHYSICALHARDDISK0”。
(2)第一个分区的设备对象,名字为“\\.\PARTITION0”。
(3)第二个分区的设备对象,名字为“\\.\PARTITION1”。
这三个设备对象,都指向相同的设备驱动程序对象。此时,我们即可调用CreateFile函数,指定上述任何一个名字作为对象,来打开对应的设备进行操作。比如可通过调用ReadFile,直接读取物理硬盘或分区上的内容。这时候的读取,是不考虑文件系统的存在的,只是把整个硬盘或者分区当作顺序存放的字节数组来处理。(www.xing528.com)
在Hello China V1.75的实现中,devlist程序(字符模式下,输入devlist并按Enter键)可显示系统中所有的存储设备对象和分区对象。图12-5显示了系统中所有的物理设备对象。
图12-5 物理设备对象及其属性
其中的“\\.\PHYSICALHARDDISK0”即是硬盘设备对象。这个硬盘只有一个分区,对应于“\\.\PARTITION0”设备对象。
2.存储设备的读/写
上面说过,通过调用ReadFile直接读取磁盘或分区设备是可以的。但是这个函数只能按照顺序对磁盘进行读/写。比如第一次调用ReadFile,读取1KB的内容。当第二次调用的时候,读取的内容是从1KB处开始的。这样显然缺乏灵活性。在文件系统的实现中,需要通过一种灵活的方式,在磁盘或分区上“跳跃”读取或写入,ReadFile/WriteFile是难以胜任的,我们需要另外的途径解决这个需求。
第10章讲到,设备驱动程序的DeviceCtrl函数是一个非常灵活的函数,通过这个函数可以实现常规操作无法胜任的功能。在存储设备驱动程序中,我们也是通过这个函数,实现了磁盘扇区的随机读取和写入功能。我们先通过一段代码,来理解一下如何调用扇区的随机访问功能。下面这个函数,用于从一个分区设备(用分区设备对象标识)上读取一个或几个扇区,这个函数被文件系统实现代码调用(为了解释方便,做了删减):
上述函数比较简单,首先申请一个DRCB对象,然后初始化这个对象。需要注意的是,把dwCtrlCommand初始化为IOCONTROL_READ_SECTOR,这是一个预定义的常量,表示要调用磁盘驱动程序的扇区随机读取功能。
准备好DRCB对象之后,然后就以分区设备对象、DRCB等为参数,调用对应驱动程序对象的DeviceCtrl函数。函数成功返回之后,pBuffer里面就存放了读取的磁盘数据。
DeviceCtrl是磁盘驱动程序实现的,这个函数分析DRCB对象的参数,然后根据参数进一步调用扇区读取函数__CtrlSectorRead。这是该函数的实现代码:
在当前的实现中,ReadSector函数是通过BIOS调用实现的。这个函数首先切换回实模式,然后调用int 0x13中断调用,完成磁盘读取,然后返回到保护模式。当然,也可以使用直接硬盘控制器读/写的方法来实现ReadSector函数(在最初版本中,就是直接实现的),但是在很多非IDE接口的硬盘上会出问题,所以使用BIOS调用方式更加保险。这个函数的实现非常简单,其代码位于[/kernel/arch/bios.cpp]文件中,读者可自行阅读。
对分区的随机写入功能,实现方法与此类似,在此不作赘述。分区的随机读取和写入功能,是实现文件系统的基础。在文件系统的实现代码中,把这两项功能分别封装为ReadDeviceSector和WriteDeviceSector两个函数,以方便程序编写。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。