linux文件——文件系统与内存管理——理解打开文件, 写入数据与文件系统的关系

news2024/11/15 6:41:34

        前言:本节课算是一点文件系统的补充内容。 但是说是文件系统的补充内容,其实我们也可以把这篇文章当作linux下的内存管理的文章来看待。 因为博主会从内存管理的角度, 将进程打开文件、写入数据的流程, 以非常底层的角度, 给剖析出来。 ——本篇文章可能讲的可能不会很细致。 但是博主相信友友们如果看完本篇文章后,一定会对于文件打开, 文件写入和磁盘之间的关联有一个很清晰的认识!!!

        ps:注意本篇内容非常抽象, 非常难以理解。没有前面文件系统、文件管理、进程管理的铺垫可能无法学习本节。建议友友们了解对应只是后再来学习本节内容!!! 

目录

页框和页帧

页框

页帧

数据的交换

 操作系统如何管理内存?

先描述

再组织

从用户层到内存

重新理解文件管理

struct inode

adress_space

radix_tree解析——补充知识

从内存到磁盘


页框和页帧

页框

对于物理内存来说, 内存可以分成一块一块的, 并且这些一块一块的内存是连续的, 也就可以被当成一个线性的逻辑结构, 如下图:

这物理内存中一块一块的4kb的块, 就叫做页框。 ——这里为什么是4kb, 不太重要, 而且很简单, 这里先卖关子。

页帧

不只只物理内存的逻辑结构可以是线性的。 我们也知道磁盘的逻辑结构也是线性的。 并且里面的存储内容是以块为单位的!!! 那么我们的程序, 在磁盘中保存的时候, 其实就是一块一块的分开保存的。 如下图:

注意, 这里的块是连续的, 因为一个程序就是一个文件, 而我们说过, 一个文件的块是连续的。 这里的这一个个的块, 就叫做页帧。

        

数据的交换

        我们知道, 在进行IO的时候, 物理内存和磁盘的数据是来回加载过来加载过去的。 而之所以上面的块都是4kb, 就是因为方便交换, 这个4kb就是交换数据的单位。(为什么是4kb, 因为是大佬们规定的。这也就回答了物理内中的页框是4kb一个。)

        在物理内存和磁盘进行数据交换的时候, 会以4kb的大小进行加载。 这个时候, 即便我们都要加载的数据只有1字节有效内容, 也只能将连带1字节在内的4kb一起加载进来!!!

        为什么会这样呢?  ——因为我们的磁盘是机械设备,而机械设备的速度相对于计算机内部的速度(光电速度)是很慢的。 那么就说明磁盘存储数据或者拿数据向内存中加载数据是很慢的, 那么我们一次性向内存中加载4kb数据一次性向内存中加载1kb, 但是加载4次相比肯定要快的多得多。 ——这个过程只需要定位磁头, 定位磁道, 定位扇区, 将数据连续的写到磁盘上即可。

        不知道友友们是否知道局部性原理——也就是我们一般访问代码和数据的时候, 有非常大的概率, 在该正在访问的代码区域的附近, 也会被访问。 ——所以我们在加载内存的时候, 即便只有1字节, 也要把这一字节的上下文全部加载进来。 这就是——基于局部性原理的预加载机制。

        所以, 综上, 一次性加载4kb这样做有什么好处呢?

  • 减少IO的次数——减少访问外设的次数——硬件层面
  • 进行预加载——有较大概率在软件层面上是实现效率提升

 操作系统如何管理内存?

        这里我们需要提前知道的是——我们从内存的学习中了解到了虚拟地址空间, 而操作系统想要管理物理地址。 那么就要能够看到虚拟地址, 同时也要看到物理地址。 ——为什么会这样?

        因为操作系统给我们的虚拟地址利用页表进行映射物理内存, 分配物理空间, 如果操作系统看不到物理内存, 如何给进程分配物理空间呢? 如果看不到虚拟地址, 进程如何实现我们看到的显示器上面的数字呢?

        所以,操作系统是能够看到内存的物理地址的!!!那么如果操作系统想, 是不是就能够管理物理地址了?——那么操作系统是如何管理物理地址的呢?

        具体操作系统如何管理地址, 这对于学习了如何进行文件管理, 如何进行进程管理的我们来说太简单了——也就是先描述,再组织!!!如果友友不知道,那么可能需要先学习一下前面的知识, 因为这六个子是整个计算机世界进行管理的本质

