4.3 Linux文件系统-inode

1:基本信息

代码:linux-0.11

2:linux文件系统的几部分

  1. 有关linux中高速缓冲区的管理部分。分页机制 buff.c
  2. 文件系统的底层通用函数。(对于硬盘的读写,分配,释放,对于目录的节点管理,内存和磁盘的映射)
  3. 对文件数据进行读写操作的模块(VFS 虚拟文件系统,硬件驱动和文件系统的关系, pipe,块设备的读取)
  4. 文件系统与其他程序的接口实现(fopen 关闭,创建等文件的调用方式)

3.文件系统的基本概念

以磁盘为例,磁盘中有目录的映射,把磁盘分成盘片,每一个盘片 都有一个文件系统的子系统(章节目录)
4.3 Linux文件系统-inode_第1张图片

引导块:用来引导设备的。可以为空,但是一定要空开,为了保持数据的一致性
超级块:该文件子系统描述符,记录该i节点位图和逻辑块位图的地址,通过设备号可以获取超级块
逻辑位图:其每一位对应数据区的盘块(逻辑块)的使用情况,如果对应的逻辑块使用了,则逻辑块位图的值为1
i节点位图:对应着后面i节点的使用情况,如果i节点使用了则对应i节点位图的位置1
i节点:目录与磁盘的桥接,文件的属性描述
数据区的盘块:用来存储数据

i节点位图,块中1bit对应一个i节点 1024 * 8 - 1 = 8191个 0位不用
逻辑块位图中1bit对应一个逻辑块
所有的块大小是固定的,都是盘块或者磁盘块
扇区:是块设备上长度为512B的数据块。不同文件系统,扇区和盘块对应关系不同
2个扇区 对应一个盘块1024B MINX

逻辑块号是从第一个引导快开始计数的

3.1 inode的结构体

struct m_inode {
	unsigned short i_mode;//文件的类型和属性,
	unsigned short i_uid; //宿主的用户ID
	unsigned long i_size; //该文件的大小
	unsigned long i_mtime;//该文件的修改时间
	unsigned char i_gid; //宿主的组ID
	unsigned char i_nlinks; //链接数,记录链接到本文件的数目
	unsigned short i_zone[9]; //该文件映射在逻辑块号(数据区)的数组,表示占用了那些数据的的盘块,文件和磁盘的映射。
	//前7个为直接块号,每一个代表一个块号
	//第8个一次间接块号,代表了512个块号
	//第9个二次间接块号,代表512*512个块号
/* these are in memory also */
	struct task_struct * i_wait; //等待i节点的进程
	unsigned long i_atime;//最后访问的时间
	unsigned long i_ctime;//i节点自身被修改的时间
	unsigned short i_dev;//i节点所在的设备号
	unsigned short i_num;//i节点号
	unsigned short i_count;//i节点被引用的次数,0是空闲
	unsigned char i_lock;//i节点被锁定的标志
	unsigned char i_dirt;//i节点被修改的标志
	unsigned char i_pipe;//i节点用作管道标志
	unsigned char i_mount;//i节点安装其他文件系统的标志
	unsigned char i_seek;//收索标志
	unsigned char i_update;//i节点已更新的标志
};

3.1.1 文件的类型和属性

例如crw-rw-
c代表文件类型
c:字符型设备
b:块设备
p:管道
l:链接
d:目录
-:普通文件
s:符号文件

属性:rwxrwxrwx
第一个rwx:表示当前用户的权限
第二个rwx:表示当前用户组的权限
第三个rwx:其他人的权限
4.3 Linux文件系统-inode_第2张图片

3.1.2 文件的类型和属性i_zone[9]

4.3 Linux文件系统-inode_第3张图片

4:超级快的结构体

