Linux内核源码分析--文件系统(五、Inode.c)


_bmap()

        1、_bmap()函数用于把一个文件数据块映射到盘块的处理操作

        Linux内核源码分析--文件系统(五、Inode.c)_第1张图片

        因为一个i节点对应一个文件,所以上面的i节点映射的逻辑块号就是文件数据存放的逻辑块号;i_zone[0]到i_zone[6]是直接逻辑块号,i_zone[7]是一次间接逻辑块号,i_zone[8]是二次间接逻辑块号;文件中的数据存放在哪个硬盘上的逻辑块上就是由这个数组来映射的,根据这个也可以知道一个文件的最大存储是多少?

//把文件上的数据块映射到磁盘上,inode 文件i节点;block 文件中数据块号,create是否创建标志
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");

	//使用直接块
	if (block<7) {
		if (create && !inode->i_zone[block])//创建标志置位,i节点对应的逻辑块字段为0
			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) {
		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->i_zone[7])//表示创建间接块磁盘失败,或者create未置1
			return 0;
		if (!(bh = bread(inode->i_dev,inode->i_zone[7])))//读取间接块的信息
			return 0;
		i = ((unsigned short *) (bh->b_data))[block];//得到间接块上block所处的位置,判断是否为0
		if (create && !i)//如果间接块号上的block位置为0,create置位
			if (i=new_block(inode->i_dev)) {//申请一个新的逻辑块给block
				((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;
	//获取block二级间接块号在指定的哪个二级间接块的一级块号
	i = ((unsigned short *)bh->b_data)[block>>9];//右移9位表示整除512,得到一级块号
	if (create && !i)//如果二级间接块中二级块不存在,则要为该二级块申请一个逻辑块号
		if (i=new_block(inode->i_dev)) {//得到一个逻辑块用来存放二级间接块中的二级间接块号
			((unsigned short *) (bh->b_data))[block>>9]=i;//block>>9 === block/512 为二级块映射一个逻辑块
			bh->b_dirt=1;
		}
	brelse(bh);
	if (!i)
		return 0;
	if (!(bh=bread(inode->i_dev,i)))//读取二级间接块中的二级块映射的缓存块
		return 0;
	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;//返回逻辑块号
}

input()

        2、iput()函数是把i节点的引用递减,如果i节点引用为1,则递减删除i节点,并且设置相关属性;如果是管道文件或者块设备文件,则另外进行处理;

//放置一个i节点,减去1个i节点的引用
void iput(struct m_inode * inode)
{
	if (!inode)
		return;
	wait_on_inode(inode);
	if (!inode->i_count)//如果i节点已经是0,那么死机
		panic("iput: trying to free free inode");
	if (inode->i_pipe) {//如果是管道文件
		wake_up(&inode->i_wait);//唤醒等待该管道的进程
		if (--inode->i_count)//引用减去1
			return;//如果还被其他程序引用,则直接返回;否则就设置一些属性和释放内存
		free_page(inode->i_size);//释放管道使用的内存页面
		inode->i_count=0;//对i节点属性字段设置
		inode->i_dirt=0;
		inode->i_pipe=0;
		return;
	}
	//i节点对应的设备号为0,引用递减后返回;如,管道操作i节点
	if (!inode->i_dev) {
		inode->i_count--;
		return;
	}
	//如果是块设备文件的i节点,刷新该设备
	if (S_ISBLK(inode->i_mode)) {
		sync_dev(inode->i_zone[0]);//zone[0]是设备号
		wait_on_inode(inode);
	}
repeat:
	if (inode->i_count>1) {//引用递减  返回
		inode->i_count--;
		return;
	}
	if (!inode->i_nlinks) {//链接数为0, 表示文件已经删除,置文件长度为0,释放i节点
		truncate(inode);
		free_inode(inode);
		return;
	}
	if (inode->i_dirt) {//已经更改过
		write_inode(inode);	/* we can sleep - so do again *///把i节点信息写入缓存中
		wait_on_inode(inode);
		goto repeat;//经过睡眠,要再一次确认
	}
	inode->i_count--;//递减返回,文件还存在
	return;
}

get_empty_inode()

        3、get_empty_inode(void)函数 表示在i节点数组中查找一个空闲的i节点,进行设置,然后返回该i节点;

//从i节点表中获取一个空闲i节点项
struct m_inode * get_empty_inode(void)
{
	struct m_inode * inode;
	static struct m_inode * last_inode = inode_table;
	int i;

	do {
		inode = NULL;
		for (i = NR_INODE; i ; i--) {
			if (++last_inode >= inode_table + NR_INODE)//如果last_inode已经到了最后一项,则重新扫描
				last_inode = inode_table;
			if (!last_inode->i_count) {//引用为0,
				inode = last_inode;
				if (!inode->i_dirt && !inode->i_lock)//没有修改,没有上锁,就选择这个i节点
					break;
			}
		}
		
		//没有找到i节点,打印i节点列表,供调试,死机
		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);
		//若i节点信息修改过,则刷新等待i节点解锁
		while (inode->i_dirt) {
			write_inode(inode);
			wait_on_inode(inode);
		}
	} while (inode->i_count);//如果i节点又被别的进程使用,那么再找一个i节点

	//对空闲i节点详内容清零,设置引用,返回i节点指针
	memset(inode,0,sizeof(*inode));
	inode->i_count = 1;
	return inode;
}

iget()

        4、iget(int dev, int nr)函数 根据设备号和i节点号得到i节点信息;其中有个重点判断的是该i节点是否是某个文件系统的安装节点。如果是,则把i节点释放一个引用;并且重新在该文件系统中查找i节点(也就是根节点)

//从设备上读取指定i节点号的i节点信息
//参数 dev设备号,nr  i节点号
//1、在内存中的i节点列表中扫描,若查找到了,则返回i节点指针
//2、如果上面没有找到,则从设备上读取指定i节点号的i节点信息放入内存i节点表中,返回i节点指针
struct m_inode * iget(int dev,int nr)
{
	struct m_inode * inode, * empty;

	if (!dev)
		panic("iget with dev==0");
	empty = get_empty_inode();//得到一个空闲i节点
	inode = inode_table;//i节点列表数组
	
	while (inode < NR_INODE+inode_table) {
		if (inode->i_dev != dev || inode->i_num != nr) {//设备号或者i节点号不相等,则看下一个
			inode++;
			continue;
		}
		//解锁,然后再确认下设备号和i节点号是否改变了
		wait_on_inode(inode);
		if (inode->i_dev != dev || inode->i_num != nr) {
			inode = inode_table;
			continue;
		}
		inode->i_count++;//引用

		//如果找到的i节点是其他文件系统的安装节点
		if (inode->i_mount) {
			int i;
			
			for (i = 0 ; i<NR_SUPER ; i++)
				if (super_block[i].s_imount==inode)//看看是哪个文件系统的安装节点
					break;

			//如果没有找到安装在此i节点上的超级块,则释放申请的空闲i节点	
			if (i >= NR_SUPER) {
				printk("Mounted inode hasn't got sb\n");
				if (empty)
					iput(empty);//释放空闲i节点
				return inode;
			}

			//找到i节点安装的超级块,获取到该文件系统的设备号,得到nr为根节点1,重新在该文件系统中查找根i节点
			iput(inode);
			dev = super_block[i].s_dev;
			nr = ROOT_INO;
			inode = inode_table;
			continue;
		}
		//如果找到的i节点不是,其他文件系统的安装节点,
		//则释放空节点,返回i节点
		if (empty)
			iput(empty);
		return inode;
	}
	//如果在i节点列表中,没有找到指定i节点,则自己新建一个
	if (!empty)
		return (NULL);
	inode=empty;
	inode->i_dev = dev;
	inode->i_num = nr;
	read_inode(inode);//从设备上读取该i节点
	return inode;
}

read_inode()

        5、读取设备上指定i节点的信息到缓存中,其中要分清楚,参数中的i节点是内存中使用的i节点,而程序后面是读取到硬盘上的i节点;这应该是从硬盘上的i节点中加载其信息到内存中的i节点处,也就是更新i节点信息;所以用读i节点信息(把i节点信息从硬盘上读取到缓存区中,从硬盘使用的i节点更新到内存使用的i节点)

//从设备上读取指定i节点的信息到缓存中
static void read_inode(struct m_inode * inode)
{
	struct super_block * sb;
	struct buffer_head * bh;
	int block;

	lock_inode(inode);
	if (!(sb=get_super(inode->i_dev)))//根据设备号获取到超级块
		panic("trying to read inode without dev");

	//下面是计算i节点逻辑号,引导块 超级块  两个位图 然后就是(i节点号/每块所拥有的i节点数)
	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
		(inode->i_num-1)/INODES_PER_BLOCK;

	//从磁盘上获取到i节点信息
	if (!(bh=bread(inode->i_dev,block)))
		panic("unable to read i-node block");
	*(struct d_inode *)inode =
		((struct d_inode *)bh->b_data)
			[(inode->i_num-1)%INODES_PER_BLOCK];//这是从硬盘中i节点上获取到数据,更新内存中i节点内容
	brelse(bh);
	unlock_inode(inode);
}

write_inode()

        6、将指定i节点信息写入磁盘中,和上面的read_inode()函数正好相反,该函数是由内存中使用的i节点写入到硬盘上使用的i节点;把内存中修改过的i节点信息更新到硬盘上使用的i节点信息;

//将i节点信息写入缓存区中
static void write_inode(struct m_inode * inode)//参数是内存中使用的i节点结构
{
	struct super_block * sb;
	struct buffer_head * bh;
	int block;

	lock_inode(inode);
	if (!inode->i_dirt || !inode->i_dev) {
		unlock_inode(inode);
		return;
	}
	if (!(sb=get_super(inode->i_dev)))
		panic("trying to write inode without device");
	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
		(inode->i_num-1)/INODES_PER_BLOCK;//根据i节点号,找到磁盘逻辑块上i节点位置
	if (!(bh=bread(inode->i_dev,block)))//把该i节点磁盘块读取到缓存区
		panic("unable to read i-node block");
	((struct d_inode *)bh->b_data)//把该缓存区强转为 磁盘使用的i节点结构
		[(inode->i_num-1)%INODES_PER_BLOCK] =//根据i节点号,取余得到在该块磁盘上第几个位置为i节点
			*(struct d_inode *)inode;//由内存i节点强转为磁盘i节点结构,因为磁盘i节点结构和内存i节点结构前几位一样
	bh->b_dirt=1;
	inode->i_dirt=0;
	brelse(bh);
	unlock_inode(inode);
}
         其实上面的读写i节点信息,只是在后面的转换顺序不一样。是在d_inode和m_inode结构体之间做文章的,因为两个结构体中前7项是一样的,也就是说前面7项是可以共用,可以改变的,而m_inode结构体中后面几项d_inode结构体中是没有的(其实这也是确定i节点的唯一性),所以也就不存在是否同步,是否一样。

        读i节点顺序是:*(struct d_inode *)inode = ((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK];从硬盘映射的缓存区中读取到i节点信息到内存i节点结构体中;

        写i节点顺序是:((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK] =*(struct d_inode *)inode;把i节点结构体内容从内存中写入到硬盘映射的缓存区中使用的i节点结构体(其实也就是硬盘上使用的i节点);

        转载请注明作者和原文出处,原文地址:http://blog.csdn.net/yuzhihui_no1/article/details/43951153

        若有不正确之处,望大家指正,共同学习!谢谢!!


你可能感兴趣的:(操作系统,文件系统,高速缓存,linux内核源码)