文件读写的理论

2023-11-06

为了提高效率,稍微复杂一些的操作系统对文件的读写都是带缓冲的,Linux当然也不例外。所谓缓冲,就是操作系统为最近刚读写的文件内容在内核保留一份副本,以便当再次需要已经缓冲存储在副本中的内容时就不必再临时从设备上读入,而需要写的时候则可以先写到副本中,待系统较为空闲的时候再从副本写入设备。在多进程的系统中,由于同一个文件可能为多个进程所共享,缓冲的作用就更为显著。

然而,怎么样实现缓冲,在哪一个层次上实现缓冲,确实一个值得考虑的问题。在文件层有三种主要的数据结构:file、dentry、inode。

先看file结构:前面讲过,一个file结构代表着目标文件的一个上下文,不但不同的进程可以在同一个文件上建立不同的上下文(每个进程都有自己的file结构体),就是同一个进程也可以通过打开同一个文件多次而建立起多个上下文。如果在file结构中设置一个缓冲区队列,那么缓冲区中的内容虽然贴近这个特定上下文的使用者,却不便于为多个进程共享,甚至不便于同一个进程打开的不同上下文共享,这显然是不合适的。

那么dentry结构怎么样呢?这个数据结构并不属于某一个上下文,也不属于一个进程,可以为所有进程和上下文共享。可是dentry结构与目标文件并不是一一对应的关系,通过文件链接,我们可以为已经存在的文件建立别名。一个dentry结构知识唯一的代表这文件系统中的一个节点,也就是一个路径名,但是多个节点可以同时代表同一个文件,所以还应该再抽象一次。

显然,在inode数据结构设置一个缓冲队列是最合适不过了,首先,inode结构与文件是一一对应的关系,即使一个文件有多个路径名,最后也归为同一个inode上。再说,一个文件中的内容是不能由其他文件共享的,在同一时间里,设备上的每一个记录都只能属于至多一个文件,将载有同一个文件内容的缓冲区都放在其所属文件的inode结构中是很自然的事。因此在inode数据结构中设置了一个指针i_mapping,它就指向一个address_space数据结构,缓冲区队列就在这个数据结构中。

不过,挂在缓冲区队列中的并不是记录块而是内存页面。也就说,文件的内容并不是以记录块为单位,而是以页面为单位进行缓冲的。为什么这个搞?这是为了将文件内容的缓冲与文件的内存映射结合在一起。进程可以通过系统调用mmap()将一个文件映射到它的用户空间。建立了这样的映射以后,就可以像访问内存一样访问这个文件。如果将文件的内容以页面为单位缓冲,放在附属于该文件的inode结构的缓冲队列中,那么只要相应的设置进程的内存映射表,就可以很自然地将这些缓冲页面映射到用户空间中。这样,在按常规方式文件操作访问一个文件时,就可以通过read()和write()系统调用访问目标文件的inode结构访问这些缓冲页面;而通过内存映射机制访问这个文件时,就可以经由页面映射直接访问这些缓冲着的页面。当目标页面不在内存中时,常规的文件操作通过系统调用read()、write()的底层将其从设备读入,而通过内存映射机制访问这个文件时,则由缺页异常的服务程序将目标页面从设备上读入。明白了这个背景,就明白上述的指针为什么叫i_mapping,它指向的数据结构为什么叫address_space就不会奇怪了。

