先看外包装,__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(¤t->fs->lock);
nd->mnt = mntget(current->fs->pwdmnt);//如果路径名不以"/"开头,那就当前目录开始
nd->dentry = dget(current->fs->pwd);
read_unlock(¤t->fs->lock);
return 1;
}
static inline int
walk_init_root(const char *name, struct nameidata *nd)
{
read_lock(¤t->fs->lock);
if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
nd->mnt = mntget(current->fs->altrootmnt);
nd->dentry = dget(current->fs->altroot);
read_unlock(¤t->fs->lock);
if (__emul_lookup_dentry(name,nd))
return 0;
read_lock(¤t->fs->lock);
}
nd->mnt = mntget(current->fs->rootmnt);
nd->dentry = dget(current->fs->root);
read_unlock(¤t->fs->lock);
return 1;
}
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_compare) {
if (parent->d_op->d_compare(parent, &dentry->d_name, name))
continue;
} else {
if (dentry->d_name.len != len)//比较长度
continue;
if (me

本文详细分析了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等操作。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