struct super_block {
	unsigned short s_ninodes;//i节点数目
	unsigned short s_nzones;//逻辑块数
	unsigned short s_imap_blocks;//i节点位图所占块数
	unsigned short s_zmap_blocks;//逻辑块位图所在块数
	unsigned short s_firstdatazone;//数据区中第一个逻辑块块号
	unsigned short s_log_zone_size;//log2(磁盘块/逻辑块)
	unsigned long s_max_size;//最大文件长度
	unsigned short s_magic;//文件系统幻术(0x137f)
/* These are only in memory */
	struct buffer_head * s_imap[8];  //i节点位图在高速缓冲块指针数组, 从磁盘上缓冲到对应的缓冲区上,数组存放在缓冲区对应的地址,一个设备最大支持8个文件系统
	struct buffer_head * s_zmap[8];  //i节点位图在高速缓冲块指针数组
	unsigned short s_dev; //设备号
	struct m_inode * s_isup; //根目录的I节点, 被安装文件系统根目录的i节点
	struct m_inode * s_imount;//安装i节点,   该文件系统被安装到的i节点
	unsigned long s_time;//修改时间
	struct task_struct * s_wait;//等待本超级快进程的指针
	unsigned char s_lock; //锁定标志
	unsigned char s_rd_only;//只读标志
	unsigned char s_dirt;//已被修改的配置	
};

不管读取磁盘上什么资源,都是:
先getblk(获取该资源对应的设备和块号对用的缓冲区)
然后bread(确认有效数据的缓冲区
最后进行区域内存的拷贝,从bh(buff_head结构体)b_data数据区域拷贝到数据的内存中

5:bitmap.c

操作i节点位图和逻辑块位图(销毁,创建,查找)

/*
 *  linux/fs/bitmap.c
 *
 *  (C) 1991  Linus Torvalds
 */
//将指定地址的一块1024字节内存清0
#define clear_block(addr) \
__asm__("cld\n\t" \
	"rep\n\t" \
	"stosl" \
	::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")
//指定地址开始的第nr个位偏移处的比特位置位(置1), 并返回原来bit位。可以用来操作i节点位图和逻辑位图
#define set_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
//指定地址开始的第nr个位偏移处的比特位复位(置0), 并返回原来bit位。可以用来操作i节点位图和逻辑位图
#define clear_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
//从addr开始寻找第一个0值的比特位,并返回偏移量。可以用来找i节点位图和逻辑位图空闲的位置
#define find_first_zero(addr) ({ \
int __res; \
__asm__("cld\n" \
	"1:\tlodsl\n\t" \
	"notl %%eax\n\t" \
	"bsfl %%eax,%%edx\n\t" \
	"je 2f\n\t" \
	"addl %%edx,%%ecx\n\t" \
	"jmp 3f\n" \
	"2:\taddl $32,%%ecx\n\t" \
	"cmpl $8192,%%ecx\n\t" \
	"jl 1b\n" \
	"3:" \
	:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
__res;})
//释放逻辑块
void free_block(int dev, int block)
{
	struct super_block * sb;
	struct buffer_head * bh;

	if (!(sb = get_super(dev))) //根据设备号获取设备对应的超级快结构体
		panic("trying to free block on nonexistent device");
	if (block < sb->s_firstdatazone || block >= sb->s_nzones) //传入块号,在第一个块号和最后块号范围之外,就报错
		panic("trying to free block not in datazone");
	bh = get_hash_table(dev,block); //是否能在hash表中找到,对应的高速缓冲区
	if (bh) {//已经在高速缓冲区中存在,下面就把对应的高速缓冲区释放
		if (bh->b_count != 1) {//b_count为0 表示未在使用,不需要释放
			printk("trying to free block (%04x:%d), count=%d\n",
				dev,block,bh->b_count);
			return;
		}
		//以下是在使用的状态
		bh->b_dirt=0;//修改状态清空
		bh->b_uptodate=0;//更新状态清空
		brelse(bh);//释放高速缓冲区
	}
	 //不在高速缓冲区或者已经从高速缓冲区释放,再进行下面操作
	 //复位block在逻辑块图的比特位(置0),  先计算block在数据区开始运算起的数据逻辑块号(从1开始运算)
	 //然后对逻辑块位图进行操作,复位对应的bit位,如果对应的bit位原来就是0,则停机出错
	 //由于1个缓冲区块1024字节,即8192 bit。因此block / 8192,可以计算出指定块block在逻辑位图中的哪个块上,
	 //而block&8191 (8191  = 0x1FFF)(相当于block%8191)可以得到block在逻辑块图中bit位的偏移
	block -= sb->s_firstdatazone - 1 ; //计算这个块在数据区相对于数据区起始位置的位置
	if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) { //清空这个块的块的对应的逻辑位图,成功返回0表示(由1->0)
		printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
		panic("free_block: bit already cleared");
	}
	sb->s_zmap[block/8192]->b_dirt = 1;//相应的逻辑块位图所在缓冲区的 已修改标志置1
}
//新建逻辑块
int new_block(int dev)
{
	struct buffer_head * bh;
	struct super_block * sb;
	int i,j;

	if (!(sb = get_super(dev)))
		panic("trying to get new block from nonexistant device");
	j = 8192;
	for (i=0 ; i<8 ; i++) //遍历8个逻辑块位图
		if (bh=sb->s_zmap[i]) //获取逻辑位图对应的的高速缓冲区
			if ((j=find_first_zero(bh->b_data))<8192) //在高速缓冲区的位置,从缓冲区开始找到第一个为0的块位置,返回的位置小于8192即算找到
				break;
	if (i>=8 || !bh || j>=8192)
		return 0;
	if (set_bit(j,bh->b_data))//设置高速缓冲区的j的位置为1
		panic("new_block: bit already set");
	bh->b_dirt = 1;//修改标志置1,后期给写盘检测用不用写
	j += i*8192 + sb->s_firstdatazone-1; //计算当前分配的逻辑块是第几个block
	if (j >= sb->s_nzones) 
		return 0;
	if (!(bh=getblk(dev,j))) //针对设备dev和块号j来获取缓冲区
		panic("new_block: cannot get block");
	if (bh->b_count != 1)//不为1就打印错误信息,因为getblk回把它置1
		panic("new block: count is != 1");
	clear_block(bh->b_data);//把缓冲区的内存清空
	bh->b_uptodate = 1;//设置缓冲区更新标志
	bh->b_dirt = 1;//设置缓冲区修改标志
	brelse(bh);
	return j;
}
//释放指定的i节点,把i节点对应的i节点位图中的bit置0,清空i节点位图的信息
void free_inode(struct m_inode * inode)
{
	struct super_block * sb;
	struct buffer_head * bh;

	if (!inode)
		return;
	if (!inode->i_dev) { //设备号为0
		memset(inode,0,sizeof(*inode));
		return;
	}
	if (inode->i_count>1) {//>1 节点被使用
		printk("trying to free inode with count=%d\n",inode->i_count);
		panic("free_inode");
	}
	if (inode->i_nlinks)//>=1 节点被使用
		panic("trying to free inode with links");
	if (!(sb = get_super(inode->i_dev)))//取出超级块
		panic("trying to free inode on nonexistent device");
	if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)//i_num不在正确范围内
		panic("trying to free inode 0 or nonexistant inode");
	if (!(bh=sb->s_imap[inode->i_num>>13])) //找到i节点对应的高速缓冲区
		panic("nonexistent imap in superblock");
	if (clear_bit(inode->i_num&8191,bh->b_data)) //清除对应位i节点位图上的bit位
		printk("free_inode: bit already cleared.\n\r");
	bh->b_dirt = 1;//修改标志置1
	memset(inode,0,sizeof(*inode));
}
//创建一个新的i节点, 返回i节点的指针,通过super_block找到inode的信息(i节点位图, 逻辑块位图)
//通过inode操作函数找到对应的inode的分配内存区
//设置inode位图中对应的位为1
//返回设置号的inode结构体
struct m_inode * new_inode(int dev)
{
	struct m_inode * inode;
	struct super_block * sb;
	struct buffer_head * bh;
	int i,j;

