Linux内核源代码情景分析-从路径名到目标节点

本文详细分析了Linux内核中从路径名到目标节点的查找过程,涉及__user_walk、cached_lookup和real_lookup等关键步骤。在内存中查找或创建dentry结构,并通过d_lru链入LRU队列。dentry结构与inode、super_block和dentry_operations等紧密关联,最终通过dir->i_op->lookup分配并挂载inode。以ext2文件系统为例,介绍了lookup、iget和d_add等操作。
摘要由CSDN通过智能技术生成

    先看外包装,__user_walk,假设name为/usr/local/hello.c

int __user_walk(const char *name, unsigned flags, struct nameidata *nd)
{
	char *tmp;
	int err;

	tmp = getname(name);//在系统空间分配一个页面,并从用户空间把文件名复制到这个页面
	err = PTR_ERR(tmp);
	if (!IS_ERR(tmp)) {
		err = 0;
		if (path_init(tmp, flags, nd))
			err = path_walk(tmp, nd);
		putname(tmp);
	}
	return err;
}
    第一个参数name为路径名。

    第二个参数flags可以为以下的标志:

#define LOOKUP_FOLLOW		(1) //如果找到的目标只是"符号连接"到其它文件或者目录的一个目录项,则要顺着连接链一直找到终点
#define LOOKUP_DIRECTORY	(2) //表示要寻找的目标必须是个目录
#define LOOKUP_CONTINUE		(4)
#define LOOKUP_POSITIVE		(8)
#define LOOKUP_PARENT		(16) //找到父节点
#define LOOKUP_NOALT		(32)
    第三个参数nd是个结构指针,数据结构nameidata的定义如下:

struct nameidata {
	struct dentry *dentry;
	struct vfsmount *mnt;
	struct qstr last;
	unsigned int flags;
	int last_type;
};

    

    path_init,代码如下:

int path_init(const char *name, unsigned int flags, struct nameidata *nd)
{
	nd->last_type = LAST_ROOT; /* if there are only slashes... */
	nd->flags = flags;
	if (*name=='/')
		return walk_init_root(name,nd);//如果路径名是以"/"开头的绝对路径,那就要通过这个函数从根目录开始查找
	read_lock(&current->fs->lock);
	nd->mnt = mntget(current->fs->pwdmnt);//如果路径名不以"/"开头,那就当前目录开始
	nd->dentry = dget(current->fs->pwd);
	read_unlock(&current->fs->lock);
	return 1;
}

static inline int
walk_init_root(const char *name, struct nameidata *nd)
{
	read_lock(&current->fs->lock);
	if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
		nd->mnt = mntget(current->fs->altrootmnt);
		nd->dentry = dget(current->fs->altroot);
		read_unlock(&current->fs->lock);
		if (__emul_lookup_dentry(name,nd))
			return 0;
		read_lock(&current->fs->lock);
	}
	nd->mnt = mntget(current->fs->rootmnt);
	nd->dentry = dget(current->fs->root);
	read_unlock(&current->fs->lock);
	return 1;
}


    返回到__user_walk,继续执行path_walk,代码如下:

