014 Linux文件系统数据结构详解:地址空间struct address_space

什么是地址空间address_space?是Linux内核提供的一种数据结构,通过该数据结构可以管理离散到各设备上的数据映射到内存的page缓存页面。

听起来很晦涩,其实地址空间就是一个中间层,内核把周边离散的设备组织起来,然后映射到page缓存页面,内核子系统通过地址空间,就可以操作这些page缓存页面,进而达到操作周边设备的目的。

所以page就是内核管理的虚拟内存空间,它是真实物理设备的在内存上的映射,通过操作page页面,就可以完成对具体物理设备操作,这样做的好处是:

  • ① 屏蔽了多种设备的物理差异,避免内核子系统直接访问物理设备
  • ② 相比CPU操作外设的等待时间,内存操作更加高效,提升了整体性能
  • ③ 文件在设备上可能是不连续的,通过page页面屏蔽这个矛盾,通过连续的page呈现给程序

举例说明:一个文件的数据存储在块设备上,内核将块设备映射到缓存页面page,VFS通过地址空间就可以操作缓存页面page,完成文件的读取和写入。

地址空间和虚拟内存
地址空间和虚拟内存
继续阅读“014 Linux文件系统数据结构详解:地址空间struct address_space”

013 Linux一次文件读过程read()

上一篇介博客绍了文件写过程write(),接下来看一下读过程read()。在写过程中,根据打开文件的文件描述符,获取文件对象指针file*,然后调用操作表中的write()函数或write_iter()函数,完成写操作file->ops->write()。读过程与写过程类似,最终调用的是read()或read_iter()函数。

读文件read()
读文件read()
继续阅读“013 Linux一次文件读过程read()”

012 Linux一次文件写过程wirte()

上一篇介绍了文件打开open()操作,在open()操作中,已经获取到了文件描述符fd,fd本质就是file对象的下标,通过fd可以直接得到缓存中的file对象,通过file对象就可以进行文件的写操作write()。

写文件过程
写文件过程
继续阅读“012 Linux一次文件写过程wirte()”

011 Linux一次文件打开过程open()

前面介绍了文件系统的各种结构体,那么一次打开文件过程,需要和哪些结构体产生联系呢?要解决上面提出的问题,就要先搞清楚,函数是调用流程是怎样的,调用过程中串联了哪些数据结构,说起来也就是回答如下几个疑问:

  1. 打开文件的内核入口在哪里?
  2. 打开文件时,如何知道当前属于哪个文件系统呢?
  3. 如果文件已存在,那又如何获取目录项dentry和索引节点inode呢?
  4. 文件对象file是怎么构造的?
  5. 文件描述符是怎么产生的,又是如何跟file对象关联起来?
open()调用流程
open()调用流程
继续阅读“011 Linux一次文件打开过程open()”

010 Linux文件系统数据结构详解:文件对象struct file

struct file是Linux文件系统中的一个重要数据结构,用于表示进程打开的文件,也就是文件对象,struct file是已打开的文件在内存中的表示,存储了与文件操作和状态相关的信息。

通过file对象就可以获取当前文件的目录项和索引节点,通过files_struct这个纽带,就把进程了和文件系统对接起来了。那么文件系统给进程呈现的是什么信息呢?

文件对象
文件对象
继续阅读“010 Linux文件系统数据结构详解:文件对象struct file”

009 Linux文件系统数据结构详解:挂载点mount和vfsmount

前面介绍了超级块super_block、索引节点inode、目录项dentry等核心数据结构,接下来介绍文件系统挂载相关的mount和vfsmount结构体。

文件系统挂载就是将一个文件系统安装到全局文件结构树的一个分支上,文件系统挂载能力是VFS对外提供的统一的接口,实际挂载时会调用具体文件系统的实现(mount函数),这个具体实现逻辑是注册文件系统时,通过文件系统类型file_system_type结构体传递给VFS。挂载文件系统,VFS会回调具体文件系统的挂载函数mount(),mount()函数会构造超级块和根dentry,函数的返回值就是根dentry指针。

struct file_system_type {
	const char *name;
	int fs_flags;
	int (*init_fs_context)(struct fs_context *);
	const struct fs_parameter_spec *parameters;
	struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *);
	void (*kill_sb) (struct super_block *);
	struct module *owner;
	struct file_system_type * next;
	struct hlist_head fs_supers;
        ......
};
继续阅读“009 Linux文件系统数据结构详解:挂载点mount和vfsmount”

008 Linux文件系统数据结构详解:目录项dentry

前两篇博客看了超级块和索引节点的数据结构,接下来研究一下目录项dentry的结构。目录项设计的目的是构建完整的目录树,这样可以快速找到文件对应的inode,帮助VFS完成文件操作。具体过程如下:

用户程序在调用open()、stat()、chmod()等函数时,传递文件路径信息到VFS,VFS需要根据文件路径找到对应的inode,所以VFS用文件名和目录项缓存一层一层比对,直到找到对应的目录项,进而获取inode信息。如果在缓存中没有命中,VFS将会新创建文件的inode和对应的目录项,并且持久化到磁盘上。

继续阅读“008 Linux文件系统数据结构详解:目录项dentry”

007 Linux文件系统数据结构详解:索引节点inode

上一篇讲了超级块,超级块存储了文件系统的基础信息,以及文件系统的控制信息。而今天介绍的索引节点数据结构,负责保存文件系统中实际文件一般信息,文件系统使用inode管理文件和目录。与超级块类似,索引节点也有三种形态:

  • 持久化的索引节点
  • 内存中构建的索引节点
  • VFS提取的索引节点公共信息,构建出来的索引节点对象
索引节点存储结构
索引节点存储结构
继续阅读“007 Linux文件系统数据结构详解:索引节点inode”

006 Linux文件系统数据结构详解:超级块super_block

前面几篇文章,介绍了内核虚拟文件系统VFS的一些基本概念,今天开始正式详细介绍几个核心数据结构:超级块super_block、索引节点inode、目录项dentry、文件对象file、挂载点mount和vfsmount。对于文件系统来说,在谈论数据结构时,一般至少要关注三个方面:持久化的结构(磁盘上)、内存中的结构、以及VFS的结构。

Linux有一棵全局文件系统树,文件系统要被用户使用,必须先安装(挂载)到这棵树上。每一次安装被都会生成一个装载实例。每个文件系统装载后,都包含上述提到的四个对象:mount(含vfsmount)、超级块、根inode和根dentry,而文件对象file,是文件系统中文件被进程使用后产生的,可以认为是一个打开的文件。

持久化超级块
持久化超级块
继续阅读“006 Linux文件系统数据结构详解:超级块super_block”

005 虚拟文件系统VFS中超级块、安装点、文件系统类型三者之间关系

上两篇文章介绍虚拟文件系统VFS中基本的数据结构、以及与进程相关的数据结构,为了描述完整的VFS,其实还有一类数据结构,他们是描述文件系统的。文件系统在不同状态下,根据功能不同,主要包含三个数据结构:超级块super_block、安装点(也叫挂载点)mount和vfsmount、文件系统类型file_system_type。下面就详细介绍下这三个数据结构之间的关系。

继续阅读“005 虚拟文件系统VFS中超级块、安装点、文件系统类型三者之间关系”