	if (!(inode=get_empty_inode())) //找到空闲的inode
		return NULL;
	if (!(sb = get_super(dev)))
		panic("new_inode with unknown device");
	j = 8192;
	for (i=0 ; i<8 ; i++)
		if (bh=sb->s_imap[i])
			if ((j=find_first_zero(bh->b_data))<8192)
				break;
	if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
		iput(inode);
		return NULL;
	}
	if (set_bit(j,bh->b_data)) //对应高速缓冲区的位置置位
		panic("new_inode: bit already set");
	bh->b_dirt = 1;
	inode->i_count=1;
	inode->i_nlinks=1;
	inode->i_dev=dev;
	inode->i_uid=current->euid;
	inode->i_gid=current->egid;
	inode->i_dirt=1;
	inode->i_num = j + i*8192; //在逻辑块上的位置 
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
	return inode;
}

mount
操作系统中有一个超级块数组
mount->把需要挂在设备文件系统的super_block读到高速缓冲区中并且放到超级块数组中

6: inode.c

处理inode节点的函数
iget:获得inode节点
iput:释放inode节点
bmap:对文件进行磁盘映射

从磁盘读写数据信息 inode的流程

  1. 找到dev
  2. 找到设备的super_block
  3. 通过sb的信息计算要读的块号
  4. 调用bread将其读取或写入到高速缓冲区
  5. 读:将高速缓冲区的b_data读到内存地址,释放高速缓冲区
    写: 将要写入的数据写到高速缓冲的b_data 并设置dirt修改标志位,等待系统的sys_sync进行写盘,释放高速缓冲区

