超级块描述了整个文件系统的信息,而文件作为存储的对象,它的信息是有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;