先描述

结构体的名称是page, 并且描述的是物理内存的一个个的页框。

struct page
{
    //page必要的属性信息。
}//描述的是一个个页框

再组织

        如果这里我们的物理内存一共有4GB, 那么如果按照一个页框4kb来算, 物理内存就有大概一百万个页框, 计算后其实是1,048,576个页框。

        一个struct page对象用来描述一个页框, 那么操作系统要将内存组织起来就要创建一个一百万大小的struct page对象组成的数组。 如下:

struct page page_arrar[1,048,576];//创建一个结构体数组进行组织

         所以, 我们操作系统对内存的管理, 就转化为了对于数组的管理!!!

         又因为数组天然是有下表的, 所以页框就有了下标!也就有了页号这样的概念!(了解即可, 本篇文章并没有用到页号)

        那么这里就可以得出结论了——我们要访问一个内存 ,只需要先直接找到这个对应的4kb对应的page, 就能在系统中找到对应的物理页框(在操作系统中, 所有申请内存的动作, 其实都是在访问内存page数组)。

我们查看一下linux下面的page结构体, 这里先看一部分, 一个flags, 一个_count

        这两个字段, 我们虽然没有学过内存管理, 但是我们已经学习了虚拟地址空间, 学习了页表。 应该也能够看出来——flag代表映射的物理内存的状态, 是否是只读, 是否是只写等等。而_const就是引用计数, 什么是引用计数呢? 是否还记得我们在学习父子进程的时候, 说过:子进程刚刚被创建出来的时候, 我们的父子进程的页表可能会指向同一块代码和数据。而这里的引用计数其实就是有多少页表会指向这块空间

        操作系统是如何发现缺页中断, 是如何发现要执行写时拷贝的呢?——其实非常简单, 我们知道, 我们的子进程在创建出来的时候, 指向的物理空间应该是只读的, 并且这个时候, 这块物理空间的引用计数应该是2, 那么我们如果看到某一块的物理内存的flags是为只读, 并且这块物理空间内的引用计数大于2, 那么就要执行写时拷贝!!!

        page里面还有一个链表结构——lru(这是个数据结构, 友友们可能学过), 也就是最近最少使用, 我们的操作系统要把我们的内存中不太使用的, 不长使用的刷新出去。因为如果一个一个遍历, 那么就太慢了,所以就用到了一个lru的数据结构。

        其实对于操作系统内存管理的上层还有伙伴系统算法, slab分派器。 这里解释一下slab分派器

        slab分派器就是一种对于垃圾数据结构回收的机制——就比如我们创建进程, 必须要有虚拟地址空间, 也就要有mm_struct对应, 也要有struct page对象。 当我们释放掉这些对象的时候, 我们的操作系统就可能不去释放了, 而是直接将这些对象保存在了一个队列, 或者一个数组等等数据结构里缓存起来等到下次创建对象的时候就直接拿过来用, 就不用再重新向操作系统中申请内存了!!!

从用户层到内存

我们电脑开机的时候, 基本上会将文件系统的前四个信息内容, 加载到内存中, 也就是下面这四个内容:

        那么问题就来了, 我们的磁盘里面可不止有一个分区, 而是由很多个分区。 这些分区可能文件系统不一样, 也就是super block的内容不一样。 那么怎么办呢? 没关系, 其实操作系统就是使用一个链表结构, 将所有分区的super block进行链接起来。 然后在操作系统的层面上就知道了我们分区的位置, 然后还有文件系统的样子等等。如下图:

重新理解文件管理

struct inode

        现在我们来回忆一下文件fd

我们在今天这节课之前理解的文件fd其实就是下面这一张图

        今天, 我们要重新理解一下这张图了。 

        其实, 我们在加载inode属性信息的时候, 不是将磁盘中对应的inode属性直接加载到struct file里面。 而是先创建另一个结构体对象, 这个结构体对象叫做struct inode, 然后将大部分文件的属性保存到这个结构体里面 然后struct file里面包含一个指向这个结构体的指针, 如下图:

        然后我们的上层, 也就是应用层。 我们是如何打开的一个文件?  ——这里以前讲过, 这里相当于重新复习。 这里只是简单的说一下流程。

        首先我们要知道这个文件的路径+文件名。 ——》这就相当于知道了当前目录, 然后我们就可以读取当前目录的数据块, 找到对应目录的inode, 进而找到对应的struct file 对象的inode, 然后查看已经加载到内存中的inode bitmap, 看这个inode是否存在, 如果存在, 再讲磁盘中等等属性信息加载到内存中。