6.1 读写inode

/*
 *  linux/fs/inode.c
 *
 *  (C) 1991  Linus Torvalds
 */
 #define BLOCK_SIZE 1024
#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
//把i节点的信息写入缓冲区中
//该函数把参数指定的i节点信息写入缓冲区相应的缓冲区块中,待缓冲区刷新时会写入磁盘。
//为了确认i节点所在的逻辑块号(或缓冲块)必须首先读取相应设备上的超级块,已获取用于计算逻辑块号的每块i节点信息INODES_PER_BLOCK
//在计算出i节点所在逻辑块号后,就把该逻辑块读入一缓冲块中,然后把i节点的内容复制到缓冲块的相应位置处
static void write_inode(struct m_inode * inode)//参数struct m_inode * inode是自己在内存上开辟的空间,不在磁盘上,也不在高速缓冲区
{
	struct super_block * sb;
	struct buffer_head * bh;
	int block;

	lock_inode(inode);//锁定i节点
	if (!inode->i_dirt || !inode->i_dev) { //(i节点没有被修改过 || i节点设备号是0)
		unlock_inode(inode); //解锁i节点
		return;//退出
	}
	if (!(sb=get_super(inode->i_dev)))//获得i节点对应的超级块
		panic("trying to write inode without device");
	//计算当前inode节点的
	// i节点所在的逻辑块号 = 2 (引导快 + 超级快) + i节点位图个数 +  逻辑块的个数 + (i节点总数-1)/ 每一块含i节点的个数
	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +(inode->i_num-1)/INODES_PER_BLOCK; 
	if (!(bh=bread(inode->i_dev,block))) //从设备读取i节点所在的逻辑块
		panic("unable to read i-node block");
	//把该i节点的信息复制到逻辑块对应该i节点啊的项位置处
	((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK] = *(struct d_inode *)inode;
	bh->b_dirt=1; //缓冲区修改标志置1
	inode->i_dirt=0; //i节点的内容和缓冲区一直,因此修改标志置0
	brelse(bh);//释放该含有i节点的缓冲区
	unlock_inode(inode);//解锁i节点
}
//inode同步
void sync_inodes(void)
{
	int i;
	struct m_inode * inode;

	inode = 0+inode_table;
	for(i=0 ; i<NR_INODE ; i++,inode++) {
		wait_on_inode(inode);
		if (inode->i_dirt && !inode->i_pipe)
			write_inode(inode);
	}
}

//读取inode的信息, 读出的信息不报括内存动态信息信息的,只有struct d_inode的信息
static void read_inode(struct m_inode * inode) //struct m_inode * inode是自己在内存上开辟的空间
{
	struct super_block * sb;
	struct buffer_head * bh;
	int block;

	lock_inode(inode);//i节点上锁
	if (!(sb=get_super(inode->i_dev)))//获取超级快
		panic("trying to read inode without dev");
	//计算当前inode节点的块号	
	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +(inode->i_num-1)/INODES_PER_BLOCK;
	if (!(bh=bread(inode->i_dev,block)))//通过设备号和块号,读到高速缓冲区中
		panic("unable to read i-node block");
	//从高速缓冲区中 读到用户的inode结构体中	
	*(struct d_inode *)inode =((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK];
	brelse(bh);//释放缓冲区中
	unlock_inode(inode);//i节点解锁
}

高速缓冲区与磁盘同步

系统调用,进行同步
int sys_sync(void)
{
	int i;
	struct buffer_head * bh;

	sync_inodes();		/* write out inodes into buffers */
	bh = start_buffer;
	for (i=0 ; i<NR_BUFFERS ; i++,bh++) { //遍历高速缓冲区
		wait_on_buffer(bh);
		if (bh->b_dirt) //当前缓冲区修改标志位为1
			ll_rw_block(WRITE,bh);//调用底层驱动,进行写盘操作
	}
	return 0;
}

6.2 进行inode节点和磁盘块映射 bmap

主要是对i_zone进行赋值

struct m_inode {
	unsigned short i_zone[9]; //inode节点的磁盘块映射,7个对应直接的逻辑块号(大小为7 * 1K = 7K),
	第8个对应一次间接块(1024/2 = 512 个逻辑块,地址是short类型占2个字节。大小为512 * 1K = 512K)
	第9个对应二次间接块(512 个一次间接块 512 * 512 个逻辑块。 大小为 512*512*1K)
	则一个文件最大的块数 7 + 512 + 512*512 = 262663
	则一个文件最大值为7K + 512K + 512*512K = 262663K = 256.5M

如有已有磁盘信息则进行赋值
如果没有磁盘信息先分配再赋值

文件数据块映射到盘块的处理操作, 调用一次申请一个block

inode: 文件的i节点指针
block: 文件中的数据块号
create: 创建标志

该函数把指定文件数据块block对应到设备上的逻辑块上,并返回逻辑块号。
如果创建标志位置位,则在设备上对应逻辑块不存在时就申请新的磁盘块,返回文件数据块block对应在设备上的逻辑号(盘块号)


static int _bmap(struct m_inode * inode,int block,int create)
{
	struct buffer_head * bh;
	int i;
	//判断block得有效性
	if (block<0)
		panic("_bmap: block<0");
	if (block >= 7+512+512*512) //超过文件的最大限制
		panic("_bmap: block>big");
	//块号 小于7
	if (block<7) {//小于7,直接块号就可以
		if (create && !inode->i_zone[block]) //crete为1 创建新 为0不创建 , inode->i_zone[block]为0表示对应的块中没有信息
			if (inode->i_zone[block]=new_block(inode->i_dev)) { //新块创建,并把块号放到i_zone中去
				inode->i_ctime=CURRENT_TIME; //时间标志
				inode->i_dirt=1; //修改标志
			}
		return inode->i_zone[block];//返回块号
	}
	//块号 大于等于7 且小于512+7
	block -= 7;//去掉直接块的大小
	if (block<512) {//一次间接块大小小于512
		if (create && !inode->i_zone[7])//创建 && i_zone[7]= 0。说明是首次使用间接块
			if (inode->i_zone[7]=new_block(inode->i_dev)) {//申请一磁盘块来存放间接块信息,把块号写到i_zone[7]
				inode->i_dirt=1;//设置i节点已修改
				inode->i_ctime=CURRENT_TIME;//设置i节点修改时间
			}
		if (!inode->i_zone[7])//i_zone[7]= 0。创建失败
			return 0;
		if (!(bh = bread(inode->i_dev,inode->i_zone[7])))//读取i节点一次间接块,读到高速缓冲区
			return 0;
		i = ((unsigned short *) (bh->b_data))[block];.//取得间接块中第block项中逻辑块号i,每一项占2个字节
		if (create && !i)//create = 1 && i = 0 
			if (i=new_block(inode->i_dev)) {//申请一个盘块,块号赋值给i
				((unsigned short *) (bh->b_data))[block]=i; //让间接块第block项等于该新逻辑块号i
				bh->b_dirt=1;//间接块得高速缓冲区修改标志位置1
			}
		brelse(bh);//释放该间接块占用的高速缓冲区
		return i;//返回磁盘上新申请或原有对应block的逻辑块块号
	}
	//块号 大于512+7 分配二级间接块
	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])))//读取该i节点的二次间接块到缓冲区
		return 0;
	i = ((unsigned short *)bh->b_data)[block>>9];//并读取该二次间接块的一级块第(block / 512)项的逻辑块号i
	if (create && !i)//创建 && i= 0
		if (i=new_block(inode->i_dev)) { //则为i分配一个块
			((unsigned short *) (bh->b_data))[block>>9]=i; //把分配的块号赋值给一级块项上
			bh->b_dirt=1;
		}
	brelse(bh);
	if (!i)
		return 0;
	if (!(bh=bread(inode->i_dev,i)))//读取该i节点的一次间接块到缓冲区
		return 0;
	i = ((unsigned short *)bh->b_data)[block&511];//读取一次间接块上的第(block%511)项直接逻辑块号
	if (create && !i)//创建 && i= 0
		if (i=new_block(inode->i_dev)) {//则为i分配一个块
			((unsigned short *) (bh->b_data))[block&511]=i;//把分配的块号赋值给直接块项上
			bh->b_dirt=1;
		}
	brelse(bh);
	return i;
}
//读取文件数据块block在设备中对应的逻辑块号
//成功:返回对应的逻辑块号。失败:返回0
int bmap(struct m_inode * inode,int block)
{
	return _bmap(inode,block,0);
}
//读取文件数据块block在设备中对应的逻辑块号
//如果对应得逻辑块号不存在就创建一块。返回设备上对应已存在或者新建得逻辑块号
//成功:返回对应的逻辑块号。失败:返回0
int create_block(struct m_inode * inode, int block)
{
	return _bmap(inode,block,1);
}

