文件系统磁盘布局与I/O映射

70 篇文章 6 订阅 ¥9.90 ¥99.00

文件系统磁盘布局与I/O映射

本文主要对文件系统的磁盘布局进行概要的梳理,并在此基础上分析文件系统I/O到块I/O的映射机制。

1.文件系统磁盘布局

首先文件系统层面来阐述文件与块设备的关系。

1.1文件描述符与inode

应用程序在访问文件时都会先打开文件,在内核中,对应每个进程,都会有一个文件描述符表表示这个进程打开的文件,但是用户程序不能直接访问内核中的文件描述符表,而只能使用文件描述符表的索引(一个整数),这些索引就被称为文件描述符。当调用open 打开一个文件或创建一个新文件时,内核分配一个文件描述符并返回给用户程序,该文件描述符表项中的指针指向新打开的文件。

文件描述表中每一项都是一个指针,指向一个用于描述打开的文件的数据块–file对象,file对象中描述了文件的打开模式,读写位置等重要信息,当进程打开一个文件时,内核就会创建一个新的file对象。需要注意的是,file对象不是专属于某个进程的,不同进程的文件描述符表中的指针可以指向相同的file对象,从而共享这个打开的文件。file对象有引用计数,记录了引用这个对象的文件描述符个数,只有当引用计数为0时,内核才销毁file对象,因此某个进程关闭文件,不影响与之共享同一个file对象的进程。

file对象中包含一个指针,指向dentry对象。dentry对象代表一个独立的文件路径,如果一个文件路径被打开多次,那么会建立多个file对象,但它们都指向同一个dentry对象。inode对象代表一个独立文件,inode 对象包含了最终对文件进行操作所需的所有信息,如文件系统类型、文件的操作方法、文件的权限、访问日期等。

inode

1.2文件系统磁盘布局

在了解了文件与inode的对应关系后,为了阐述文件与具体的物理块的映射关系,必须先理解文件系统的磁盘布局。下图是ext2/ext3文件系统的磁盘布局。
fs2disk
ext3文件系统将其所管理的磁盘或者分区(引导块除外)中的块划分到不同的块组中。每个块组大小相同,当然最后一个块组所管理的块可能会少一些,其大小在文件系统创建时决定,主要取决于文件系统的块大小。每个块组包含一个块位图块,一个inode位图块,一个或多个块用于描述 inode 表和用于存储文件数据的数据块,除此之外,还有可能包含超级块和所有块组描述符表。
块位图用于描述该块组所管理的块的分配状态,如果某个块对应的位未置位,那么代表该块未分配,可以进行分配使用。inode位图用于描述该块组所管理的inode的分配状态,每个inode对应文件系统中唯一的一个号,如果inode位图中相应位置位,那么代表该inode已经分配出去;否则可以使用。

1.3文件系统逻辑块映射

下面就来阐述文件与物理块的映射关系。
ext2/ext3采用的是直接+间接方式来存储这些地址映射:其中0~11是存储用户数据的物理块号(直接寻址),12是存储一级地址映射表数据的物理块地址(一级间接寻址),地址映射表中的每一项都是另外一个物理块号。13存储二级地址映射表地址,14存储三级地址映射表地址。这种方式对于小文件比较合适,对于大文件来说要经过很多次间接寻址,效率很低。
ext3-map
而ext4则采用的是extents的映射方式:[logicalStartBlock,physicalStartBlock, blockCount]。ext4中一个inode的大小是256Bytes,因此一个inode能容纳的extent有限,当文件增大到一定程度的时候,extent无法以数组形式存放,就会以B+tree的形式存放。相比于ext3的间接映射方式,extents的方式增加了大文件的效率并减少了分裂文件。
ext4-map
通过上面的阐述,我们知道了一个文件和具体物理块的对应关系,那么问题来了:用户以4KB大小对文件写一个I/O,对于磁盘来说是一个I/O吗?如果采用direct方式呢?如果是16KB大小的一个用户I/O呢?下面就从文件I/O到块I/O的映射机制来进行阐述。

2.文件I/O到块I/O的映射