adress_space

另外其实在struct file里面, 存在一个数据结构的指针, 这个数据结构叫做adress_space(地址空间)。 如下图:

adress_space这个数据结构里面包含一颗radix树, 这棵树叫做page_tree

radix_tree这个结构的定义如下:

        这是整颗树的结构体定义。这棵树是一颗多叉树:

        这个叶子结点, 其实就是上面的radix_tree_node类型的对象。

        真正的树节点的结构是下面这张图:

        radix_tree_node这个结构体里面, 我们看一下slots指针数组。 这个指针数组其实就是当前节点的叶子节点的指针。 上图中有RADIX_TREE_MAP_SIZE个数组元素, 说明这是一颗除叶子结点外,每个节点都有RADIX_TREE_MAP_SIZE个孩子节点的多叉树。

        那么可能有的友友们会疑惑了, 这颗树的叶子结点连接的是什么呢?是不是指向空呢?——答案是指向的是我们的page!!!——我们知道, 我们使用fopen, fprintf本质上就是先向内存中写入数据, 再将内存中的数据刷新到磁盘里面。而page就是物理内存page现在被一颗树指向了!!那么是不是也就是说, 我们的用户层, 在使用fopen, fprintf向文件中打印的时候, 一定会先按照一种流程, 这种流程从上到下贯穿整个文件管理, 一直进入地址空间, 然后穿过整颗radix树, 最后找到page, 将数据写到物理内存里面!!!——而这, 就是整个的fopen, fprintf写入数据的过程。 这, 也是内存进行管理管理的过程。 这里的page,这颗树连接的所有的page, 组成的这个page_array[]数组, 其实就是文件页缓冲区, 也是我们所说的内核级缓冲区!!!

        画成图就是这样子:

        这其实就是用户层到内存的整个流程, 也可以说是结论。 

radix_tree解析——补充知识

        radix_tree到底是一棵什么样的树呢?

        其实, 这棵树叫做基数树 或者 基数——》本质上就是一棵字典树。 

        这棵树的节点的结构大致如下:

struct node
{
    int level; //层数
    void* slot[NUM]; //这个NUM可以是很多数, 可以是26, 可以是3, 可以是5
}

        我们这里假设NUM是3, 也就是说这棵树每一个节点都有3个子节点。 并且, 字典树的层数一般和这个NUM是相同的。 也就是说, 每一个节点有几个孩子节点, 这棵树就有几层。 假设NUM为3, 那么这棵树就有三层, 并且每个节点的子节点都是三个。

上面这就是一颗字典树,最后叶子节点的slot指向的是要保存的数据. 也就是说, 一个叶子节点, 可以保存三个数据块。 那么如何找到某个数据呢?——如果我们要找bca这个数据, 那么就先找第一个节点的b子节点, 再找b子节点的cc子节点。 以此类推, 直到找到最后要查找到的数据, 上图中用红线画的就是整个查找流程。

        文件的内容按照4kb是有偏移量的, 加入今天我有一个10MB的大小的文件。 如下图:

         所以这里块的个数就是10 * 1024 * 1024 / 1024 / 4 = 2560

        然后每一个数据块的下标乘以4kb就能计算出这个数据在原始内容中的偏移量。 

        所以, 如果我们提前构建出一颗字典树, 那么我们如果将我们的文件内容的下标(下表是int类型), 以比特位为单位, 按照某种规则构成特定的几个区域, 在字典树当中找到对应的偏移量和page的映射关系。 就能通过偏移量等等直接查找物理内存了。

        那么未来我们读写文件, 都是通过偏移量, 我们一旦有了偏移量, 根据这棵字典树里面的映射关系, 我们就可以找到对应的page。  ——这个偏移量的具体细节博主也不清楚, 知道就行

偏移量不太重要, 但是下面的这个结论是非常重要的!

        这里的这个文件, 是有自己的文件页缓冲区的。 其实, 在linux中, 我们的每一个进程, 打开的每一个文件, 都要有自己的文件页缓冲区!!!这个文件页缓冲区, 就是字典树能够找到的所有的page结构体对象!!!