可是,尽管以页面为单位的缓冲对于文件层确实是很好的选择,对于设备层则不那么合适了。对设备层而言,最自然的当然是以记录块为单位的缓冲,因为设备的读写都是以记录块为单位的。不过,从磁盘上读写主要的时间都花在准备工作上,一旦准备好了以后读一个记录块与接连读几个记录块相差并不大,而且每次只读写一个记录块反而是不经济的。所以每次读写若干连续的记录块、以页面为单位缓冲并不是问题。另一方面,如果以页面为单位缓冲,而一个页面相当于若干连续记录块,那么无论是对于缓冲页面还是对于记录块缓冲区,其控制信息显然应该游离于该页面之外,这些信息不应该映射到进程的用户空间。这个问题不难解决。在设备层中要保持一些buffer_head结构,让它们的b_data指针分别指向缓冲区页面中的相应位置就可以了。以一个缓冲页面为例,在文件层它通过一个page数据结构挂入所属inode结构的缓冲页面队列,并且同时又可以通过各个进程的页面映射表映射到这些进程的内存空间;而在设备层又通过若干buffer_head结构挂入其所在设备的缓冲区队列。

在这样一个结构框架中,一旦所欲访问的内容已经在缓冲页面队列中,读文件的效率就很高了,只要找到文件的inode结构就找到了缓冲页面的队列,从队列中找到相应的页面就可以读出了。

上面的设备缓冲区队列我们还没介绍是什么东西,下面来介绍

当一个块调入内存时,它要存储在一个缓冲区中。每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示,磁盘块包含一个或者多个扇区,但是不能超过一个页面,所以一个物理页可以容纳一个或者多个内存中的块。由于内核在处理数据时需要知道一些相关信息(比如块属于哪一个块设备,块对应于哪个缓冲区等),所以每个缓冲区都有一个对应的描述符。该描述符用buffer_head结构体表示,成为缓冲区头,在文件<linux/buffer_head.h>中定义,它包含了内核操作缓冲区的全部信息。

其实这个buffer_head存在于linux2.4版本中,在linux中使用bio结构体代替。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

文件读写的理论 的相关文章