int path_walk(const char * name, struct nameidata *nd)//name为/usr/local/hello.c
{
	struct dentry *dentry;
	struct inode *inode;
	int err;
	unsigned int lookup_flags = nd->flags;

	while (*name=='/')//跳过/usr/local/hello.c最开始的'/'
		name++;
	if (!*name)//如果只有'/',那么直接退出
		goto return_base;

	inode = nd->dentry->d_inode;//根节点的indode结构
	if (current->link_count)
		lookup_flags = LOOKUP_FOLLOW;//如果找到的目标只是"符号连接"到其它文件或者目录的一个目录项,则要顺着连接链一直找到终点

	/* At this point we know we have a real path component. */
	for(;;) {//第一次循环
		unsigned long hash;
		struct qstr this;
		unsigned int c;

		err = permission(inode, MAY_EXEC);
		dentry = ERR_PTR(err);
 		if (err)
			break;

		this.name = name;//已经指向了usr的首字符'u'
		c = *(const unsigned char *)name;

		hash = init_name_hash();
		do {
			name++;
			hash = partial_name_hash(c, hash);
			c = *(const unsigned char *)name;
		} while (c && (c != '/'));//如果字符c为空或者遇到了'/'则退出循环,本例中c指向的usr后面的'/'
		this.len = name - (const char *) this.name;//usr的长度
		this.hash = end_name_hash(hash);

		/* remove trailing slashes? */
		if (!c)//如果字符c为空,那么说明是最后一个节点,且最后一个节点是文件
			goto last_component;
		while (*++name == '/');//跳过'/'
		if (!*name)//如果'/'后面为空,那么说明这也是最后一个节点,且最后一个节点是目录
			goto last_with_slashes;

		/*
		 * "." and ".." are special - ".." especially so because it has
		 * to be able to know about the current root directory and
		 * parent relationships.
		 */
		if (this.name[0] == '.') switch (this.len) {//执行到这里,this代表中间节点,中间节点一定是目录
			default:
				break;
			case 2:	
				if (this.name[1] != '.')//如果当前节点是..,那么要表示上一级目录
					break;
				follow_dotdot(nd);
				inode = nd->dentry->d_inode;
				/* fallthrough */
			case 1://如果当前的节点是 . ,表示当前目录
				continue;
		}
		/*
		 * See if the low-level filesystem might want
		 * to use its own hash..
		 */
		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {//如果有d_hash这个函数
			err = nd->dentry->d_op->d_hash(nd->dentry, &this);//使用这个函数,重新计算hash值
			if (err < 0)
				break;
		}
		/* This does the actual lookups.. */
		dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//在内存中寻找该节点业已建立的dentry结构
		if (!dentry) {//如果没有找到
			dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//那么就要建立该节点的dentry结构
			err = PTR_ERR(dentry);
			if (IS_ERR(dentry))
				break;
		}
		/* Check mountpoints.. */
		while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))//检查是否是挂载点
			;

		err = -ENOENT;
		inode = dentry->d_inode;
		if (!inode)
			goto out_dput;
		err = -ENOTDIR; 
		if (!inode->i_op)
			goto out_dput;

		if (inode->i_op->follow_link) {//看看这个指针是否为NULL,这个指针是在ext2_read_inode中设置的
			err = do_follow_link(dentry, nd);
			dput(dentry);
			if (err)
				goto return_err;
			err = -ENOENT;
			inode = nd->dentry->d_inode;
			if (!inode)
				break;
			err = -ENOTDIR; 
			if (!inode->i_op)
				break;
		} else {
			dput(nd->dentry);
			nd->dentry = dentry;//如果没有,直接附给nd->dentry
		}
		err = -ENOTDIR; 
		if (!inode->i_op->lookup)//iop指针也是ext2_read_inode中设置的为ext2_dir_inode_operations,因为当前/usr为目录节点,所以一定要有ext2_lookup这个指针
			break;
		continue;
		/* here ends the main loop */

last_with_slashes:
		......
last_component:
		......
}
struct qstr {
	const unsigned char * name;
	unsigned int len;
	unsigned int hash;
};
    

    cached_lookup,在内存中寻找该节点业已建立的dentry结构,代码如下:

static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags)
{
	struct dentry * dentry = d_lookup(parent, name);

	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
		if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {
			dput(dentry);
			dentry = NULL;
		}
	}
	return dentry;
}
struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
{
	unsigned int len = name->len;
	unsigned int hash = name->hash;
	const unsigned char *str = name->name;
	struct list_head *head = d_hash(parent,hash);
	struct list_head *tmp;

	spin_lock(&dcache_lock);
	tmp = head->next;
	for (;;) {
		struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
		if (tmp == head)
			break;
		tmp = tmp->next;
		if (dentry->d_name.hash != hash)//比较hash值
			continue;
		if (dentry->d_parent != parent)//比较父节点的dentry结构是否一样
			continue;
		if (parent->d_op && parent->d_op->d_c
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值