------------------------------以上内容转自Linux中的VFS实现 [二] - 知乎-----------------------------
O、dentry是什么
dentry是一个纯粹的内存结构,由文件系统在提供文件访问的过程中在内存中直接建立。
首先假设不存在dentry这个数据结构,我们看下我们可能会面临什么困境:
比如我要打开/usr/bin/vim 文件, 1 首先需要去/所在的inode找到/的数据块,从/的数据块中读取到usr这个条目的inode, 2 跳转到user 对应的inode,根据/usr inode 指向的数据块,读取到/usr 目录的内容,从中读取到bin这个条目的inode 3 跳转到/usr/bin/对应的inode,根据/usr/bin/指向的数据块,从中读取到/usr/bin/目录的内容,从里面找到vim的inode
我们都知道,Linux提供了page cache页高速缓存,很多文件的内容已经缓存在内存里,如果没有dentry,文件名无法快速地关联到inode,即使文件的内容已经缓存在页高速缓存,但是每一次不得不重复地从磁盘上找出来文件名到VFS inode的关联。【转自linux文件系统基础--VFS中的file、dentry和inode--讲得非常透的一篇文章_jinking01的专栏-CSDN博客】
一、inode是什么
理解inode,要从文件储存说起。
文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是4KB,即连续八个sector组成一个block。
文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。
二、inode内容
inode包含文件的元信息,具体来说有以下内容:
* 文件的字节数
* 文件拥有者的User ID
* 文件的Group ID
* 文件的读、写、执行权限
* 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
* 链接数,即有多少文件名指向这个inode
* 文件数据block的位置
可以用stat命令,查看某个文件的inode信息:
stat example.txt
总之,除了文件名以外的所有文件信息,都存在inode之中。
了解一下文件系统如何存取文件的:
1、根据文件名,通过Directory里的对应关系,找到文件对应的Inode number
2、再根据Inode number读取到文件的Inode table
3、再根据Inode table中的Pointer读取到相应的Blocks
【转自【Linux常识篇(2)】理解inode_weixin_30338481的博客-CSDN博客】
-----------------------------------------------------------------------------------------------------------------------
1. sys_open:
include/linux/syscalls.h 声明sys_open(),fs/open.c定义COMPAT_SYSCALL_DEFINE3(open,...)。之后直接调用do_sys_open()。
->do_sys_openat2()->do_filp_open()->do_open()。
static long do_sys_openat2(int dfd, const char __user *filename,
struct open_how *how)
{
struct open_flags op;
int fd = build_open_flags(how, &op);//把how相关数据填写到op中
struct filename *tmp;
if (fd)//如果返回非零值,一般是error。
return fd;
tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
fd = get_unused_fd_flags(how->flags);
if (fd >= 0) {
struct file *f = do_filp_open(dfd, tmp, &op);//打开文件
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f);
fd_install(fd, f);//将fd和文件指针写入字典
}
}
putname(tmp);
return fd;
}
struct file *do_filp_open(int dfd, struct filename *pathname,
const struct open_flags *op)
{
struct nameidata nd;//一层层路径查询信息
int flags = op->lookup_flags;
struct file *filp;//分配struct file
set_nameidata(&nd, dfd, pathname);//先调用set_nameidata对nd进行简单初始化
filp = path_openat(&nd, op, flags | LOOKUP_RCU);//再完成路径查找。
if (unlikely(filp == ERR_PTR(-ECHILD)))
filp = path_openat(&nd, op, flags);
if (unlikely(filp == ERR_PTR(-ESTALE)))
filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
restore_nameidata();
return filp;
}
static struct file *path_openat(struct nameidata *nd,
const struct open_flags *op, unsigned flags)
{
struct file *file;
int error;
file = alloc_empty_file(op->open_flag, current_cred());
if (IS_ERR(file))
return file;
if (unlikely(file->f_flags & __O_TMPFILE)) {
error = do_tmpfile(nd, flags, op, file);
} else if (unlikely(file->f_flags & O_PATH)) {
error = do_o_path(nd, flags, file);
} else {
const char *s = path_init(nd, flags);
while (!(error = link_path_walk(s, nd)) && //This is the basic name resolution function, turning a pathname into the final dentry.
(s = open_last_lookups(nd, file, op)) != NULL)
;//用于一级一级解析文件路径
if (!error)
error = do_open(nd, file, op);//如果没有error,打开文件
terminate_walk(nd);
}
if (likely(!error)) {
if (likely(file->f_mode & FMODE_OPENED))
return file;
WARN_ON(1);
error = -EINVAL;
}
fput(file);
if (error == -EOPENSTALE) {
if (flags & LOOKUP_RCU)
error = -ECHILD;
else
error = -ESTALE;
}
return ERR_PTR(error);
}
link_path_walk:解析文件路径名,由于使用了LOOKUP_PARENT,因此将得到最后一级目录的
注意:此处设置了标志LOOKUP_PARENT,这样在执行link_path_walk之后路径上下文将保存的是最后一个目录的上下文。
open_last_lookups:分析最后的路径分量,在open_last_lookups之前nd为最后一级目录的路径上下文,nd->last保存最后一级路径名分量的信息,open_last_lookups是将最后一级路径名单独拿出来进行处理,期间会为之获取dentry以及inode,如果为空则会创建,并用此inode和nd填充前述的file文件描述符
do_open:主要对file描述符进行初始化,并最终调用具体文件系统的open回调,其中f->f_op = fops_get(inode->i_fop);用来初始化file的f_op回调。
do_open()会调用vfs_open()->do_dentry_open()->open()。do_dentry_open中的open(),会拿到inode里面指向的f_op中的open函数,如果inode中即驱动中有这个,则执行这个真正的open函数。
int vfs_open(const struct path *path, struct file *file)
{
file->f_path = *path;
return do_dentry_open(file, d_backing_inode(path->dentry), NULL);
}
//do_dentry_open.c
if (!open)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
2. inode: