到这里我们可以知道,对文件或设备驱动的判断与函数集的赋值都是在文件系统这一级区分的,也就是说在有 open 操作时是到具体的文件系统,如ext3,并在 ext3 上再次区分出是否为设备驱动,这点很好理解,因为设备驱动也是以文件形式存在的。分析到这里可以算是把对设备驱动的操作流程弄清晰了。但这仅是对设备驱动的操作,别忘了上面还存在一大堆的疑问,我们知道只有在触发调用了 ext3_read_inode() 时才会区分,那么它何时被调用的?什么情况下调用的?为什么 ext3 的文件操作集中的 open 是空操作呢?要解答这些问题,我们仍要从 open_namei() 函数开始进行分析。
在这个函数的实现过程中会根据标志的不同将路径名转换成 struct nameidata结构,当得到此结构后还会根据目录,连接等做不同处理。这里我们只关心操作流程,所以只对 path_look_xxx() 函数做跟踪分析。
int open_namei(int dfd, const char *pathname, int flag,
int mode, struct nameidata *nd)
{
int acc_mode, error;
struct path path;
struct dentry *dir;
int count = 0;
//
// 篇幅所限,在这个函数中我们只列出相关代码。
//
......
//
// 判断是否是建立标志,如果不是则使用 path_lookup_open()
//
if (!(flag & O_CREAT)) {
//
// 通过路径名查询 inode, dentry 并返回 nameidata 结构。
//
error = path_lookup_open(dfd, pathname, lookup_flags(flag),
nd, flag);
if (error)
return error;
goto ok;
}
/*
* Create - we need to know the parent.
*/
//
// 如果是建立标志则使用 path_lookup_create()
//
error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
if (error)
return error;
......
}
上面的 path_lookup_open() 与 path_lookup_create() 都是一个很简单的封状无条件的调用了 __path_lookup_intent_open() 函数,只不过是传输标志不同而已。此函数在预先填充一些 struct nameidata 结构后继续调用 do_path_lookup()完成查找。
static int __path_lookup_intent_open(int dfd, const char *name,
unsigned int lookup_flags, struct nameidata *nd,
int open_flags, int create_mode)
{
//
// 获得一个空的 struct file 结构
//
struct file *filp = get_empty_filp();
int err;
if (filp == NULL)
return -ENFILE;
//
// 先填充要返回的 struct nameidata 结构中的相关字段
//
nd->intent.open.file = filp;
nd->intent.open.flags = open_flags;
//
// 填充建立标志位,这个也就是 path_lookup_open()
// 与 path_lookup_create() 函数调用的区别
//
nd->intent.open.create_mode = create_mode;
//
// 根据路径调用 do_path_lookup() 得到一个 struct nameidata 结构
//
err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
if (IS_ERR(nd->intent.open.file)) {
if (err == 0) {
err = PTR_ERR(nd->intent.open.file);
path_release(nd);
}
} else if (err != 0)
release_open_intent(nd);
return err;
}
此函数根据 "/" 根路径与 AT_FDCWD 标志从不同位置得到 struct vfsmount 与struct dentry 结构来填充 struct nameidata 中的相关字段,这里应该仅是占位用。最终路径分解工作与查找由 link_path_walk() 函数来完成。
static int fastcall do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
int retval = 0;
int fput_needed;
struct file *file;
//
// 当前进程的 struct file 集
//
struct fs_struct *fs = current->fs;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
nd->depth = 0;
//
// 如果路径是根目录则从 fs_struct->altrootmnt 与 fs_struct->altroot
// 中得到 struct vfsmount 与 struct dentry 结构
//
if (*name=='/') {
read_lock(&fs->lock);
if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
nd->mnt = mntget(fs->altrootmnt);
nd->dentry = dget(fs->altroot);
read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
goto out; /* found in altroot */
read_lock(&fs->lock);
}
nd->mnt = mntget(fs->rootmnt);
nd->dentry = dget(fs->root);
read_unlock(&fs->lock);
//
// 如果路径不是根目录且有 AT_FDCWD 标志则从 fs_struct->pwdmnt
// 与 fs_struct->pwd 中得到 struct vfsmount 与 struct dentry 结构
// 这里应该表示是当前目录? FIXME
//
} else if (dfd == AT_FDCWD) {
read_lock(&fs->lock);
nd->mnt = mntget(fs->pwdmnt);
nd->dentry = dget(fs->pwd);
read_unlock(&fs->lock);
//
// 如果以上都不是的话则使用 fget_light() 得到一个 struct file
// 并从 struct file->f_vfsmnt 中得到 struct vfsmount 结构,而
// struct dentry 则使用 struct file->f_dentry 中的
//
} else {
//
// 注意:这里声明了一个 struct dentry 结构
//
struct dentry *dentry;
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
//
// 使用 struct file 中的来填充
//
dentry = file->f_dentry;
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_fail;
retval = file_permission(file, MAY_EXEC);
if (retval)
goto fput_fail;
nd->mnt = mntget(file->f_vfsmnt);
nd->dentry = dget(dentry);
fput_light(file, fput_needed);
}
current->total_link_count = 0;
//
// 注意:这个函数才真正的分解路径,调用实际文件系统的操作。
// 它本身也是个简单封状,实际是使用 __link_path_walk() 函数
// 完成操作。
//
retval = link_path_walk(name, nd);
out:
if (likely(retval == 0)) {
if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode);
}
out_fail:
return retval;
fput_fail:
fput_light(file, fput_needed);
goto out_fail;
}
在 link_path_walk() 函数中实际使用的函数为 __link_path_walk(),在这个函数中分解路径,并依次调用 do_lookup() 函数完成实际的转换工作,do_lookup()才有可能去调用实际文件系统的读磁盘 inode 操作。结合上下文,我们只关心读取流程,不对路径分解算法做分析,所以只提取相关代码。
static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
{
struct path next;
struct inode *inode;
int err;
unsigned int lookup_flags = nd->flags;
//
// 篇幅所限,在这个函数中我们只列出相关代码。
//
//
// 这里是一个大循环,目的是用来分解路径并在
// 分解的中间过程使用 do_lookup() 得到相关的
// inode 一直到最后指定的文件或路径。也就是说对
// 于象 /dir/temp/readme.txt 这种路径会首先从
// 根一直分解并调用 do_lookup() 得到其 inode
// 一直到得到最后的 readme.txt 为止。
//
for(;;) {
.......
//
// 从缓存或调用实际文件系统函数获取 inode 信息
//
err = do_lookup(nd, &this, &next);
if (err)
break;
......
last_with_slashes:
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
/* Clear LOOKUP_CONTINUE iff it was previously unset */
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
.......
//
// 这里是去掉了 LOOKUP_CONTINUE 标志后,又调用了一次。
//
err = do_lookup(nd, &this, &next);
if (err)
break;
......
}
path_release(nd);
return_err:
return err;
}
到这里才是查找对应 struct dentry 的具体操作,此函数首先从缓存中尝试获取struct dentry 结构。如果获取失败,则调用 real_lookup() 函数使用实际文件系统方法来读取 inode 信息。这里要明确 struct dentry 中包含了 struct inode信息。
static int do_lookup(struct nameidata *nd, struct qstr *name,
struct path *path)
{
struct vfsmount *mnt = nd->mnt;
//
// 从 hlist 中获取 struct dentry 结构,hlist 代表的是
// 一个 inode 的缓存即是一个 HASH 表。
//
struct dentry *dentry = __d_lookup(nd->dentry, name);
//
// 如果没有找到则会调用 real_lookup() 实际文件系统方法
// 从磁盘中获取
//
if (!dentry)
goto need_lookup;
if (dentry->d_op && dentry->d_op->d_revalidate)
goto need_revalidate;
done:
//
// 如果从缓存中找到,则设置 struct path 并返回
//
path->mnt = mnt;
path->dentry = dentry;
__follow_mount(path);
return 0;
need_lookup:
//
// 使用实际文件系统方法,从磁盘中获得 inode 信息
//
dentry = real_lookup(nd->dentry, name, nd);
if (IS_ERR(dentry))
goto fail;
goto done;
need_revalidate:
dentry = do_revalidate(dentry, nd);
//
// 这里是缓存的分之。如果 struct dentry 无效还是需要调
// 用 real_lookup() 读取
//
if (!dentry)
goto need_lookup;
if (IS_ERR(dentry))
goto fail;
goto done;
fail:
return PTR_ERR(dentry);
}
在分析 real_lookup() 函数前,我们先来看一下 ext3 文件系统的 inode结构。很明显可以看出 lookup 指向了 ext3_lookup() 函数。
struct inode_operations ext3_dir_inode_operations = {
//
// 为了更清晰,在这个结构中只列出我们感兴趣的字段
//
......
.lookup = ext3_lookup,
......
};
此函数先从缓存中查找对应的 inode,如果没有则新分配一个 struct dentry结构,然后调用 parent->d_inode->i_op->lookup 即调用了 ext3_lookup()函数来查找 inode。
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
struct dentry * result;
//
// 篇幅所限,在这个函数中我们只列出相关代码。
//
//
// 获得上一层目录的 inode。别忘了我们是分解路径依次
// 调用的,所以上一层的 inode 肯定是存在的。
//
struct inode *dir = parent->d_inode;
......
//
// 先从缓存里查找。
//
result = d_lookup(parent, name);
if (!result) {
//
// 没找到的话,新分配一个 struct dentry 结构
// 注意:我们这里新分配了一个 struct dentry,
// 也就是说每一个目录或文件都需要一个 dentry 结构。
//
struct dentry * dentry = d_alloc(parent, name);
result = ERR_PTR(-ENOMEM);
if (dentry) {
//
// 这里也就是调用了 ext3_lookup() 函数,可以
// 看下上面介绍的 ext3_dir_inode_operations
// 结构
//
result = dir->i_op->lookup(dir, dentry, nd);
if (result)
dput(dentry);
else
result = dentry;
}
mutex_unlock(&dir->i_mutex);
return result;
}
.......
}
这里到了实际文件系统的查找函数。首先根据第一个参数,也就是上级的 dentry从 ext3_dir_entry_2 中得到新的 dentry 结构,并从其中得到相关的 inode number,再调用 iget() 函数去获取相应的 struct inode 结构,最后将此 inode 与 dentry进行关联。
static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
struct inode * inode;
struct ext3_dir_entry_2 * de;
struct buffer_head * bh;
if (dentry->d_name.len > EXT3_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
//
// 得到新的 dentry 并返回一个磁盘缓存 buffer_head 结构
// 注意:这个 dentry 虽然是新分配的,但它所指向的 d_parent
// 与 d_inode 是有效的,也就是说上级目录相关信息是有效的。
// 返回的 de 里包含了 inode number。
//
bh = ext3_find_entry(dentry, &de);
//
// 注意:这里的 inode 默认置为 NULL
//
inode = NULL;
if (bh) {
unsigned long ino = le32_to_cpu(de->inode);
brelse (bh);
//
// 如果对应的超级块(super block)无效则直接返回错误
//
if (!ext3_valid_inum(dir->i_sb, ino)) {
ext3_error(dir->i_sb, "ext3_lookup",
"bad inode number: %lu", ino);
inode = NULL;
} else
//
// 有效则调用 iget() 函数得到正确的 struct inode
// 其实也就是根据超级块(super block)的函数集获取
//
inode = iget(dir->i_sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
}
//
// 关键此 inode 对应的 dentry 结构并返回。
//
return d_splice_alias(inode, dentry);
}
在分析 iget() 函数之前,有必要先了解下超级块(super block)中的相关字段与函数。
struct super_block {
//
// 为了更清晰,在这个结构中只列出我们感兴趣的字段
//
......
//
// 文件系统结构。在下面介绍 mount 挂载文件系统时
// 会有详细介绍。
//
struct file_system_type *s_type;
//
// 超级块(super block)函数集
//
struct super_operations *s_op;
......
};
共3页: 上一页 [1] 2 [3] 下一页 |