Chinaunix首页 | 论坛 | 博客
  • 博客访问: 42434
  • 博文数量: 16
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 135
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-28 13:50
文章分类
文章存档

2009年(16)

我的朋友

分类: LINUX

2009-12-21 17:18:13

超级块描述了整个文件系统的信息,而文件作为存储的对象,它的信息是有inode节点来描述的。i节点位图描述了inode的使用情况。
struct m_inode{
        unsigned short i_mode; //文件类型
        unsigned short i_uid;//文件宿主
        unsigned long i_size;//文件大小
        unsigned long i_mtime;//文件修改时间
        unsigned char i_guid; //文件组id
        unsigned char i_nlinks;        //文件目录项连接数
        unsigned char i_zone[9];//文件所在的设备逻辑块号
//以下的字段在内存中
        struct task_struct *i_wait; //等待该i节点的进程
        unsigned long i_atime,i_ctime;
        unsigned short i_dev; //文件所在的设备号
        unsigned short i_num; //i节点号
        unsigned short i_count;//i节点被使用的次数,0表示空闲
        unsigned char i_lock;
        unsigned char i_dirt;
        unsigned char i_pipe;
        unsigned char i_mount; //该节点是否是某个文件系统的安装节点
        unsigned char i_seek;
        unsigned char i_update;
}
其中i_zone[9]很重要,它指出了文件使用的设备的逻辑块号。其中0-6为直接块,也就是文件的数据直接在相应的逻辑块上;7位1级块,1级块可以包含512个逻辑块号;8位2级块,它可以存放512个1级块。所以文件长度在7k以内只使用前7个位指定逻辑块,再大就要使用间接块指定了。
对i节点位图以及i节点的操作有new_inode,free_inode,iget,iput,bmp,以及namei.辅助的函数有get_empty_inode,read_inode,write_inode.内核维护了一个inode节点数组:
struct m_inode inode_table[NR_INODE]={{0,},};
new_inode(int dev)从inode_table中获取一个空闲的inode节点项,然后相应设备超级块中的i节点位图中的第一个0bit,接着设置inode节点信息,并置引用为1。free_inode则相反,进行一系列测试后,它清除位图的相应比特为,并清空inode节点占用的内存。
对逻辑块位图的操作有new_block和free_block,前者获取一个空闲缓冲块并置位相应比特位,后者相反。以上都是对内存中的inode数组以及缓冲区中的位图进行操作,而最终要落实到设备特定的块上。read_inode是从设备上读取指定i节点的信息到内存中,write_inode是写入指定i节点到设备。后者是将inode写到相应缓冲块中,并置位
b_dirt。这里涉及到计算指定的inode节点在设备上的逻辑块号:
//启动块+超级块+i节点位图块数+逻辑块位图块数+(i节点号-1)/每块含有的i节点
block=1+1+sb->s_imap_blocks+sb->s_zmap_blocks+(inode->s_inum)/INODES_PER_BLOCK;
iget 函数是从设备上读取指定的i节点。如同getblk,考虑到同步问题也是个反复的过程。它首先从i节点表中申请一个空闲i节点。然后搜索i节点表,看是否有指定的i节点,如果没有就用read_inode从设备中读取到空闲节点就可返回。如果有则等待它解锁。等待过程中如果节点表发生变化那么要重新搜索。若该i节点是某个文件系统的安装点,则读取相应文件系统的超级块并设置节点号为该文件系统的第一个inode,然后重新开始。
iput是回写到设备,这里根据文件的类型有几种不同的处理方法。如果文件是管道文件,则唤醒管道上等待的进程,并释放管道页面。管道的物理内存地址在inode-> i_size字段。如果文件是块设备,那么刷新设备。inode->i_zone[0]中存放的是设备号。如果链接数为0,则释放i节点所有的逻辑块,并释放该节点。
还有一个比较重要的函数是 _bmp,它是将文件数据块映射到盘块。在前面讲过的do_no_page中用到过,计算出缺页的内存地址在程序所占的块号后,通过bmp映射到设备上的块号,并读取被交换到块设备上的页面。
下面具体讲解一下:
//inode:文件的节点,block:文件中的块号;create:创建标志。
static int _bmp(struct m_inode *inode,int block,int create)
{
        struct buffer_head *bh;
        int i;
        if(block<0)
                panic("_bmp:block<0");
//如果块号大于直接块数+间接块数+二次间接块数,则超出文件系统表示范围,死机
        if(block>=7+512+512*512)
                panic("_bmp:block>big");
//如果块号小于7,则使用直接块表示
        if(block<7){
                if(create&&!inode->i_zone[block])
                        if(inode->i_zone[block]=new_block(inode->i_dev)){
                                inode->i_ctime=CURRENT_TIME;
                                inode->i_dirt=1;
                        }
                return inode->i_zone[block];
        }
//否则就看是不是在间接块中
        block-=7;
        if(block<512){
//如果间接块没有创建且创建标志为1,那么创建间接块
                if(create&&!inode->i_zone[7])
                        if(inode->i_zone[7]=new_block(inode->i_dev)){
                                inode->i_dirt=1;
                                inode->i_ctime=CURRENT_TIME;
                        }
                if(!inode->_zone[7])
                        return 0;
//读取设备上的一次间接块
                if(!(bh=bread(inode->i_dev,inode->i_zone[7])))
                        return 0;
//取间接块第block上的逻辑块号
                i=((unsigned short*)(bh->b_data))[block];
                if(create&&!i)
                        if(i=new_block(inode->i_dev)){
                        ((unsigned short*)(bh->b_data))[block]=i;
                        bh->b_dirt=1;
                        }
                brelse(bh);
                return i;
        }
//到此,表明数据块是在二次间接块上,如上所述处理之
        block-=512;
        if(create&&!inode->i_zone[8])
                if(inode->i_zone[8]=new_block[inode->i_dev]){
                        inode->i_dirt=1;
                        inode->i_ctime=CURRENT_TIME;
                }
        if(!inode->i_zone[8])
                return 0;
//读取该二次间接块的一级块
        if(!(bh=bread(inode->i_dev,inode->i_zone[8])))
                return 0;
        i=((unsigned short*)bh->b_data)[block>>9];
        if(create&&!i)
                if(i=new_block(inode->i_dev){
                        bh->b_dirt=1;
                }
        brelse(bh);
        if(!i)
                return 0;
//读取二次间接块的二级块
        if(!(bh=bread(inode->i_dev,i))
                return 0;
//取二级块上的block项的逻辑号
        i=((unsigned short*)bh->b_data)[block&&511];
        if(create&&!i)
                if(i=new_block(inode->i_dev)){
                        ((unsigned short*)(bh->b_data))[block&511]=i;
                        bh->b_dirt=1;
                }
        brelse(bh);
        return i;
}
bmp是create=0的_bmp,仅取文件中的数据块号在缓冲区的对应块号,而create_block则是create=1的_bmp,它创建对应的设备逻辑块的缓冲块。

到目前为止,应该对超级块,i节点,逻辑块,缓冲块有了一定的认识了。设备上的物理部分都有内存的表示,比如超级块在内存中有超级块数组 super_block[NR_SUPER],i节点和逻辑块位图有s_imap[8]和s_zmap[8]指向的缓冲区表示,i节点在内存中有i节点数组inode_table[NR_INODE],逻辑块有缓冲区中的缓冲块。分配inode和逻辑块只需要分配内存中保留的空闲块,并设置对应位图和修改位,内核会在适当时间刷新到设备上。
阅读(1253) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~