目录与文件系统--OS

目录与文件系统

前面一节我们完成了第三层抽象,就是将盘块号抽象到文件,这一次,我们要对磁盘再进行一次抽象,将磁盘抽象为一个文件系统,用户看到的磁盘就是一个文件目录的结构,完成第四层抽象主要就是实现文件系统到磁盘的映射关系

文件系统,抽象整个磁盘(第四层抽象)

目录树结构:易于分类,且扩展性好,每一个节点就是一个目录

我们是如何使用目录的?

我们使用目录的方式就是给出一个路径名如/my/data/m,然后根据路径名找到文件的FCB,再接上我们前面讲的,有了FCB就能找到字符流对应的盘块号…等等

因此我们需要做的就是实现路径名到文件FCB的映射,问题是如何实现,这里有两个问题

  1. 目录中又存放着什么信息?
  2. 那磁盘块要存放什么信息来实现目录呢?

从直观想法来看,我们会在目录中存放它子目录的FCB,当有路径名时,将FCB读取进来,再挨个比较就可以了,但是我们实际上只需要比较一个文件名,但是却读取了大量的FCB,这毫无疑问是一种浪费,因此根据索引的思想,我们希望在目录中存放一个子目录名还有该目录对应的FCB的地址,也就是这种结构 <目录名:索引值>,但是要实现这一索引系统,我们需要磁盘的帮助,也就是磁盘划分一块区域用来存放FCB块,并且保证连续,这样所有的FCB块就会有对应的索引值了,类似于数组,因为根目录没有其他目录来保存它的索引值,因此我们需要在磁盘块定义一个固定的地址来存放根目录,这样我们就可以回答上面的问题了

  1. 目录中存放什么信息?

    目录中应该存放子目录的名字还有子目录对应的索引值

  2. 磁盘块要存放什么信息来实现目录

    磁盘块要划分一段连续的区域专门存放FCB块,并定义一个初始地址作为根目录的索引

我们看一下磁盘的划分结构

Y5TlwT.png

超级块:存放着两个位图的长度还有超级块的长度等信息,可以根据这些长度找到根目录的位置

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的关键代码

猜你喜欢

转载自blog.csdn.net/jump_into_zehe/article/details/106220548