本文引用代码及图片均来自 李治军: 操作系统32讲
目录树
在 操作系统 (14) 磁盘的使用 中提到文件在用户眼中是文本、图像和视频等形式,在操作系统看来则是磁盘中盘块的集合
,对文件的所有操作最终都转化为对磁盘扇区
的操作。为了方便用户对磁盘进行操作,操作系统抽象
出文件的概念,为文本、图像、设备等建立统一的文件视图
使用文件首先得打开文件,在用户眼中
文件在磁盘有序存放,只需给出路径+文件名
即可打开文件
file = open('/var/temp/test.txt')
而实际上
磁盘只是存放了一堆盘块,操作系统需要找到文件所对应的盘块集合。操作系统使用inode
记录文件内容和物理盘块的对应关系
,因此只需找到文件inode
即可。系统中有大量文件因此也会有大量inode
,为了高效查找inode
,需要将其组织成一定的形式,对于查找来说树状结构
无疑是一个合适的选择,因此也就诞生了所谓的目录树
目录树是一棵节点为inode
的树,系统打开文件主要就是在目录树中找到对应的inode节点
值得一提的是,目录也是一种文件
(linux中一切皆文件)也有inode
,它也
位于目录树中。不过与普通文件不同的是,目录文件中记录的不是文件内容而是目录项
,目录项记录它所包含的文件或目录的名称
以及对应的inode地址
查找目录树时从根目录
入手,遍历根目录的目录项
找到第一层目录的文件名以及对应的inode
地址,然后取出inode
并在其中进行第二层目录的查找,依此类推直到最后找到文件
实际上磁盘按照一定的格式
组织所有的盘块,如上图中磁盘将一部分盘块用于存储所有文件的inode组成的数组
,一部分盘块用于存储文件内容
根目录的inode固定
在数组第一个
位置,可是数组的第一个位置又在哪里呢?当然会有某个地方记录它,接着便引出下面的存储结构:
引导块
,通常用于计算机启动时引导系统,可以参考 操作系统(1) 系统的启动
超级块
,超级块存放了整个磁盘的全局信息
包括文件格式、磁盘大小、各部分大小等,这些信息可以描述整个文件系统,其中i节点数组的起始位置
就存放在超级块中。超级块的内容会被加载进内存
且随着磁盘文件的创建、删除等操作而更新,内容会被定期
更新回磁盘。因为超级块的内容关乎整个文件系统,所以一般都在磁盘中有备份
i节点位图和盘块位图
,用于记录数组中inode节点
的空闲情况和盘块
的空闲情况,比如某个盘块空闲那么对应的盘块位图的那一个bit
就是0,否则为1
i节点数组和盘块区
就如前面所提到的那样,不再赘述,前面提到的目录项记录inode地址
其实就是记录了inode在数组中的索引
我们把硬盘拔下来接入到另一台计算机,系统能够识别出磁盘并使用其中的文件就是因为磁盘总是按照一定的格式来组织盘块的,比如我们格式化硬盘的时候可以选择盘块大小,文件系统(NTFS,EXT4等)等设定,然后系统就根据设定在磁盘中构造上面提到的各种块
最后,在补上了打开文件的过程之后,结合上一篇文章来看,用户从打开文件到最后读写磁盘扇区的流程可以总结为下图:
代码实现
看看open
的代码实现中是如何解析目录
找到inode
open
一路往下最终调用get_dir
获取inode
get_dir
中先判断是绝对路径
或相对路径
并获取根目录或当前目录的inode
,然后从获取的inode
开始往下找。find_entry
用于获取inode
中保存的目录项
,inr
保存目录项中下层目录或文件的inode在数组中的索引
,然后使用iget
获取下一层目录或文件对应的inode
,获得inode
后重新开始循环直到
找到目标文件
get_dir
的PCB
中为什么直接可以获取到根目录的inode
呢?可以参考 操作系统 (14) 磁盘的使用 ,其实也是从init
进程中继承
来的
find_entry
首先在inode
的直接索引块
(直接记录了逻辑盘块对应哪个物理盘块)里找目录项
,如果没找到就会到一级索引块
甚至二级索引块
中找直到找到和目录或文件名对应的目录项
iget
首先获取一块空闲空间用于将磁盘中的inode
数据读入内存
,然后调用read_inode
读取数据,read_inode
先获取超级块
中的数据,计算出目录项中索引对应的inode的位置
并获取inode
上图中2
是引导块
和超级块
占用的盘块数,然后加上inode位图
和盘块位图
的长度,再换算每个盘块存几个inode
之类的数据得出应该读取哪个盘块
,读取出盘块后因为每个盘块保存多个inode
,还得根据索引
再计算一次得出目标inode的具体位置
才能获得正确的inode
至此,open如何解析目录获取文件inode的流程便清晰了