文件系统内部结构(7)

13.4.8  "Read(读)"系统调用

"Read(读取)"系统调用格式如下

number= Read (fd, buffer, count),其中:

fd是文件描述符。buffer表示要读取的数据在内存中的起始地址。count表示要读的字节数。number表示执行该系统调用后实际读取的字节数。如果该系统调用失败,或是在读取所有字节前就到了文件结尾,那么该数值和"count" 字段中的参数值不同(否则,二者相同)。

应该注意:对文件而言,读操作是从该文件中对应模式(也就是读模式)下保存在FT条目中的偏移量开始的。内核按照以下步骤执行该系统调用:

(1) 内核将fd作为访问UFDT中正确条目的索引使用。

(2) 沿着由UFDT指向FT,再指向IT的指针访问正确的IT条目。诚如所见,内核在按照特定模式打开文件时设置所有这些条目。

(3) 内核验证用户对哪个进程提交了对该文件具有"读取"权限的系统调用。索引节点对所有这三类都包括读、写、执行权限。对用户类别而言,内核为用户设置访问权限。如果该用户被拒绝,那么就提示错误消息,并退出。前面已经研究过这部分内容,这里不再介绍。

(4) 现在,内核设置u区中的不同字段,从而消除将它们作为函数参数传递的必要性。这些参数如下:

模式(mode):读或写(本例中是读)

偏移量(offset):文件中从FT条目中开始偏移的字节

地址(address):目标内存起始地址(和系统调用参数一起提供)

指示器(indicator):指明目标内存在用户内存空间还是在内核内存空间

计数(count):要读取的字节数(和系统调用参数一起提供)

除了上述参数之外,几乎所有字段都是由系统调用的参数得到的。模式要从"读取"系统调用中得到,而偏移量要从FT中的条目得到。

(5) 内核现在锁定IT中的索引节点条目。对该文件进行的每个操作(写、关闭等)而言,内核都要查询IT中的该索引节点条目。因此,通过将该条目状态改为"被锁定",实际上任何进程对该文件的所有后续操作都会被挂起,这就确保了完整性和一致性。

(6) 现在内核读取请求的全部字节。除非在系统调用执行期间出现错误或是提前到了文件结尾处,否则就要读取全部的字节。该过程逐块地执行如下步骤:

内核将字节偏移量(也就是相对字节号RBN)转换成逻辑块号(LBN)以及LBN中的偏移量。

内核通过查询核心条目,将这个LBN转换成物理块号(PBN)。如果偏移量在前面10个逻辑块中(也就是偏移量小于10240),那么内核通过查询索引节点自身就可以得到PBN;否则要查询不同间接级别的索引节点。

内核将PBN转换成物理地址,并命令设备驱动程序先读取系统缓冲区中期望的块。该工作由DMA完成。

内核选择相关字节(取决于LBN中的偏移量)。对连续块而言,要选择大小为1024字节的整个块。

只要期望的字节小于等于"计数"字段中的数据,内核就会将期望的字节从系统缓冲区传送到内存缓冲区。

内核递增文件偏移量,并按照实际读取或传送的字节数递减计数字段。它还递增内存中目标起始地址,同时以相同的量递增"number"字段。

内核重复该过程,直到读取完毕或是出现错误或满足文件结束条件为止。

传送完所有期望的字节以及对应的"number"字段被更新后,内核输出这个"number"字段。该字段现在提供了实际读取的字节数。这个字段对于从读操作未完成的地方得到线程进行错误恢复很有用。

在读操作结束后,内核解锁IT中的索引节点条目。

问题在于:为什么在执行这个操作期间要锁定IT中的索引节点这个条目?原因在于确保一致性。进程可以调用一个"读取"系统调用,然后在这之间进入睡眠状态。如果允许另一个进程在第一个进程睡眠期间修改这个文件,那么"读取"系统调用得到的结果就不一致了。例如,可以有一个读取3个块或3072字节的系统调用。然而,已经知道实际的读操作是逐块进行的,整个系统调用并不是不可分的指令,因为实现这种不可分性,很难而且代价很大。

因此,该进程读完第一个块之后可能会睡眠,另一个进程可能会改变第二个块。这会产生不希望出现的不一致性。这就是为什么在适当的时候要将IT中索引节点条目锁定和解锁的原因。IT中"状态"字段有助于内核实现这样的处理工作。

同一个进程有可能打开同一个文件进行多次读取,通过不同的文件描述符读取该文件。这两个读操作通过两个不同的FT条目分别处理。

posted @ 2012-03-17 22:47  CobbLiu  阅读(288)  评论(0编辑  收藏  举报