title: UNIX File System 实现
tags:
- Operating System
categories: - Operating System
- 随堂笔记
abbrlink: 48303
date: 2018-06-14 20:57:03
最后的大作业是实现一个文件系统,我以UNIX的文件系统为标准,实现了简单的文件系统和API接口。
本文的所有代码已经上传我的Github。
要求
- 设计一个文件系统,最多可以容纳
5000
个文件,设备容量为250MB
,单个文件最大容量为50MB
,提供目录树功能,文件名最长为50
个字符,每块大小1KB
。 - 本实验采用文件来模拟磁盘,采用
dd
命令创建文件,例如:dd if=/dev/zeroof=simulated_device bs=1024 count=250000
将创建一个包含250000块文件名为simulated_device
的文件。(这个文件可以满足测试程序中所有测试程序的空间需要) - 本实验已经给出测试文件,最大测试文件为50M。
思考
- 实验要求很简单,就是实现一个普通的文件系统,而且实现方式不限,使用提供的
write_block
等接口来模拟低层的磁盘操作。 - 设计一个文件系统,最主要的是思考如何布局。无论什么文件系统,都要包含目录树、文件信息等功能。
- 文件的实现书上介绍了几种常见的方法:
- 连续分配
- 最简单的分配方案,将每个文件存放在连续的磁盘数据块中。
- 易于实现,性能不错,但会造成大量外碎片(在多个小文件的时候经常发生)
- 带有文件分配表的链表结构
- 把每一个磁盘块中的链表指针抽取出来,单独组成一个表格,放在内存中。
- 这样的坏处是整个文件分配表必须都放在内存,太占用空间。
- I-Node
- 大名鼎鼎的
i-node
登场啦。此i-node
节点列出了文件的属性和各个数据库的磁盘地址,在给定一个i-node
之后,就能找到该文件的所有信息。 - 只有当一个文件被打开时,才需要把它的
i
节点装入内存。 - 当
i-node
存储的磁盘地址不够时,我们还可以使用多级间接块(indirect block)来描述更多的磁盘地址。
- 大名鼎鼎的
- 连续分配
- 目录
- 相对来说,目录的实现就简单多了。目录也可以理解为一种特殊的文件,UNIX中的目录非常简单,每一项(entry)只包含
文件名
和i-node
节点号两个信息。
- 相对来说,目录的实现就简单多了。目录也可以理解为一种特殊的文件,UNIX中的目录非常简单,每一项(entry)只包含
- 空闲块管理
- 如何迅速知道磁盘中的空闲块以及剩余容量呢?
- 一种方法是使用链表,但会占用大量空间。
- 更常用的方法是使用位图(bitmap)来管理,已经分配的块用1表示,未分配的用0表示,每个块只占1个byte。
- SuperBlock
- 我们还需要一个
SuperBlock
作为文件系统的初始块。它应该包含文件系统初始化信息、位图地址、根目录地址等等,以便让系统将文件系统加载进内存。
- 我们还需要一个
实现
本项目使用了SuperBlock
、Bitmap
,以及i-node
来实现文件系统。
- Superblock
- 是否初始化
- 磁盘剩余空间(用于快速访问,了解该文件是否能放入磁盘)
- bitmap信息(每个位图的使用情况)
- 存储在第一块block中,当初始化时自动加载。
- Bitmap
- 按理来说,
Bitmap
中的每一个byte
来代表一个块。但用byte
操作不太直观,因此本项目使用short
类型代替(会多浪费不少空间,但总占比空间依然不多) - 由于我们由
250000
个块,本项目设计每个Bitmap包含500个块的位图信息,其余地址用于存储起始磁盘块地址
、剩余块多少。 - 因此,我们需要
500
个Bitmap
,存储在第2~501个磁盘块中。
- 按理来说,
- Root目录
Root
目录也必须在文件系统初始化时加载和初始化,因此我们将它分配在502
磁盘块上。
i-node
- 在每个
i-node
中,我们设计了以下属性: - 文件大小
- 创建、修改时间
- 直接块(200个,可以存储200KB的信息)
- 一级间接块(1个,可以存储256KB的信息–一条地址4bytes)
- 二级间接块(1个,可以存储256*256KB的信息,完全足够我们用了)
- 在每个
总结下来,我们的文件系统大体框架如下图所示:
缓存
解决了文件系统的大体框架问题,接下来我们就要想缓存的问题了。因为我们不可能每次写都直接写到磁盘、每次读都直接从磁盘中读,这样一个block(1KB)一个block的读实在是太慢了。因此我想出了以下几个方法:
- 每次
write
只写到缓存,只有当文件close
的时候才真正写到内存。这种方法的好处是无论如何读写都很快,但当文件很大的时候非常占用内存。 - 给内存分配一块专门用作文件读写的缓冲区,当文件大小小于缓冲区时,就直接将文件存储在缓存区中,当文件大于此大小,就将多余的部分存储在磁盘中。
理论上来说,第二种方案肯定是更好的,因为作为操作系统的一部分,肯定不希望占用过多的内存。但本项目为了简单,只采用了第一种方案。
代码逻辑
结构设计
因为文件系统涉及的结构层次较多,为了方便阅读代码,我整理了函数的逻辑如下:
可