在Hello China当前版本的实现中,把文件跟普通的设备同等对待。即每打开一个文件,在操作系统内部都会增加一个设备对象(文件设备对象);对于文件的读写等操作,直接调用设备对应的驱动程序提供的DeviceRead和DeviceWrite等服务函数。
CreateFile是操作系统提供给用户的通用设备打开接口函数,这个函数可用于打开普通的设备进行操作,也可用于打开文件系统中的任何文件。该函数原型如下:
其中,第一个参数lpThis是一个指向IOManager全局对象的指针;第二个参数lpszFileName,则指明了要打开的设备或文件的名称;dwAccessMode和dwOperationMode两个参数用于控制打开的文件,不适用于目标对象是物理设备的情形;最后一个参数保留,用于将来使用。不同的命名规则,用来标识打开的目标对象是设备还是文件。对于设备,lpszFileName的形式遵循\\.\devname的规则,即开始是两个反向斜线,接着一个点号(或DEV字符串),后面再跟着一个反向斜线,最后是设备的名字(即设备标识字符串);而对于文件,则按照“文件卷标识符+文件路径+文件名”的格式,比如“C:\HCN\DATA1.BIN”。
用户通过调用CreateFile函数来打开一个文件,该函数按下列步骤进行操作:
(1)首先根据文件名以及命名规范,来确定请求打开的对象是文件还是普通的设备对象。
(2)如果判断结果是普通的设备对象,则转到设备对象打开流程。这个过程将遍历设备对象链表,根据名字匹配设备对象。详细过程请参考第10章。
(3)如果判断结果是文件,则IOManager首先遍历系统中已经打开的设备链表,用文件名来匹配每个设备名字。如果匹配成功,则说明该文件已经打开,于是进一步检查该文件的打开方式(先前已经打开的方式),如果允许按照本次请求的打开方式重复打开,则直接给用户返回已经打开的文件对象的地址,并增加引用计数。如果不允许重复打开,则返回失败标志。
(4)如果遍历完设备对象链表后没有找到对应的文件,则说明该文件没有被打开,于是从文件名中提取文件卷标识符(比如,C:,D:,E:等)。
(5)根据文件卷标识符遍历IOManager的FsArray数组,这个数组存放了系统中所有正确识别的文件卷。一旦找到一个数组元素,即可从数组元素中进一步得到文件卷设备对象。
(6)如果找不到对应的文件卷设备对象,则说明给出的文件名有问题。比如当前系统中只有C:、D:两个文件卷,但是用户却尝试打开“E:\DATA.TXT”文件,则会出现这种情况。显然,这只能以失败返回。
(7)如果可以找到对应的文件卷设备对象,则IOManager调用文件卷设备对象的DeviceOpen函数(以文件名或DRCB为参数),尝试打开该文件。
(8)如果DeviceOpen函数执行成功,则返回被打开文件的句柄,否则返回一个失败标志(NULL)。(www.xing528.com)
(9)CreateFile根据DeviceOpen函数的返回结果,给初始调用返回适当的数值。
文件驱动程序的DeviceOpen函数,会从分区的根目录开始,根据文件名进行逐级搜索。一旦定位到文件所在目录,即可读取目录文件信息,获得文件的相关信息,如文件名、文件起始cluster号、文件大小等。然后创建一个文件扩展对象(__FAT32_FILE),把文件相关的信息存放在这个对象中。接下来需要调用CreateDevice函数,创建一个文件设备对象,并把文件扩展对象作为文件设备对象的设备扩展。
下面是FAT32文件系统的实现中,文件扩展对象(__FAT32_FILE)的定义:
显然,这是与特定文件相关的信息。最后两个指针pNext和pPrev,把文件扩展对象链接成一个双向链表。需要注意的是,这个双向链表与文件设备对象所在的设备链表不同。pPartition对象指针指向了文件所在的分区。而pFileSystem指针指向该文件所在的文件系统设备扩展对象。文件系统设备扩展对象记录了文件卷相关的特定信息,这是文件卷设备对象的设备扩展。而系统中的所有文件卷对象都统一存放在IOManager的FsArray数组中。图12-6示意了这些对象之间的逻辑关系。
图12-6 文件系统核心对象之间的关系
在我们的实现中,每打开一个文件,DeviceOpen函数便会创建一个文件设备对象。这个对象与普通的设备对象一样,被连接到系统的设备对象链表中(IOManager的lpDevice Object成员指向这个链表)。针对系统中每个成功识别的分区,都会有一个与之对应的文件卷设备对象。需要注意的是,文件卷设备对象也是一个设备对象,也会被链入设备链表中,这在图中没有体现。文件卷设备对象是与一个特定分区连接在一起的,其设备扩展—文件卷扩展,则包含了分区(文件卷)特定的信息。下面是文件卷扩展对象的定义:
这个对象记录了文件卷相关的信息。pPartition指向该卷所在的分区对象,这个对象与文件对象扩展中指向的分区对象是同一个对象。在读取文件卷中的数据的时候,最终都是转化为以pPartition对象为参数调用DeviceReadSector函数的。DeviceReadSector函数进一步调用了pPartition对象驱动程序提供的DeviceCtrl函数,这个函数提供了扇区随机读写功能。
需要注意的是,不论是文件设备对象,还是文件卷设备对象,都指向相同的驱动程序对象。但是在调用DeviceOpen函数的时候,是以文件卷设备对象及其指向的驱动程序对象为参数的。因为这时候具体的文件还未打开。而在调用DeviceRead/DeviceWrite等函数时,则是以具体的文件设备对象和其指向的设备驱动程序对象为参数的,这时文件已经打开。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。