设备驱动与文件系统:05 文件使用磁盘的实现
从文件使用磁盘的实现逻辑分享
我们现在讲第30讲,内容是文件使用磁盘的具体实现,也就是相关代码是如何编写的。上一节我们探讨了如何从字符流位置算出盘块号,这是文件操作磁盘的核心。而这节课,我们将深入研究实现这一核心功能的实际代码。
一、文件读写磁盘的接口与流程
通过文件来使用磁盘,最常用的操作就是读写,以write
操作为例。我们知道,write
的格式需要传递三个参数:第一个是文件描述符fd
,用于指定操作的文件;第二个是内存缓冲区,用于存储要写入文件的数据或读取文件数据的存储区域;第三个是读写字符的个数。从这个接口可以看出,用户在使用时接触到的是字符流相关的概念,并没有直接涉及盘块号,但在系统内部,必然会将这些字符流信息换算成盘块号,因为磁盘实际读写操作关注的是扇区,需要根据盘块号进一步算出扇区。
在实际操作中,write
到了内核中就变成sys_write
,这是系统调用的基本知识。sys_write
首先要获取inode
,inode
本质上就是FCB
(文件控制块),获取它就相当于掌握了文件的关键信息。接下来,要根据FCB
以及读写的字符流位置,算出对应的盘块号,得到盘块号后,就可以进行读写操作了。
进一步跟踪代码,file_write
函数在这个过程中发挥重要作用,它需要接收inode
、file
、内存缓冲区以及读写长度等参数。这里需要注意的是,字符流中的读写位置信息其实存储在file
中。
二、字符流读写位置的确定
file
中有一个f_pos
指针,它就是我们所说的读写指针。如果大家编写过较多的C程序并操作过文件,就会对文件读写指针比较熟悉,常用的fseek
函数就是用来调整这个指针的。在文件刚打开时,读写指针指向第0个位置,随着不断读写,指针会向后移动,这也就形成了我们所说的字符流。
如果是追加写入,pos
会被设置为inode
中的i_size
,也就是文件的大小,这样就相当于把写入位置放在了文件末尾;如果不是追加写入,pos
则会取出上一次读写的位置,继续往后读写。这样,我们就得到了字符流中的读写位置,再结合count
参数,就能确定具体的读写范围。
三、根据读写位置计算盘块号
得到字符流中的读写位置后,接下来关键的一步就是根据读写位置找到盘块号,这是文件抽象的核心,inode
在其中发挥着重要作用。计算盘块号时,需要用读写位置除以盘块的尺寸,得到对应的逻辑块号。
在Linux 0.11系统中,文件存储采用了直接块和索引块的方式。如果f_block
小于7,说明是直接块,此时可以直接从inode
的i_block
中获取对应的盘块号,因为要写入新内容,所以还需要先申请一个空闲块,并将其记录在i_block
中;如果f_block
大于等于7且小于512,则进入一阶索引阶段,需要先将一阶索引块读进来,再从索引块中找到对应的盘块号;如果涉及更复杂的情况,还可能会有二阶索引等。
四、向磁盘发出读写请求
一旦确定了盘块号,就可以通过bread
函数(如果是写操作则类似流程)进一步处理。bread
函数会根据盘块号算出扇区号(由于一个盘块对应两个扇区,所以盘块号左移一位得到扇区号),然后结合内存缓冲区信息,形成一个请求,并将其放入电梯队列。放入电梯队列后,当前操作会阻塞等待,直到磁盘中断发生,从中取出请求,算出CHS
(柱面号、磁头号、扇区号),再通过out
指令发送到磁盘控制器,从而完成磁盘的读写操作。
在读写完成后,pos
指针需要根据实际读写的字符数进行增加,以保证下次读写能够从正确的位置开始,这部分操作和计算盘块号的过程共同构成了文件读写的核心逻辑。
五、inode
的双重角色与文件视图
讲到这里,关于从文件到盘块的映射故事基本讲完了。但需要强调的是,inode
除了作为从文件对应到盘块号的映射表之外,它还是文件抽象的关键。我们之前讲过文件视图,在系统中,不仅普通的数据文件是文件,设备文件同样也是文件。对于普通文件,inode
中的映射表用于实现字符流位置到盘块号的映射;而对于设备文件,inode
则用来存放主设备号、次设备号等信息。
通过inode
的这种设计,系统形成了统一的文件视图。无论是读写普通磁盘文件,还是操作其他设备文件,用户看到的都是一个连续的字符流,而底层复杂的映射和处理过程都由操作系统完成。这也引出了我们下一节要讲的内容——打开文件,其实质就是从文件名到inode
的映射,这是整个文件系统操作的重要环节。
六、课程总结与实验任务
我们最终使用磁盘的基本流程是通过open
函数获取文件描述符fd
,然后使用read
或write
等函数基于fd
进行操作。今天我们详细讲解了在有了fd
之后,如何找到读写位置,并根据inode
找到盘块号来实现磁盘读写,整个过程涵盖了从文件名到inode
,再到盘块号,最后到磁盘实际操作的完整链条。
最后,给大家布置一个重要的实验任务,这也是操作系统课程八个实验中的最后一个。在Linux 0.11系统中原本没有proc
文件,我们需要通过实验创建类似功能,以深入体会文件视图的概念。proc
文件通常用于展示进程信息,这些信息存储在内核的task_struct
全局数组中。我们要做的就是将这些信息按照文件的方式提取并返回给用户,具体实现过程是当系统发现inode
表示的是proc
设备时,调用proc_read
函数,从task_struct
中取出信息存入buffer
,再传递给用户端内存,同时不要忘记修改pos
指针,以保证数据以统一的字符流形式呈现。再次强调,操作系统的学习离不开实践,只有动手编写代码、完成实验,才能真正掌握操作系统的原理和实现方法。