Linux文件系统

 

 ------------------------------以上内容转自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,因此将得到最后一级目录的<vfsmount,dentry>二元组保存到路径上下文nd,否则保存的是最后一级路径名的<vfsmount,dentry>
注意:此处设置了标志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:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值