文件系统创建时会指定块大小(一般512,1024,2048,4096字节),一个文件会根据文件系统块大小和文件的大小切分成各个逻辑块,文件系统就会为这些逻辑块分配空闲的物理块(ext4文件系统是延迟分配的),用户程序对一个文件进行读写时,文件系统就会根据对应的逻辑块找到具体的物理块,然后下发io请求到块设备层进行处理。

用户调用read()或write()系统调用进行io读写时,对应的缓冲区buf是用户进程空间的虚拟地址,在内核处理时将虚拟内存映射到到物理内存页。如果是缓存I/O,会经过用户缓冲到内核缓存(page cache)的拷贝,而Direct I/O则可以直接让内核态映射用户态虚拟内存对应的物理页,少了一次拷贝的过程。在从文件I/O到块I/O的映射处理过程中,缓存I/O的刷脏页和Direct I/O的方式类似,这里以Direct I/O进行简要分析,详细的可以去看内核代码里的submit_page_action,__bio_add_page等函数的实现。

用户程序在使用Direct I/O时,使用的buffer和读写的长度需要512字节的整数倍对齐,之所以这样要求,是因为非对齐的请求在底层构造bio时有可能会因为PageSize边界带来额外的I/O,从而需要多次下发请求,降低了效率。

一个文件I/O下发到底层会变成多少个块I/O,取决于文件系统块大小和用户缓冲区的对齐长度,以及该文件的逻辑块对应的物理块是否连续。下面以具体的例子来进行说明。
1)文件系统块大小是1KB ,用户发起一个4KB的请求,申请的buffer在一个页中(或者direc io下的按照4KB对齐)。图中绿色和红色方块里的数字表示对应的真实的物理块的块号,要读写的这个文件的4KB的数据,按照文件系统块大小切分,分成4个逻辑块(0~3),其中逻辑块0对应物理块60,逻辑块1对应物理块61,逻辑块2对应物理块200,逻辑块3对应物理块201(这些对应关系是由文件系统分配块时确定的)。逻辑块0和1对应的物理块是连续的,并且因为块大小是1KB,所以可以放在一个bio里,而逻辑块2和3对应的物理块与逻辑块0和1的物理块不连续,所以不能放在一个bio里,需要新生成一个bio。这样就调用了两次submit_bio下发到通用块层去处理,经过调度层(有可能和其他请求连续,从而进行合并),最后通过块设备驱动层下发到底层磁盘上。
block-is-1kb_4kb-io-in-page
2)文件系统块大小是1KB, 用户发起一个4KB的请求,申请的buffer不在一个页中(或者direc io下的按照1KB对齐)。请求落到两个页中,并且因为逻辑块2和逻辑块3对应的物理块不连续,所以构造bio时变成了3个bio(如果两个页中的逻辑块对应的物理块是连续的,则会放到一个bio里,每个bio可以包含多个页)。因此就会有3次submit_bio的调用。
block-is-1kb_4kb-io-not-in-page
3)文件系统块大小是4KB ,用户发起一个4KB的请求,申请的buffer在一个页中(或者direc io下的按照4KB对齐)。这里刚好一个页里只有1个逻辑块,所以只有一次submti_bio。
block-is-4kb_4kb-io-in-page
上面的几个示例就解释了第3节末尾提出的问题,一个文件I/O下发到底层会变成多少个块I/O,取决于文件系统块大小和用户缓冲区的对齐长度,以及该文件的逻辑块对应的物理块是否连续。

3.参考资料

