Minix文件系统框架

Minix文件系统框架

摘要:本文介绍了Minix的文件系统框架,包括文件系统的布局、块高速缓存、目录管理和对文件的基本操作。

文件系统布局

一个Minix文件系统实体包含了i节点、目录和数据块。

 

每个文件系统以一个引导块(bootblock)开始,其中包含可执行代码,其大小为1024字节(两个磁盘扇区)。启动计算机时硬件首先执行引导块中的代码,引导块则负责操作系统本身的加载。为防止硬件从非引导设备中启动,引导块的固定位置上有一个魔数(magicnumber),启动设备时硬件会检查该文件的引导块中是否存在该魔数。

超级块(superblock)描述了文件系统的布局,它给出了文件系统各个部分的大小,其大小为1024字节。下面给出超级块的结构定义:

EXTERN structsuper_block {

ino_ts_ninodes;/* 可用的i节点个数*/

zone1_t s_nzones;/* 总的位图大小,包括位图等*/

shorts_imap_blocks;/* i节点位图块个数*/

shorts_zmap_blocks;/* 区段位图块个数*/

zone1_ts_firstdatazone;/*第一个数据块区段*/

shorts_log_zone_size;/* log2(块数/区段)*/

shorts_pad;/* 防止关联编译的填充位*/

off_ts_max_size;/* 设备上的最大文件长度*/

zone_ts_zones;/*区段个数*/

shorts_magic;/* 魔数*/

 

/* Thefollowing items are valid on disk only for V3 and above */

 

shorts_pad2;/* 防止关联编译的填充位*/

unsigned shorts_block_size;/* 块大小(字节)*/

chars_disk_version;/* 文件系统格式子版本号*/

 

/*下列各项只有超级块载入内存时才使用*/

struct inode*s_isup;/* 指向被挂载的文件系统根目录的i节点指针*/

struct inode*s_imount;/* 指向挂载到i节点的指针*/

unsigneds_inodes_per_block;/*魔数的预先计算值*/

dev_ts_dev;/* 超级块所属的设备*/

ints_rd_only;/* 该位1表示只读*/

ints_native;/*该位1表示非字节交换*/

ints_version;/* 文件系统版本号,0意味着魔数异常*/

ints_ndzones;/* 直接区段/i节点*/

ints_nindirs;/* 间接区段/间接块*/

bit_ts_isearch;/* i节点位图中的第一个区段位*/

bit_ts_zsearch;/* 区段位图中的第一个空闲位*/

}

一个磁盘块如果想用作Minix文件系统,它必须具有图1所示的结构。工具程序mkfs可经用来创建一个文件系统。

Mkfs/dev/fd1 1440

该命令将在驱动器1的软盘上创建一个空的1440个块的文件系统。

Minix系统分别使用i节点位图和区段位图来管事i节点和区段。当一个文件被删除时很容易算出该文件的i节点所在的位图块,并利用通常的高速缓存机制查找到该块。一旦找到它,就把对应于该i节点的那一位清零。区段的释放过程与此类似。

何谓i节点?i节点(indexnode)是用来实现文件块到物理块之间映射而赋给文件的一种数据结构:

EXTERN structinode {

mode_ti_mode;/* 文件类型,保护位等*/

nlink_ti_nlinks;/* 文件的链接数*/

uid_ti_uid;/* UID */

gid_ti_gid;/* GID*/

off_ti_size;/* 当前文件长度(字节)*/

time_ti_atime;/* 最后访问时间(V2only) */

time_ti_mtime;/* 最后修改时间*/

time_ti_ctime;/* i节点最后更改时间(V2only)*/

zone_ti_zone[V2_NR_TZONES]; /* 区段数组*/

 

/* 下列各项不在磁盘上*/

dev_ti_dev;/* 设备标识*/

ino_ti_num;/* i节点号*/

inti_count;/* i节点使用的次数*/

inti_ndzones;/*直接区段(Vx_NR_DZONES)*/

inti_nindirs;/*间接区段/间接块*/

structsuper_block *i_sp;/*指向超级块的指针*/

chari_dirt;/* 脏位*/

chari_pipe;/* 管道标识位*/

chari_mount;/* 挂载位*/

chari_seek;/* 位置指针位*/

chari_update;/* ATIME, CTIME,MTIME*/

}

i节点指示了本文件位于磁盘上的哪些存储块当中。每个i节点可以指示8个磁盘块,当实际的文件大小超过8个磁盘块时就要用到二级间接区段,即一级区段的最后一位不是指向一个实际存储文件的磁盘块,而是指向另一个i节点。当使用二级间接块还不能指示文件的所有存储位置时可以依次类推使用三级间接块。

块高速缓存

MINIX使用块高速缓存来改进文件系统性能。高速缓存用一个缓冲数组来实现,其中每个缓冲区由包含指针、计数器和标志的头以及用于存放磁盘块的体组成。所有未使用的缓冲区均使用双链表,按最近一次使用时间从近到远的顺序链接起来。

 

2.块高速缓存使用的链表