6.3 释放一个inode节点

主要是对i_count引用次数进行操作,把i节点引用数值减1
并且若是管道i节点,则唤醒等待进程
若是块设备文件i节点则刷新设备
若i节点的链接计数为0,则释放该i节点占用的所有磁盘逻辑块,并释放该节点

void iput(struct m_inode * inode)
{
	if (!inode) //判断i节点的有效性
		return;
	wait_on_inode(inode);//等地啊i节点解锁(如果已经上锁的话)
	if (!inode->i_count)//i节点应用计数是0,表示i节点已经是空闲的,内核还要球释放放回操作,说明其调用代码有问题,显示错误并停机
		panic("iput: trying to free free inode");
	if (inode->i_pipe) {//inode是pipe 管道节点
		wake_up(&inode->i_wait);//唤醒等待管道的等待队列
		if (--inode->i_count)//引用计数减1,如果还有引用则直接返回
			return;
		free_page(inode->i_size);//否则释放管道占用的内存页面。对于管道节点,inode->size存放着内存页地址,参考get_pipe_inode() 288,234行
		//复位该节点的引用计数,已修改标志位和管道标志,并返回。
		inode->i_count=0;
		inode->i_dirt=0;
		inode->i_pipe=0;
		return;
	}
	if (!inode->i_dev) {//i节点对于的设备号是0,则将此节点应用计数递减1,返回。例如用于管道操作的i节点,对于的设备号是0
		inode->i_count--;
		return;
	}
	if (S_ISBLK(inode->i_mode)) {  //如果是块设备,先进行块的同步刷新设备
		sync_dev(inode->i_zone[0]);//同步,i_zone[0]存放的是设备号
		wait_on_inode(inode);//等待inode节点解锁
	}
repeat:
	if (inode->i_count>1) {//引用仍大于1,则说明还被引用,则不能释放
		inode->i_count--; //递减后直接返回
		return;//结束返回
	}
	//引用 <= 1, 则检查链接数和已修改标志位
	if (!inode->i_nlinks) {//i_nlinks链接为0,说明i节点对应文件被删
		truncate(inode);  //释放i节点的所有逻辑块
		free_inode(inode); //释放i节点,即复位i节点对应的i节点位图上的bit位,清空i节点结构内容
		return;//结束返回
	}
	//链接数不为0,则检查已修改标志位
	if (inode->i_dirt) {//修改位是1,则先写盘,在去释放
		write_inode(inode);	/* we can sleep - so do again */ //回写更新i节点
		wait_on_inode(inode);//等待i节点解锁,由于此时其他进程可能修改i节点,因此唤醒后需要再次重复上述判断过程
		goto repeat;//再次回到repeat处执行
	}
	//执行到此,说明引用计数为1, 有链接不为0,修改标志为0。此时只要把i节点引用计数减1,返回。此时i_count = 0 表示已经释放
	inode->i_count--;
	return;
}