从内存到磁盘

        上面我们已经很清楚的讲解了数据如何从上层用户写到内存中。 那么接下里的一步就是如何从内存中写到磁盘里面。

        其实我们从这张图里面可以看到, 进程是不关心文件管理如何向内存中写入数据的。并且, 写到了物理内存之后, 文件管理也不管物理内存了。 那么从物理内存到磁盘, 到底是怎么管理的呢?——其实, 之后的任务就是驱动程序的工作了, 这个也叫做IO子系统。

        而要管理好未来我们的大量的IO请求, 操作系统就要对这些请求进行描述, 再组织起来。

而描述的结构体是一个驱动级别的结构体(知道就行,博主也不理解什么是驱动级别的结构体)

struct request
{
    //这里面包含了与请求相关的各个page;
    //这里面也包含了LBA, 也就是包含了要在磁盘的哪个位置写入数据!!!
}

        描述了之后, 系统如果想要将这些IO请求组织起来, 用的是大量的request 队列——为什么使用队列, 这是因为IO也需要排队的。 

        但是, 这里有一个问题——可能我们的这个队列里面存在了这样一个情况, 比如进程1访问1号扇区, 进程2访问6号扇区, 但是进程3访问3号扇区等等。  就是说磁盘在进行访问的时候, 如果直接按照顺序访问队列的进程, 那么磁头就可能访问不到连续的区域。 而磁头的重新定位会花费大量的时间(相对于计算机内部)所以我们再进行定位的时候要尽可能让我们的请求访问同一个盘面的, 同一个磁道的, 连续的扇区。 这样效率才能保证最高。 所以, 我们的操作系统就可以对上面的队列进行IO排序, 然后再IO合并。 以此来保证能够访问连续的扇区。   

经过操作系统的组织之后, 将request对象拿给磁盘驱动, 再由磁盘驱动去执行, 就能够将数据从内存写到我们的磁盘里面了!!!

那么上面就是数据从内存到磁盘到整个过程。

 

————————以上, 就是本节的全部内容, 下面为本节课笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】汇编分析,函数是如何调用,传参,返回