1.《深入理解linux内核》
2.《Linux设备驱动程序》
3.《unix高级环境编程》
4.Linux内核源码3.10.45

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 1 虚拟文件系统概述 5 1.1 通用文件模型 7 1.2 VFS所处理的系统调用 9 2 虚拟文件系统架构 11 2.1 VFS对象数据结构 11 2.1.1 超级对象 11 2.1.2 索引节点对象 15 2.1.3 文件对象 18 2.1.4 目录项对象 22 2.2 把Linux中的VFS对象串联起来 24 2.2.1 与进程相关的文件 25 2.2.2 索引节点高速缓存 29 2.2.3 目录项高速缓存 30 2.2.4 VFS对象的具体实现 32 2.3 文件系统的注册与安装 38 2.3.1 文件系统类型注册 38 2.3.2 文件系统安装数据结构 41 2.3.3 安装普通文件系统 52 2.3.4 分配超级对象 58 2.3.5 安装根文件系统 60 2.3.6 卸载文件系统 65 2.4 路径名的查找 66 2.4.1 查找路径名的一般流程 67 2.4.2 父路径名查找 82 2.4.3 符号链接的查找 84 2.5 VFS系统调用的实现 88 2.5.1 open()系统调用 88 2.5.2 read()和write()系统调用 96 2.5.3 close()系统调用 97 3 第二扩展文件系统 99 3.1 Ext2磁盘数据结构 101 3.1.1 磁盘超级 102 3.1.2 组描述符和位图 105 3.1.3 磁盘索引节点表 105 3.2 VFS接口数据结构 110 3.2.1 Ext2 超级对象 110 3.2.2 Ext2 的索引节点对象 121 3.2.3 创建Ext2文件系统 124 3.2.4 Ext2的方法总结 126 3.3 Ext2索引节点分配 129 3.3.1 创建索引节点 130 3.3.2 删除索引节点 143 3.4 Ext2数据分配 144 3.4.1 数据寻址 145 3.4.2 文件的洞 147 3.4.3 分配数据 148 4 页面高速缓存 160 4.1 页高速缓存数据结构 160 4.1.1 address_space对象 161 4.1.2 基树 164 4.2 高速缓存底层处理函数 166 4.2.1 查找页 166 4.2.2 增加页 168 4.2.3 删除页 173 4.3 文件系统与高速缓存 175 4.3.1 缓冲头数据结构 175 4.3.2 分配设备缓冲区页 178 4.3.3 释放设备缓冲区页 184 4.4 在页高速缓存中搜索 185 4.4.1 __find_get_block()函数 185 4.4.2 __getblk()函数 188 4.4.3 __bread()函数 190 4.5 把脏页写入磁盘 191 4.5.1 pdflush内核线程 192 4.5.2 搜索要刷新的脏页 193 4.5.3 回写陈旧的脏页 196 5 文件读写 199 5.1 系统调用VFS层的处理 200 5.2 第二扩展文件系统Ext2层的处理 201 5.2.1 Ext2的磁盘布局 202 5.2.2 Ext2的超级对象 206 5.2.3 Ext2索引节点对象的创建 210 5.2.4 Ext2索引节点对象的读取 218 5.2.5 Ext2层读文件入口函数 225 5.3 页高速缓存层的处理 237 5.3.1 创建一个bio请求 238 5.3.2 得到文件的逻辑号 244 5.3.3 普通文件的readpage方法 251 5.3.4 设备文件的readpage方法 252 5.3.5 文件的预读 260 5.4 通用层的处理 264 5.4.1 设备的基础知识 265 5.4.2 通用层相关数据结构 269 5.4.3 提交I/O传输请求 271 5.4.4 请求队列描述符 273 5.5 设备I/O调度层的处理 281 5.5.1 设备的初始化 284 5.5.2 建立设备驱动环境 288 5.5.3 关联block_device结构 295 5.5.4 为设备建立请求队列 306 5.5.5 设备I/O调度程序 311 5.5.6 真实的I/O调度层处理 321 5.6 设备驱动层的处理 330 5.6.1 scsi总线驱动的初始化 330 5.6.2 scsi设备驱动体系架构 342 5.6.3 scsi设备驱动层处理 347 5.6.4 scsi命令的执行 369 5.6.5 scsi命令的第一次转变 372 5.6.6 scsi命令的第二次转变 380 5.7 写文件 384 5.7.1 generic file_write函数 384 5.7.2 普通文件的prepare_write方法 386 5.7.3 设备文件的prepare_write方法 387 5.7.4 将脏页写到磁盘 388 6 直接I/O与异步I/O 391 6.1 直接I/O 391 6.2 异步I/O 393 6.2.1 Linux 2.6中的异步I/O 394 6.2.2 异步I/O环境 394 6.2.3 提交异步I/O操作 395

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值