近日有人求助,要写一个UNIX文件系统作为暑假作业。这种事情基本是学操作系统的必须要做的或者是做过的,毕竟文件系统是操作系统课程的一个重要组成部分。要实现这个UNIX文件系统,很多人就扎进了UNIX V6的的系统源码,以及《莱昂氏UNIX源代码分析》和《返璞归真:UNIX技术内幕》这两本书,很多人出来了,很多人在里面迷失了...最终忘了自己只是要实现一个UNIX文件系统而已。
为何会迷失,因为代码不是自己写的,而且年代久远,编程理念不同了,作者为何那样写不一定就能理解,实际上对于任何别人写的代码,总是会有一些不易理解的地方,当然,如果作者水平超级高,那么代码也就相对容易理解。因此,写代码永远比读代码要容易!既然是要写一个文件系统,为何要用现成的UNIX V6代码呢?如果理解了UNIX文件的布局和结构,自己从零开始不参考任何现有的代码做一个也不是什么难事,最根本的是UNIX文件系统本身,至于说代码,仅仅是一个实现与用户操作的一个接口而已。如果代码是自己一点一点写的,那么你肯定能彻底明白每一行的每一个语句的精确含义,至于为何这么写,你当然及其明了!
本文留下我仓促间几个小时写的一个类UNIX文件系统的代码,不是让别人看的,是为了自己留档,因为本文已经说了,看别人的代吗只能学习经验,不能理解本质,更何况,你看的还得是超级一流的代码,而我写的,则是超级垃圾的代码。我只想说,理解问题的本质要比代码重要得多,代码,码,并不是很多人想象中的那般重要!本文的实现基于Linux系统,即在Linux系统上编写一个用户态程序,实现UNIX文件的IO接口以及操作。
推荐阅读:
UNIX/Linux 系统管理技术手册(第四版)高清中文PDF 下载:
UNIX/Linux 系统管理技术手册(第四版)高清英文PDF 下载见
我一向坚持的原则,那就是任何东西的根本性的,本质上的原理以及背后的思想都是及其简单的,所谓的复杂性都是优化与策略化的扩展带来的,正如TCP一样,UNIX的文件系统也不例外!我们必须知道,什么是最根本的,什么是次要的。对于UNIX文件系统,最根本的就是其布局以及其系统调用接口,一个处在最低层,一个在最上层开放给用户,如下所示:
系统调用接口:open,write,read,close...
文件系统布局:引导快,超级块,inode区表,数据块区
所有的在二者中间的部分都是次要的,也就是说那些东西不要也行,比如高速缓冲,高速缓存,VFS层,块层...因此在我的实现代码中,并没有这些东西,我所做到的,仅仅是UNIX文件系统所要求必须做到的最小集,那就是:
面对一个按照UNIX文件系统标准布局的“块设备”,可以使用open,read,write等接口进行IO操作。
在实现中,我用一个标准的Linux大文件来模拟磁盘块,这样块的操作基本都映射到了Linux标准的write,read等系统调用了。
首先定义必要的结构体:
//超级块结构
struct filesys {
unsigned int s_size; //总大小
unsigned int s_itsize; //inode表大小
unsigned int s_freeinodesize; //空闲i节点的数量
unsigned int s_nextfreeinode; //下一个空闲i节点
unsigned int s_freeinode[NUM]; //空闲i节点数组
unsigned int s_freeblocksize; //空闲块的数量
unsigned int s_nextfreeblock; //下一个空闲块
unsigned int s_freeblock[NUM]; //空闲块数组
unsigned int s_pad[]; //填充到512字节
};
//磁盘inode结构
struct finode {
int fi_mode; //类型:文件/目录
int fi_uid_unused; //uid,由于和进程无关联,仅仅是模拟一个FS,未使用,下同
int fi_gid_unused;
int fi_nlink; //链接数,当链接数为0,意味着被删除
long int fi_size; //文件大小
long int fi_addr[BNUM]; //文件块一级指针,并未实现多级指针
time_t fi_atime_unused; //未实现
time_t fi_mtime_unused;
};
//内存inode结构
struct inode {
struct finode i_finode;
struct inode *i_parent; //所属的目录i节点
int i_ino; //i节点号
int i_users; //引用计数
};
//目录项结构(非Linux内核的目录项)
struct direct
{
char d_name[MAXLEN]; //文件或者目录的名字
unsigned short d_ino; //文件或者目录的i节点号
};
//目录结构
struct dir
{
struct direct direct[DIRNUM]; //包含的目录项数组
unsigned short size; //包含的目录项大小
};
//抽象的文件结构
struct file {
struct inode *f_inode; //文件的i节点
int f_curpos; //文件的当前读写指针
};