传参 有的是用寄存器传参,有的用push传参 我在MSVC编译测出来的是PUSH传参(debug模式),具体过程如下 long func(long a, long b, long c, long d,long e, long f, long g, long h) {long sum;sum (a b c d e f g h);ret…

护栏碰撞监测系统的发展前景如何?

​ ​护栏碰撞监测系统的发展前景可谓十分广阔,主要体现在以下几个方面: ​ ​一、交通领域需求持续增长 ​ ​1. 道路建设不断拓展 ​ ​●在全球范围内,公路、高速公路以及城市道路等基础设施持续处于新建与扩建的进程之中。以…

再也不用来回切换了,多微同一界面聚合聊天

手上有多个微信,工作时经常要来回切换登录,才能处理聊天和发圈等工作,来回切换不但容易导致号的状态存在异常,还会浪费很多时间登录,忙的时候还可能会导致处理不及时。 今天必须要给你们分享这个超棒的东西&#xff0…

如何写接口自动化测试断言?

🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 你真的会写自动化测试断言吗? 在接口测试中,断言是一项非常重要的操作,它是用来校验接口返回结果是否符合预期的一种手段。…

OLED显示屏详解(IIC协议0.96寸 STM32)

目录 一、介绍 二、模块原理 1.原理图 2.工作原理:SSD1306显存与命令 三、程序设计 main.c文件 oled.h文件 oled.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 OLED是有机发光二极管,又称为有机电激光显示(Organic Electrol…

惊人发现:clickhouse的cpu暴增之谜

点/击/蓝/字 关/注/我/们 一、背景 前段时间,公司的生产环境的clickhouse的cpu突然持续高压,持续时间大约5个小时,特写此文记录,深挖clickhouse的原理,持续学习。 题主所在的公司的一些历史数据会保存到clickhouse中…

基于php的公司员工管理系统—计算机毕业设计源码25190

摘 要 公司公司员工管理系统除了具有共享系统的全部功能以外,能通过对数据的分析对决策做出解释是其主要的新特点。其体系结构,将由专用的服务器/客户方式向广域网发展,使更多的系统间能够互相交流数据,带动整个行业、领域知识和效…

如何解决前端跨域问题:从CORS到JSONP

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介如何解决前端跨域问题:从CORS到JSONP1. 引言2. 什么是跨域问题?2.1 同源策略(Same-Origin Policy)2.2 跨域请求的场景 3. 解决跨域问题的常见方法3.1 使用CORS(Cross-Orig…

实在智能联合案例入选中国信通院2024年度高质量数字化转型案例集

为推动数字化转型相关产业发展,培育数据要素市场,为数字化双方提供行业实践标杆参考,中国信通院铸基计划于2024年4月初启动高质量数字化转型典型案例征集工作,旨在遴选一批具有产业引领与推广应用效应的企业数字化转型典型案例。实…

【MySQL】Explain执行计划(十七)

🚗MySQL学习十七站~ 🚩本文已收录至专栏:MySQL通关路 ❤️每章节附章节思维导图,文末附全文思维导图,感谢各位点赞收藏支持~ ⭐学习汇总贴,超详细思维导图:【MySQL】学习汇总(完整思维导图) 一.…

[python]面向对象示例:学生管理系统

python面向对象 python基础知识整理 主要界面 建议跟着框架自行完成功能需求,最下面有完整带注释版本,学生信息会存到当前目录下data.txt文本中,没有则会自行创建 涉及到的知识点 面向对象列表, 字典for, if, input…文件操作 需求分析 开始 -> 循环调用主程序 ->不…

盲盒小程序开发,探索市场发展优势

随着潮玩市场的火热,盲盒已经成为了一种集娱乐、社交、消费为一体的模式,越来越多的消费者被盲盒所吸引,盲盒市场也迎来了巨大的发展空间,带来了各种商业机遇,同时为创业者提供新的创业渠道。本文将探讨盲盒小程序的开…

deepin-wine8-stable为某个windows应用设置环境变量

1 环境说明 deepin v23wine 8.16 2 操作步骤 2.1 在终端指定应用打开注册表编辑 WINEPREFIX~/.deepinwine/com.allroundautomations/ deepin-wine8-stable regeditWINEPREFIX:windows应用在wine容器中的路径,一般为~/.deepinwine/你的应用名称包regedit:注册表编…

类型组TYPE-POOL

文章目录 创建类型组使用类型组运行结果 创建类型组 使用类型组 *&---------------------------------------------------------------------* *& Report Z_TEST_TYPEPOOL *&---------------------------------------------------------------------* *& *&…

ClimODE——使用神经网络ODE 进行天气预报

概述 论文地址:https://arxiv.org/abs/2404.10024 源码地址:https://github.com/Aalto-QuML/ClimODE.git 这项研究提出了用于天气预报的神经 ODE 系统 ClimODE,该系统的设计特点是通过局部卷积运算获取局部依赖关系,通过全局关注…

Pycharm虚拟环境中使用pip命令报错ModuleNotFoundError解决办法

Pycharm虚拟环境中输入pip 相关命令时报错ModuleNotFoundError,记录一下。 软件版本: windows:win11 Python:3.11.4 PyCharm:转业版 2024.1.3 报错如下: 在虚拟环境中输入pip 相关命令时报错"ModuleNotFoundError: No module named pip._internal…

基于yolov8的绝缘子缺陷检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的绝缘子缺陷检测系统是一种利用先进深度学习技术的高效解决方案,旨在提升电力行业中输电线路的维护和监控水平。YOLOv8作为YOLO系列算法的最新版本,具备更高的检测速度和精度,特别适用于实时物体检测任务。 该系统…

【Spring Boot-IDEA创建spring boot项目方法】

1. 使用Spring Initializr 的 Web页面创建项目 2. 使用 IDEA 直接创建项目,其中有两种不同的搭建路径 3. 使用 IDEA 创建Maven项目并改造为springBoot 最常使用的两种方法其实就是一种,这里介绍在ieda中如何搭建 SpringBoot项目。 1.new Project--> 2…

kafka快速上手

一、kafka介绍 Kafka 是一个分布式流媒体平台,类似于消息队列或企业消息传递系统。 kafka官网:http://kafka.apach e.org/ 二、kafka入门 生产者发送消息,多个消费者只能有一个消费者接收到消息生产者发送消息,多个消费者都可以接收到消息 …