6.4 获得一个inode节点

struct m_inode * iget(int dev,int nr)

参数:
dev - 设备号
nr - i节点号

从设备上读取指定节点号的i节点到内存i节点表中,并返回该i节点指针
首先在位于高速缓冲区的i节点表中寻找,若找到指定节点号的i节点则再经过一些判断处理后返回i节点指针
否则通过设备号和指定i节点号,从设备中读取的i节点信息,放入在i节点表中申请的空闲节点中,并返回该i节点指针

struct m_inode * iget(int dev,int nr)//nr是设备第几个节点
{
	struct m_inode * inode, * empty;

	if (!dev) //有效性判断
		panic("iget with dev==0");
	empty = get_empty_inode();//从i节点表中,取出一个空闲的的inode节点备用
	inode = inode_table;
	while (inode < NR_INODE+inode_table) {//遍历inode_table
		if (inode->i_dev != dev || inode->i_num != nr) {//找到和dev和nr都相等的节点
			inode++;
			continue;
		}
		wait_on_inode(inode);//找到后等待节点,等待过程中i节点可能会发生变化
		if (inode->i_dev != dev || inode->i_num != nr) {//等待结束后再次验证,如果发生了变化则再次重新扫描整个i节点表
			inode = inode_table;
			continue;
		}
		inode->i_count++;//找到相应的i节点,把节点引用计数加1
		if (inode->i_mount) {//查看是否是另一个文件系统的安装点。若是则寻找被安装文件系统根节点并返回。
		//意思就是这个节点如果是挂在另一个设备的文件系统,那么需要得到就是这个被挂载的文件系统,获取这个文件系统的根节点即可,
		//那么就需要得到这个设备的设备号dev,和根界节点 1, 就能找到这个文件系统根节点,也就能访问这个文件系统了
			int i;

			for (i = 0 ; i<NR_SUPER ; i++)//在超级块中寻找安装在此i节点的超级块
				if (super_block[i].s_imount==inode) //找到超级快super_block
					break;
			if (i >= NR_SUPER) {//没有找到超级快
				printk("Mounted inode hasn't got sb\n");//显示错误信息
				if (empty)
					iput(empty);//放回本函数开始时获取的空闲节点empty
				return inode;//把在节点表中找到符合要求的i节点返回
			}
			iput(inode);//找到安装到inode节点的文件系统的超级块后,将i节点写盘放回
			dev = super_block[i].s_dev;//获取超级快中的设备号
			nr = ROOT_INO;//令i节点号为ROOT_INO  (#define ROOT_INO 1)
			inode = inode_table;//i节点指向节点链表头,重新扫描整个i节点表,以获取该被安装文件系统的i节点信息
			continue; //回到 while (inode < NR_INODE+inode_table) 
		}
		if (empty)
			iput(empty);//放回本函数开始时获取的空闲节点empty
		return inode; //把在节点表中找到符合要求的i节点返回
	}
	//执行到此,说明没有在i节点表中找到指定的i节点,则利用前面申请的空闲i节点empty,在i节点表中建立该i节点
	if (!empty)//为0返回NULL
		return (NULL);
	inode=empty;//i节点指向empty
	inode->i_dev = dev;//对i节点赋值设备号
	inode->i_num = nr;//对i节点赋值节点号
	read_inode(inode);//根据i节点的dev和nr,从相应的设备上读取信息,复制到i节点中
	return inode;//在节点表中新建的i节点返回
}

