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

本文详细介绍了Linux内核源码中关于文件系统部分的Namei.c,讲解了目录项、i节点与文件名的关系,以及match()、find_entry()等关键函数的工作原理。通过对目录项结构、i节点和目录之间的联系的分析,阐述了在Linux中查找和管理文件的过程。
摘要由CSDN通过智能技术生成

目录项:i节点和文件名的联系      

        接下来的学习会遇到目录是怎么存储的,以及之间的一些关系。所以这里先总结下有关目录的关系;

        先来了解下i节点结构:

//内存中i节点结构,前7项和d_inode完全一样
struct m_inode {
	unsigned short i_mode;      //文件类型和属性(rwx位)
	unsigned short i_uid;		//用户id
	unsigned long i_size;		//文件大小
	unsigned long i_mtime;		//修改时间(1970.1.1-- /秒)
	unsigned char i_gid;		//组id
	unsigned char i_nlinks;		//文件目录项链接数
	unsigned short i_zone[9];   //指向逻辑块号
/* these are in memory also */
	struct task_struct * i_wait; //等待该i节点的进程队列
	unsigned long i_atime;  	 //最后访问时间,active
	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;		 //是否上锁
	unsigned char i_dirt;		 //已修改标识
	unsigned char i_pipe;		 //管道标志
	unsigned char i_mount;		 //安装标志
	unsigned char i_seek;		 //搜索标志
	unsigned char i_update;		 //更新标志
};
        里面有很多个字段在前面已经学习使用过,这里要说到的是i_zone[9],如果对于目录来说这个数组指定的逻辑号对应的逻辑块里面存放的就是目录项;如果对于文件来说,那么里面存放的就是文件数据了。

        i_size 字段表示这个i节点代表的文件大小,其实就是i_zone[9]数组中使用了多少磁盘;

        知道i节点结构后,再来看看目录项结构:

//文件目录项结构
struct dir_entry {
	unsigned short inode; //该文件名对应的i节点号
	char name[NAME_LEN]; //文件名字符串
};
        这个结构体把文件名和i节点进行了联系,inode是i节点号,name字符数组是文件名;

        下面通过实际例子来说明下这个目录间的关系:在根目录下(/)用命令:ls 显示下  


        会显示出很多的目录来,在底层他们的关系是这样的:根节点/ 有一个目录项dir_entry,其中inode为1,目录名称为/;根据/的dir_entry中的i节点号,可以得到对应的i节点结构,在该i节点结构体中i_zone[i]保存了根目录下的一些文件的目录项(bin  boot  cgroup等等);其他目录下的文件关系也是一样的。

        所以如果你要查找/home/yzh/test/文件,首先是根据文件名/得到i节点号(根目录是特殊的文件目录,由1号i节点专门指定),在该i节点中的i_zone[i]中依次搜索每个目录项;然后让目录项中的名称和yzh字符串进行比较,如果相同,则表示找到了yzh/的目录项了;在yzh目录项中得到yzh目录对应的i节点号,然后在该i节点中继续搜索各个目录项,再让目录项中名称和test进行比较,如果相同,则表示找到了test的目录项了。在查找某个文件时,就是利用这种方法来解决的。

match()

        1、功能和stncmp()函数一样,都是对指定字符串进行比较。比较指定长度len的name和de结构体中的name进行比较,相等返回1,否则返回0;

 //字符串匹配,参数分别为:比较长度,文件名指针,目录项结构体
static int match(int len,const char * name,struct dir_entry * de)
{
	register int same __asm__("ax");//定义寄存器级的变量

//检查范围
	if (!de || !de->inode || len > NAME_LEN)
		return 0;
	if (len < NAME_LEN && de->name[len])//de->name[len]为null表示len大于name的长度
		return 0;
	__asm__("cld\n\t"
		"fs ; repe ; cmpsb\n\t"//循环比较esi++和edi++
		"setz %%al"//如果z=0,则设置al = 1
		:"=a" (same)
		:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
		:"cx","di","si");
	return same;
}

