11 ext2文件系统IO流程:读写文件read/write

ext2使用page页面缓存来完成对文件的读写。这些页面的管理是通过inode的字段i_mapping来完成,也就是地址空间。所以在创建inode时,要指定i_mapping的操作表a_ops,帮助地址空间完成页面操作。

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

地址空间操作表a_ops中,需要指定读page、写page等多种页面操作的函数指针,但是具体的块操作(读取、写入)、buffer操作、VM页面操作,其实文件系统不用太关注,因为Linux内核提供了大量公共函数(参考:buffer.c和mpage.c),文件系统可以直接调用完成读取和写入。但是,文件系统需要提供块的映射方法,帮助完成文件系统逻辑块号(就是在文件中的偏移量)到实际块设备块号映射,最后填充到buffer_head中。

// inode.c

const struct address_space_operations ext2_aops = {
	.set_page_dirty		= __set_page_dirty_buffers,
	.readpage		= ext2_readpage,
	.readahead		= ext2_readahead,
	.writepage		= ext2_writepage,
	.write_begin		= ext2_write_begin,
	.write_end		= ext2_write_end,
	.bmap			= ext2_bmap,
	.direct_IO		= ext2_direct_IO,
	.writepages		= ext2_writepages,
	.migratepage		= buffer_migrate_page,
	.is_partially_uptodate	= block_is_partially_uptodate,
	.error_remove_page	= generic_error_remove_page,
};
继续阅读“11 ext2文件系统IO流程:读写文件read/write”

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

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

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

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

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

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

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

10 ext2文件系统IO流程:inode的创建、写入、读取、删除、释放子流程

在Linux文件系统中,inode就代表一个磁盘上的文件,所有磁盘文件的操作,最终都落到inode上去处理,inode的生命周期管理非常重要。但是inode的创建、写入、读取和删除,通常不是独立的流程,它是裹挟在其它的大IO流程中,比如创建文件。但是在其它流程中,inode的处理的介绍几乎是一笔带过,所以,有必要针对inode管理,总结一篇详实的材料。本文还是以ext2文件系统为例,来讲述inode生命周期的管理。

首先,你要知道inode管理的操作函数,都是在超级块结构体里声明,因为inode自己的操作表,都是和文件操作相关。好,我们一起来看一下:

// super.c

static const struct super_operations ext2_sops = {
    .alloc_inode    = ext2_alloc_inode,
    .free_inode     = ext2_free_in_core_inode,
    .write_inode    = ext2_write_inode,
    .evict_inode    = ext2_evict_inode,
    .put_super      = ext2_put_super,
    .sync_fs        = ext2_sync_fs,
    .freeze_fs      = ext2_freeze,
    .unfreeze_fs    = ext2_unfreeze,
    .statfs         = ext2_statfs,
    .remount_fs     = ext2_remount,
    .show_options   = ext2_show_options,
};
继续阅读“10 ext2文件系统IO流程:inode的创建、写入、读取、删除、释放子流程”

09 ext2文件系统IO流程:删除目录rmdir

上一篇说到ext2文件系统删除文件是逻辑删除,对应文件系统的unlink操作,其实就是解除磁盘目录项与inode关系,同时删除目录项(也是逻辑删除,与前一项合并)。那么,同样删除目录跟删除文件类似,ext2也是做了一次unlink操作。详细的删除目录流程如下:

  • ① 检查目录是否为空(除了.和..之外,是否还有其它文件),如果目录不空有文件,返回-ENOTEMPTY,无法删除(相关函数:ext2_empty_dir())
  • ② 如果目录为空,调用ext2_unlink()函数解除link
ext2删除目录流程
ext2删除目录流程
继续阅读“09 ext2文件系统IO流程:删除目录rmdir”

08 ext2文件系统IO流程:删除文件unlink

前面几篇博客讲了创建文件、创建目录、查找文件,接下来说一下ext2文件系统是如何删除文件,ext2文件系统删除文件,并没有在物理设备上擦除文件,只是做了一个unlink操作,所谓的逻辑删除。unlink的具体实现,就是解除目录项与inode关系,这样目录项就不会关联到inode,就无法查找到文件,达到了删除的目的。unlink的具体的流程如下:

  • ① 根据文件名和父目录信息,到磁盘上查找目录项(ext2_find_entry),记录对应的页面page和页地址page_addr
  • ② 解除目录项与inode连接,然后删除目录项(逻辑删除,其实是与前一个目录项合并)
  • ③ 修改inode->i_ctime,递减inode的硬链接数i->nlink,同时标记脏inode
ext2 unlink操作
ext2 unlink操作
继续阅读“08 ext2文件系统IO流程:删除文件unlink”

05 ext2文件系统IO流程:创建文件create

创建文件是文件系统的基本操作,之前在介绍Linux文件系统VFS时,说过创建文件会调用inode操作表中的create()函数,那create函数具体应该如何实现呢?

文件系统在创建文件时,VFS会调用父目录目录的create()函数,在这个函数中要完成具体的文件创建,以ext2文件系统为例,ext2注册的创建文件的函数指针是ext2_create,在ext2文件系统创建文件,一般包括以下几个步骤:

  • 新建一个inode对象
  • 设置inode的文件操作表
  • 标记inode为脏,等待写入到磁盘
  • 在父目录的页缓存中,写入目录项数据,指向新建的inode
  • 建立缓存dentry和inode关系
ext2创建文件
ext2创建文件
继续阅读“05 ext2文件系统IO流程:创建文件create”

04 ext2文件系统格式化

要使用ext2文件系统,要先在设备上创建文件系统,也就是对设备进行格式化。常见的格式化工具有e2fsprog的mkefs,还有busybox的mkfs.ext2。接下来就以busybox的工具为例,介绍具体的格式化流程。

ext2格式化
ext2格式化
继续阅读“04 ext2文件系统格式化”

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

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

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

01 ext2注册/解注册文件系统

前面章节介绍了文件系统数据结构和基本流程,接下来以ext2文件系统为例,详细说明如何从头构建一个文件系统,主要包括:文件系统注册/解注册、文件系统挂载、超级块管理、inode管理等等。今天我们先来说一下注册和解注册文件系统。

文件系统属于内核模块,内核在加载这些模块时,会调用初始化init和退出exit函数,一般在这两个函数中完成注册和解注册动作,参考如下示意图。

ext2注册/解注册
ext2注册/解注册
继续阅读“01 ext2注册/解注册文件系统”