struct m_inode * get_empty_inode(void)
{
	struct m_inode * inode;
	static struct m_inode * last_inode = inode_table;
	int i;
	//在inode节点数组中找到一个没有被引用修改的空槽
	do {
		inode = NULL;
		for (i = NR_INODE; i ; i--) {
			if (++last_inode >= inode_table + NR_INODE)
				last_inode = inode_table;
			if (!last_inode->i_count) {
				inode = last_inode;
				if (!inode->i_dirt && !inode->i_lock)
					break;
			}
		}
		//inode =0,没有找到,则打印当前全部的inode
		if (!inode) {
			for (i=0 ; i<NR_INODE ; i++)
				printk("%04x: %6d\t",inode_table[i].i_dev,
					inode_table[i].i_num);
			panic("No free inodes in mem");
		}
		wait_on_inode(inode);
		while (inode->i_dirt) {//i_dirt修改标志位位1,则写回磁盘
			write_inode(inode);
			wait_on_inode(inode);
		}
	} while (inode->i_count);
	memset(inode,0,sizeof(*inode)); //清0 inode节点内存
	inode->i_count = 1;
	return inode;
}

struct m_inode * get_pipe_inode(void)
{
	struct m_inode * inode;

	if (!(inode = get_empty_inode()))
		return NULL;
	if (!(inode->i_size=get_free_page())) {//为当前inode节点分配一个内存页,作为pipe
		inode->i_count = 0;
		return NULL;
	}
	inode->i_count = 2;	/* sum of readers/writers */ //这个节点又读又写,引用置2,一个进程读,一个进程写
	PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;//初始化pipe的头尾
	inode->i_pipe = 1;//i_pipe置1
	return inode; //返回inode节点
}

你可能感兴趣的:(linux内核,linux,内核,文件系统)