find_entry()     

        2、static struct buffer_head * find_entry(struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir)函数,在某个i节点下查找文件名为name的目录项

        功能:在某个目录下搜索所有目录项,查找一个目录项中名称为name的。返回该目录项所在的逻辑块映射的缓存块,在参数中返回查找到的目录项。这是一级目录中查找;在dir节点下的数据块中查找到名称为name的目录项,并且返回该目录项所在的数据块的映射缓存块;参数 res_dir中保持了查找到的目录项;

        参数:dir 在它的结构体中的数据块i_zone[i]中查找目录项; name 需要查找到的目录项中的名称; namelen 需要匹配的目录项中名称的长度; res_dir 查找到的目录项将会存放到这里;

        实现:其实这个函数的实现主要分为两个部分,第一部分是处理文件名为..(父级目录),第二部分是在文件数据块中查找指定文件名的目录项;

        处理文件名为..:1、如果dir节点是当前进程的根节点(在current中会有记录),那么就把..变成.,因为这已经是进程根节点了,说明该进程已经没有权限访问(其实也没有方法)去访问其父节点了;2、如果dir节点是文件系统的根节点,那么就把..指向文件系统的安装节点(方法是:根据dir得到超级块,根据超级块就可以得到安装点);

        查找目录项:根据dir节点结构体中的数据块(i_zone[0]~~i_zone[9])中保存的逻辑号,和dir节点所在的设备,映射得到一个缓存块   ====》  用name在dir数据块中的目录项进行匹配查找,如果查找到了,则把目录项赋值给参数 res_dir;并且返回该目录项所在的数据块(其实就是dir节点中的某个 i_zone[i]的映射缓存块)   ====》  如果第 i 块i_zone[i]中没有找到名称为name的目录项,则查找第 i + 1 块i_zone[i+1]中是否有,直到查找的目录项大于dir节点结构中最大的目录项数,则结束;   

//查找指定目录和文件名的目录项,返回一个含有目录项的映射缓存块
 //参数:dir 指定目录i节点的指针;name 文件名;namelen  文件名长度;
 //res_dir  表示查找到后的目录项
static struct buffer_head * find_entry(struct m_inode ** dir,
	const char * name, int namelen, struct dir_entry ** res_dir)
{
	int entries;
	int block,i;
	struct buffer_head * bh;
	struct dir_entry * de;
	struct super_block * sb;

//对文件名长度的控制
#ifdef NO_TRUNCATE
	if (namelen > NAME_LEN)
		return NULL;
#else
	if (namelen > NAME_LEN)
		namelen = NAME_LEN;
#endif
    //得到指定目录i节点下存放了多少个目录项结构
	entries = (*dir)->i_size / (sizeof (struct dir_entry));
	*res_dir = NULL;
	if (!namelen)//如果需要查找的文件名为空
		return NULL;
	
 //如果是文件名是".."	
/* check for '..', as we might have to do some "magic" for it */
	if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
/* '..' in a pseudo-root results in a faked '.' (just change namelen) */
		if ((*dir) == current->root)//如果当前要查找的目录项i节点就是当前进程的根目录i节点
			namelen=1;//那么..就不能再回退到其父节点了(比如在/目录下cd ..),直接
		else if ((*dir)->i_num == ROOT_INO) {//如果参数目录是根节点
		//则导致目录交换到被安装文件系统的目录i节点上
/* '..' over a mount-point results in 'dir' being exchanged for the mounted
   directory-inode. NOTE! We set mounted, so that we can iput the new dir */
			sb=get_super((*dir)->i_dev);//把..指向于安装文件系统的目录i节点上
			if (sb->s_imount) {
				iput(*dir);
				(*dir)=sb->s_imount;
				(*dir)->i_count++;
			}
		}
	}
	if (!(block = (*dir)->i_zone[0]))//第一个直接数据块,如果为0,则退出
		return NULL;
	if (!(bh = bread((*dir)->i_dev,block)))//把第一个直接块映射到缓存中
		return NULL;
	i = 0;
	//得到参数目录项文件中指定的第一个直接块映射的缓存块
	de &#
  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值