为了迅速判断某一块是否在内存中,我们使用了哈希表。所有缓冲区,如果它所包含块的哈希代码为k,在哈希表中用第k项指向的单链表链接在一起。哈希函数提取块号低n位作为哈希代码,因此不同设备的块可以出现在同一哈希链之中。每个缓冲区都在其中某个链中。在MINIX启动,初始化文件系统时,所有缓冲区均未使用,并且全部在哈希表第0项指向的单链表中。这时,哈希表其他项均为空指针。但是一旦系统启动完成,缓冲区将从0号链中删除,放到其他链中。

get_block系统调用负责寻找文件块。该系统调用需要两个参数:设备号和块号。这两个值与缓冲区链中对应域相比较,如果找到包含这一块的缓冲区,则缓冲区头中标志块使用次数的计数器加1,并返回指向该缓冲区的指针。如果在哈希表中未找到这样的块,我们可以使用LRU链中的第一个缓冲区。LRU链中的缓冲区必然不在使用中,因而它包含的块可以被换出内存,以释放这个缓冲区。

目录管理

Minix中目录实际上是一种特殊的文件。系统提供了64字节的目录项,其中4字节用来存放i节点号,60个字节用来存放文件名。

下图展示了路径/home/zcy/doc/是如何查找的。

 

 

 

根目录 i节点776i节点13215


3.寻找路径/home/zcy/doc/的过程

mount系统调用实现文件系统的挂载。

mount -o loopminix3.iso /mnt/

是把ISO镜像文件minix3.iso挂载到路径/mnt/下。挂载前后的文件树结构如下图所示。

 

              图4.挂载前

 

        图5.挂载后

Minix系统中使用文件链接可以让一个文件同时出现在多个不同的目录下,link( file_name , link_name)系统调用用于创建链接文件。文件的链接数是受限制的,不能超过LINK_MAX(在文件include/limits.h中被定义为SHRT_MAX)。在这种情况情况被链接的文件在磁盘上只一个物理复本,所以当一个目录下的文件被修改时,其他路径下的链接文件也会发生相应更改。i节点中保存着文件的链接数,当删除一个文件时该数目减1,当减到0时彻底从磁盘上删除文件。

文件操作

对文件的操作包括创建、打开、读、写和关闭文件。在/servers/fs/open.c中我们看到了下面几个函数:do_createdo_opencommon_opennew_nodepipe_opendo_mknodedo_mkdirdo_closedo_lseek

do_createdo_open函数分别用于创建和打开一个文件,创建或打开一个文件一般包括三个步骤:1.找到i节点(如果是一个新文件,需要分配i节点并进行初始化);2.找到或创建相应的目录项;3.为文件建立并返回一个文件描述符。何谓文件描述符?文件描述符包含了文件指针和i节点指针,一个文件被打开时就把一个文件描述符返回给打开它的进程。由于一个文件可以被多外进程打开,但是不同进程对文件进行操作的起始位置是不同的,如一个进程可能要从文件开始处读文件,而另一个进程要从文件结尾处开始向文件中追加写入新的内容,所以一个进程打开一个文件时它还要获取对文件操作的起始位置。

common_open完成创建和打开文件的共同操作。它首先检查是否有空闲的文件描述符及空闲的filp表项。如果是要创建一个新文件,则执行new_node函数。如果目录已存在,new_node将返回相应的i节点指针;否则它会创建一个新的目录项和i节点。打开已有文件时文件系统要检查它的类型和模式(-rwx),以确保它可以被打开。

pipe_open专用来打开一个管道文件。do_mknode函数与do_create类似,它仅仅用来创建i节点并为其分配一个目录项。do_mkdir用来创建一个目录,目录不可能为空,因为在创建它时至少包含了两个目录项“.”和“..”,分别指向当前目录及其父目录。

关闭文件调用do_close函数,对于普通文件只需把filp计数器减1并看实验室是否为0,若为0则调用put_inode收回i节点。do_lseek用于定位文件中的位置,这在进行文件读写时十分有用。

关于读文件的相关函数在/servers/fs/read.c中。系统把读文件的请求分解成若干个小块,每个小块都在一个单独的磁盘块中,它从当前位置开始,直到满足下列条件之一:

  1. 所有字节都已读完。

  2. 遇到一个块边界。

  3. 读到文件末尾。

rw_chunk函数的功能是完成对每一个单独的磁盘块的读操作。该函数以i节点和文件位置为参数,把它们转换为一个物理磁盘块号,然后把请求传到用户空间中。相对文件位置与物理地址的映射是在read_map函数中完成的,它能够处于i节点和间接块。当读取一个间接块时需要调用rd_indir函数。

写文件的代码放在write.c中。写文件与读文件类似,do_write函数只是在调用rwad_writer时设置WRITEING标志位。读和写的主要区别在于写文件需要分配新的磁盘块。

对文件进行操作时需要满足Minix系统对文件设置的保护规则。每一个文件都有三组rwx位,分别用于文件所有者、所有者所在的组、其他用户。rwx分别表示可读可写可执行,可以通过chmod系统调用来设置文件的保护位。

posted @ 2010-12-04 21:31  高性能golang  阅读(6611)  评论(0编辑  收藏  举报