随机推荐

  • Attaching to process 29139 Could not attach to process.

    docker container中gdb调试程序 报错 Attaching to process 29139 Could not attach to process If your uid matches the uid of the ta
  • Windows 无法访问\\196.168.206.xx,你的计算机配置似乎是正确的,但该设备无法访问的问题解决

    报错 其实我们在不知道报错原因的情况下可以用虚拟机ping一下你的Windows ip地址看看能不能ping通 或者用cmd来ping虚拟机 如果互相不能ping通 那说明是网络设置的问题 比如我的Windows就ping不通虚拟机 解决方
  • Retrofit2+Rxjava网络请求异常的统一封装处理

    Retrofit2 Rxjava作为主流的网络请求框架 本文主要讲解针对网络请求的错误信息进行一次封装 方便我们根据返回的状态合理地在UI界面进行显示 同时如何主动取消网络请求的订阅 网络请求中有很多常见的错误 我们可以通过Toast弹出消
  • gcc编译报错unknown type name ‘bool‘

    错误描述 unknown type name bool 最近使用C实现数据结构碰到了以下这个错误 LinkedList gcc list c list c 14 1 error unknown type name bool did you
  • 经济学网站收藏

    经济学家 http www jjxj com cn 北京大学国际经济研究中心 http www ccer edu cn cn 中国经济学教育科研网 http www cenet org cn cn 清华大学经济研究中心 http www n
  • 高版本MySQL(5.7,5.8)的JDBC连接新问题

    在使用JDBC连接访问MySQL时一般要使用对应版本的驱动 比如我使用了8 0 11版本的MySQL 对应驱动的Maven描述为
  • 冒泡排序c++代码_教你写一手漂亮的伪代码

    前言 最近在复盘 算法设计与分析 这门课程的时候 发现老师写得一手漂亮的伪代码 着实羡慕不已 看他写其实已经知道大致写伪代码的规则 但本着严谨的态度 还是系统的学习和整理出来 在学习过程中 我们不可能将每一个算法都完完整整敲一遍 那么伪代码
  • WIN10中端口被占用,如何杀死该进程

    查看端口情况 netstat ano findstr lt 端口号 gt 杀死该进程 taskkill PID lt 进程号 gt F
  • M1芯片,PS2022版真的来了。Photoshop 2022 for Mac中文版介绍&安装教程,支持Monterey系统

    Adobe Photoshop 2022是一款非常实用的图形处理工具 在 ps 2022 mac中轻松创建报价图形的功能 使用预设模板 大量自定义和酷炫动画选项为您的照片添加励志名言或个性化信息 PS一直以来都被广泛的应用于平面设计 创意合
  • Esp8266智能配网代码(arduino)

    文章概述 我用的是Esp8266 12F NodeMCU开发板 配网用安信可微信公众号 的在线配网页面 把代码贴出来并解释一下 很高兴帮到和我一样的人 解析代码 程序目录 App 主程序 action 活动相关操作 btn 按钮相关操作 l
  • poj1240 Pre-Post-erous!

    照例先上题目 1 Pre Post erous 查看 提交 统计 提问 总时间限制 1000ms 内存限制 65536kB 描述 We are all familiar with pre order in order and post or
  • 微信记录怎么恢复?恢复已删除微信历史记录的4种方式

    恢复已删除微信历史记录的4种方式 如何在有 没有备份的情况下在 iPhone 和 Android 上恢复旧的或已删除的微信历史记录 如聊天对话 语音消息 照片 图片和视频剪辑 参考本指南 祝您成功恢复微信数据 关于微信数据恢复 说真的 微信
  • 【CVPR2022 BoostMIS 论文笔记】自适应伪标记和信息主动标注的医学图像半监督学习

    目录 摘要 1 Introduction 目的 贡献 3 Method 3 1 公式化符号 3 2 医学图像任务模型 3 3 基于一致性的自适应标签传播器 3 4 对抗性不稳定选择器 3 5 平衡不确定性选择器 3 6 训练算法 4 实验
  • Springboot + Vue ElementUI 实现MySQL&&Postgresql可视化

    一 功能展示 PostgreSQL教程 实现类似于MySQL的show create table功能实现见末尾 效果如图 DB连接配置维护 Schema功能 集成Screw生成文档 导出库的表结构 导出表结构和数据 表对象操作 翻页查询 查
  • Mybatis快速入门

    目录 MyBatis简介 MyBatis基于xml的快速入门 1 案例准备 2 引入依赖 3 编写全局配置文件 4 编写持久层接口的映射文件 5 编写测试类 MyBatis基于注解的快速入门 MyBatis简介 MyBatis 是一个优秀的
  • nodejs安装及环境配置

    一 下载 安装 1 下载node js 下载地址 Node js 2 安装 2 1选择Node js runtime 2 2这里不用选择安装其他的插件 如果选择会消耗2 3g流量的 2 3安装完成 3 检查是否安装成功 3 1 node v
  • C语言——时间制转换

    C语言 写一个程序 要求用户输入24小时制的时间 然后显示12小时制的时间 首先我们得搞清楚24小时制和12小时制之间的差异 从上步的差异中 我们得到规律 整理思路 并完成代码 考虑到代码的健壮性和实用性 考虑可能会遇到的问题 比如用户输入
  • 时间序列——季节系数法

    注 参考司守奎老师的数学建模与算法书籍 在数学建模问题中 有很多类问题具有 季节性特点 类似于不同蔬菜在一年四季中的价格变化 季节性服装在一年四季的价格变化等 对于季节性时间序列的预测 可以采用 季节系数法 来预测时间序列的变化趋势 在时间
  • 【python 1】python 基础

    文章目录 一 格式化输出 1 占位符 2 拼接输出 3 message 输出 4 format 输出 二 input 输入 三 pass break 四 字符串 五 列表 六 元组 七 字典 八 集合 一 格式化输出 1 占位符 s str
  • 文件读写的理论

    为了提高效率 稍微复杂一些的操作系统对文件的读写都是带缓冲的 Linux当然也不例外 所谓缓冲 就是操作系统为最近刚读写的文件内容在内核保留一份副本 以便当再次需要已经缓冲存储在副本中的内容时就不必再临时从设备上读入 而需要写的时候则可以先