目录与文件系统
前面一节我们完成了第三层抽象,就是将盘块号抽象到文件,这一次,我们要对磁盘再进行一次抽象,将磁盘抽象为一个文件系统,用户看到的磁盘就是一个文件目录的结构,完成第四层抽象主要就是实现文件系统到磁盘的映射关系
文件系统,抽象整个磁盘(第四层抽象)
目录树结构:易于分类,且扩展性好,每一个节点就是一个目录
我们是如何使用目录的?
我们使用目录的方式就是给出一个路径名如/my/data/m
,然后根据路径名找到文件的FCB,再接上我们前面讲的,有了FCB就能找到字符流对应的盘块号…等等
因此我们需要做的就是实现路径名到文件FCB的映射,问题是如何实现,这里有两个问题
- 目录中又存放着什么信息?
- 那磁盘块要存放什么信息来实现目录呢?
从直观想法来看,我们会在目录中存放它子目录的FCB,当有路径名时,将FCB读取进来,再挨个比较就可以了,但是我们实际上只需要比较一个文件名,但是却读取了大量的FCB,这毫无疑问是一种浪费,因此根据索引的思想,我们希望在目录中存放一个子目录名还有该目录对应的FCB的地址,也就是这种结构 <目录名:索引值>,但是要实现这一索引系统,我们需要磁盘的帮助,也就是磁盘划分一块区域用来存放FCB块,并且保证连续,这样所有的FCB块就会有对应的索引值了,类似于数组,因为根目录没有其他目录来保存它的索引值,因此我们需要在磁盘块定义一个固定的地址来存放根目录,这样我们就可以回答上面的问题了
-
目录中存放什么信息?
目录中应该存放子目录的名字还有子目录对应的索引值
-
磁盘块要存放什么信息来实现目录
磁盘块要划分一段连续的区域专门存放FCB块,并定义一个初始地址作为根目录的索引
我们看一下磁盘的划分结构
超级块:存放着两个位图的长度还有超级块的长度等信息,可以根据这些长度找到根目录的位置
inode位图:哪些inode空闲,哪些被占用
盘块位图:哪些盘块是空闲的,硬盘不同这个位图的大小也不同
完成全部映射下的磁盘使用
目录解析代码实现
在显示器的那一篇里,我们看到了open
的工作,就是读取到inode
,将其放在进程PCB
的文件数组中,返回文件在数组中的下标fd
,而这一讲,主要是在open
中实现找到文件的inode
;
在linux/fs/open.c中
int sys_open(const char *filename, int flag)
{
i = open_namei(filename,flag,&inode); //这个函数就是解析路径的关键代码
}
int open_namei(...)
{
dir = dir_namei(pathname,&namelen,&basename);
...
}
static struct m_inode *dir_namei()
{
dir = get_dir(pathname);
}
get_dir完成真正的目录解析
static struct m_inode *get_dir(const char *pathname)
{
if(c=get_fs_byte(pathname)=='/')
{
inode=current->root;
//每一个进程都会有root指向根目录的地址,因为都是从父进程fork来的
pathname++;
}
else if(c)
inode=current->pwd;
while(1)
{
if(!c) //目录解析完毕
return inode;
bh = find_entry(&inode,thisname,namelen,&de); //找到对应的目录的FCB
int inr=de->inode; //找到的目录项的索引值
int idev=inode->i_dev;
inode=iget(idev,inr); //根据目录找到下一层目录
}
}
我们先看一下为什么current->root指向根目录?
void init(void)
{
setip((void*) &drive_info);
...
}
sys_steup(void * BIOS)
{
hd_info[drive].head = *(2+BIOS);
hd_info[drive].sect = *(14+BIOS);
mount_root();
...
}
void mount_root()
{
mi = iget(ROOT_DEV, ROOT_INO);
current->root = mi;
}
//在初始化时,根目录挂在了第一个进程上
回到inode_iget
struct m_inode *iget(int dev, int nr)
{
struct m_inode *inode = get_empty_inode();
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode);
return inode;
}
static void read_inode(struct m_inode *inode)
{
struct super_block *sb=get_super(inode->i_dev);
lock_inode(inode);
block = 2+sb->s_imap_blocks+sb->s_zmap_blocks+(inode->i_num-1)/INODES_PER_BLOCK;
//2表示引导块和超级块的长度,另外两个为位图块的长度,最后一个为索引值,
//知道了inode的位置还要除于一个inode所占盘块数的大小得到盘块号
bh = bread(indeo->idev, block); //磁盘读
inode = bh->data[(inode->i_num-1)%INODES_PER_BLOCK];
unlock_inode(inode);
}
find_entry
de:directory entry(目录下)
struct dir_entry{
unsigned short inode; //i节点号
char name[NAME_LEN]; //文件名
}
在fs/namei中
static struct buffer_head *find_entry(struct m_inode **dir, char *name, ... ,
struct dir_entry **res_dir)
{
int entries=(*dir)->i_size/(sizeof(struct dir_entry));
int block=(*dir)->i_zone[0];
* bh=bread((*dir)->i_zone[0]);
struct dir_entry *de=bh->b_data;
while(i<entries)
{
if((char*)de>=BLOCK_SIZE+bh->i_size)
{
brelse(bh);
block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK);
bh = bread((*dir)->i_dev, block);
de = (struct dir_entry*)bh->b_data;
}
if(match(namelen, name, de))
{
*res_dir = de;
return bh;
}
de++;
i++;
}
}
以上就是根据目录得到文件的FCB的关键代码