【Linux】缓冲区和文件系统

news2024/12/27 11:11:19

目录

一、缓冲区

1.1 概念

1.2 用户缓冲区和内核缓冲区

二、磁盘的结构

三、文件系统

3.1 初识“块”和inode

3.2 磁盘分区和文件系统


一、缓冲区

1.1 概念

要理解什么是缓冲区,先看这段代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    const char *fstr = "fwrite\n";
    const char *str = "write\n";

    printf("printf\n");
    fprintf(stdout, "fprintf\n");
    fwrite(fstr, strlen(fstr), 1, stdout);
    write(1, str, strlen(str));

    fork();
    return 0;
}

 执行程序,会向终端打印四行内容

但是如果我们把程序输出的内容重定向到一个普通文件中,会打印多少行呢?

此时变成了7行内容,其中printf、fprintf和fwrite打印了两次,为什么?

我们可以看到,在程序的最后创建了一个子进程,所以肯定是因为子进程所以才会重复打印

并且重复打印的接口都是库函数,而系统调用接口write并没有打印两次

在前面的学习中,我们已经初步认识了什么是缓冲区,这里简单回顾一下

实际上我们在对一个文件进行写入时并不是直接写入到文件中的,而是先写入到缓冲区,缓冲区再根据不同的方式将其内容刷新到文件中

缓冲区有三种刷新方式:

  • 无缓冲,即一进行写入就对缓冲区进行刷新,一个一个字符写入
  • 行缓冲,遇到\n才对缓冲区进行刷新,一行一行写入
  • 全缓冲,缓冲区满了才进行刷新,一堆一堆写入

一般我们在向显示器文件进行写入时采用的策略是行缓冲,即遇到\n就打印一行;而对于普通文件系统则采用全缓冲策略。缓冲区的存在能够减少消耗,提高效率

而我们还知道,子进程虽然会继承父进程的代码和数据,不过一旦一方修改了数据,就会发生写时拷贝

而缓冲区不同的刷新方式配合上写时拷贝,你是否已经知道为什么会发生重复打印了呢?

没错,因为向显示器文件中打印的时候采用行缓冲策略,所以缓冲区中不会有内容堆积,遇到\n就刷新一次缓冲区

所以在最后创建子进程时父进程的缓冲区空空如也,所以子进程的缓冲区也是空的

但是,如果重定向到了普通文件,此时就要采用全缓冲策略,所有的内容都堆积在缓冲区中

子进程创建时继承了父进程包含一大堆内容的缓冲区,而父进程在退出时刷新缓冲区的时候发生写时拷贝,不会影响到子进程的缓冲区,此时子进程再进行写入,就导致部分内容被写入了两次

但是为什么write不会重复写呢?

1.2 用户缓冲区和内核缓冲区

我们观察到,在向普通文件中打印时,库函数被打印了两次,而只有系统调用接口write打印了一次

为什么?

实际上C语言中的库函数写入的缓冲区是C语言提供的用户级缓冲区,而write这种系统调用接口直接写入的是内核的缓冲区

父子进程间的写时拷贝局限于用户态,不会影响到内核缓冲区

C语言的部分文件操作接口封装了系统调用接口write,库函数首先将内容写入到用户级缓冲区,然后根据不同的刷新方式调用write将内容写入到内核的缓冲区

我们再来看一段代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    const char *fstr = "fwrite\n";
    const char *str = "write\n";

    printf("printf\n");
    fprintf(stdout, "fprintf\n");
    fwrite(fstr, strlen(fstr), 1, stdout);
    write(1, str, strlen(str));
    close(1); // 关闭标准输出流

    return 0;
}

将打印的内容重定向到普通文件,此时发现只有write被打印了

这也是因为close关闭文件描述符时只会把内核缓冲区中的内容刷新到文件中,所以库函数向用户缓冲区写入的内容采用全缓冲还停留在用户缓冲区内,而write向内核缓冲区写入的内容被刷新到了文件中

C语言提供的用户缓冲区在哪呢?前面我们见过C语言提供的FILE结构体,存放了文件描述符等内容,其内部还有对应文件的缓冲区字段和维护信息

语言都属于用户层,C语言提供的FILE结构体自然也属于用户,所以其内部的缓冲区属于用户级缓冲区


二、磁盘的结构

磁盘是计算机主要的存储介质,可以存储大量的二进制数据,并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘(Floppy Disk,简称软盘),如今常用的磁盘是硬磁盘(Hard disk,简称硬盘) 

文件在没被打开的时候,是以二进制形式存储在硬盘中的,而硬盘又是如何存储这些二进制数据的呢?

机械硬盘的内部结构:

一个硬盘里可能堆叠了多张盘片,盘片的每一面都有一个磁头,这些所有的磁头都是连在同一个磁头臂上的,所以只能共同移动。磁头和盘面不接触

盘片表面涂有磁性物质,通过输入不同的电流方向,来改变不同部位的磁性,从而就能区分出0和1

而一个盘面又分为许多不同半径的同心圆,每个圆环是一个磁道,磁道的内部又分为许多的扇区

每个扇区能够存储的内容大小一样,其中,最内侧磁道上的扇区面积最小,因此数据密度最大

每张盘片的大小相同,而在不同的盘片中处于相同位置(同样半径)的磁道组成了柱面

磁盘被访问的最基本单元是扇区,一个扇区一般是512字节

因此,我们可以把磁盘看作由无数个扇区构成的存储介质

既然有了扇区,要把数据保存到磁盘中或取出数据,首先要解决的问题是:如何定位一个扇区

要定位一个扇区,我们得先知道要保存到哪一面(即哪个磁头),和哪一个磁道(柱面)

因此产生了CHS寻址方式:依据柱面(磁道)、磁头、扇区来查找数据位置

首先定位对应数据所在的盘面,移动对应盘面的磁头 ;磁头臂带动磁头在盘面上移动,就是定位磁道的过程;而主轴带动盘片旋转,则是定位扇区的过程

如果我们像磁带一样把磁盘的所有盘面展开成一个线性的结构,那么整个磁盘就像是一个由无数扇区构成的数组,每个扇区就有自己独立的编号,我们称为LBA地址

现在我们可以把磁盘视为一个数组,只需要知道每个盘面有多少个扇区,每个磁道有多少个扇区,我们就可以通过某个扇区的下标找到其对应的磁头、磁道、扇区了

例如我们假设一个盘面有20000个磁道,一个磁道有400个扇区

通过计算,我们就可以把LBA地址转化为CHS地址


三、文件系统

3.1 初识“块”和inode

一个文件如果为空,那么它存储在磁盘中是否不会占用空间呢?答案是否

文件不单单包含文件内容,还包含文件属性。文件的内容由一个个数据块存储,而文件的属性则需要一个名为inode的结构保存

数据块(block)的大小通常为4KB,即8个扇区组成一个块。块是文件存取的最小单位,即操作系统在读取硬盘时不会一个个扇区存取,这样影响效率,而是最少取一个块。文件的属性则由名为inode的结构存储,其内部存放了inode编号、文件类型、文件权限等属性

每个数据块也有编号,因为文件的内容存储在许多的数据块中,inode中一定会有一个数组存储该文件的所有块编号。但是如果块很多,一个数组大小不一定存的下这么多编号

为了让数组存下整个文件的所有块编号,数组后几个位置存在多级索引,具体实现为其编号对应的块中存储的不是文件内容,而是其他块的编号。在逻辑上体现为一颗多叉树的结构

需要注意的是,inode中的属性不包含文件名,每个inode都有自己唯一的编号,系统中标识一个文件只看inode编号

我们也可以通过 ls -li 命令来查看一个文件的inode编号:

除此之外,我们还可以通过stat命令来获取更详细的文件属性信息:

3.2 磁盘分区和文件系统

我们发现,即使我们的电脑里只有一块硬盘,但也能分出很多个区,即我们电脑中的C盘、D盘等,并且我们还可以自己设置这些分区的大小

但是这些分区还是太大了,不便于管理,因此我们再将一个分区划分为多个块组

在Linux的ext2文件系统中,Boot Block(引导块)位于分区的开头,占用1024字节,用于存放开机管理程序

而每个Block group又细分为

其中:

  • Super Block(超级块):存放文件系统本身的结构信息,主要有block和inode的总量、未使用的block和inode的数量、单个block和inode的大小、最近一次写入数据的时间等等。Super Block对于文件系统而言是至关重要的,所以ext2文件系统会在多个块组中保存超级块的信息
  • Group Descriptor Table(GDT):块组描述符表,描述块组的属性信息
  • Block Bitmap(块位图):记录Data blocks中哪些数据块已被使用,哪些数据块未被使用
  • inode Bitmap(inode位图):记录inode的使用情况
  • inode Table(inode表):存放inode
  • Data blocks:存放数据块

所以对一个文件的增删查改,实际上就是对文件系统的操作 

新建一个文件,在inode位图中找到最近一个未被使用的位置来分配inode并初始化信息,然后根据文件的大小分配数据块并修改inode位图和块位图结构

删除一个文件,将文件所属的inode位图和块位图中的位置修改为0即可(删除即允许被覆盖)

查找一个文件,通过其路径(哪个盘)就可以找到所在分区,然后根据inode编号知道其属于哪个块组的,在inode的位图中确认其是否存在,确认存在就在inode表中取出其inode中的信息并载入内存,通过这些信息找到文件所属的数据块,将这些块拼接得到文件内容

修改一个文件,还是先找到其inode,然后修改对应的属性,如果文件大小发生变动还需要决定是否新增数据块或删除数据块并修改位图

但是问题来了:我是用户,只用文件名,我怎么知道文件的inode编号?

在Linux下一切皆文件,目录也是文件,也有自己的内容和属性,和自己的inode编号

而目录的内容,就是该目录下文件的文件名与inode编号的映射关系

这也解释了为什么同一目录下不能有同名文件,就像map中不能有相同的key一样

但是,要找一个文件得先知道其目录的inode编号,但是其目录的inode编号又被这个目录的父目录管理着,还得知道父目录的inode编号...一路下去我们就得先从根目录开始找起

这样的方式一定程度上会影响效率,所以系统把我们常访问的目录存到dentry缓存

简单的介绍了一下文件系统,如有错误欢迎在评论区指出

完.

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

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

相关文章

OpenTelemetry:新一代的开源可观测性标准

OpenTelemetry是由OpenTracing和OpenCensus两个开源项目合并而成&#xff0c;由云原生计算基金会&#xff08;CNCF&#xff09;托管。该项目旨在提供一套标准化的、跨语言的观测性工具&#xff0c;帮助开发人员和运维人员更好地理解和管理分布式系统的性能和行为。通过统一的Me…

html5宠物网站模板源码

文章目录 1.设计来源1.1 主界面1.2 主界面菜单1.3 关于我们界面1.4 宠物照片墙界面1.5 宠物博客界面1.6 宠物服务界面1.7 宠物团队界面1.8 联系我们界面 2.效果和源码2.1 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 【博主推荐】&a…

微信小程序同城信息共享

前端uniapp 后端thinkphp 热乎的纯手工代码

机械学习—零基础学习日志(如何理解线性代数3)

零基础为了学人工智能&#xff0c;正在快乐学习&#xff0c;每天都长脑子 行列式 最早行列式&#xff0c;是莱布尼茨用于判断&#xff0c;一个方程有没有解。例如&#xff0c;三元一次方程&#xff0c;如果有解&#xff0c;对应行列式就有值&#xff0c;但是如果无解&#xf…

一篇文章理清Java持久化脉络(关于JDBC、JPA、Hibernate、Spring Data JPA)

Java持久化 一、JDBC、JPA、Spring Data JPA 的定义及关系二、JDBC&#xff08;古老的东西&#xff0c;只需简单知道是啥&#xff09;1.1 JDBC概念1.2 JDBC示例 三、JPA&#xff08;第二代持久化&#xff0c;代表是Hibernate等框架&#xff09;3.1 JPA概念3.2 JPA示例 四、Spri…

GB/T 38082-2019 生物降解塑料购物袋检测

生物降解塑料购物袋是指以生物降解树脂为主要原料制得的&#xff0c;具有提携结构的&#xff0c;在销售、服务等场所用于盛装及携提商品的袋制品。 GB/T 38082-2019 生物降解塑料购物袋检测项目&#xff1a; 检测项目 测试标准 尺寸偏差 GB/T 38082 感官 GB/T 38082 提掉…

html+css+js网页设计 大一电商6个页面 带js 有轮播图,增删改查等功能

htmlcssjs网页设计 大一电商6个页面 带js 有轮播图&#xff0c;增删改查等功能 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等…

JTAG标准笔记:IEEE1149.1、IEEE1149.4、IEEE1149.5、IEEE1149.6、 IEEE1500等协议之前的发展和联系

JTAG (Joint Test Action Group) 是一种串行通信协议。对于典型的串行通信&#xff0c;总线较少&#xff0c;线路数通常为1到4条&#xff0c;数据是以位为单位依次传输的。笔记中大部分图片来自JTAG标准介绍UP的视频 IEEE 1149.1&#xff0c;通常称为JTAG&#xff08;Joint Tes…

安防监控/视频汇聚平台EasyCVR如何配置,实现默认获取设备的子码流?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构&#xff0c;兼容性强、支持多协议接入&#xff0c;包括国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SD…

设计模式22-迭代器模式

设计模式22-迭代器模式 迭代器模式&#xff08;Iterator Pattern&#xff09;动机定义结构定义结构结构图解释注意事项 C代码推导多态属性&#xff08;虚函数&#xff09;实现迭代器1. **返回值问题**2. **对象切割问题**3. **内存管理问题**4. **迭代器生命周期问题**5. **接口…

static、extern,const关键字

1、static关键字 static关键字&#xff1a;延长生命周期&#xff0c;限制作用域 static修饰局部变量&#xff1a;静态局部变量 static修饰全局变量&#xff1a;静态全局变量 static修饰函数&#xff1a;静态函数 2、extern关键字 extern&#xff1a;引用其他文件 .c 中的全局…

对敲期权组合如何操作?

对敲期权组合按照你说的对沖敲出期权应该是一种期权套利行为&#xff0c;在买入的同时卖出一个执行价格不同的期权进行对冲&#xff0c;或者在卖出一张期权合约的时候同时买进一张执行价不动的同类期权进行对中&#xff0c;这样亏报有限&#xff0c;是种套利行为&#xff0c;下…

Java基础之进制转换

1 进制基础 概念&#xff1a; ​ 进制就是进位制&#xff0c;是人们规定的一种进位方法&#xff0c;二进制逢2进1&#xff0c;八进制是逢8进1&#xff0c;十进制逢10进1&#xff0c;十六进制逢16进1。 不同进制形式&#xff1a; 二进制 0b或0B开头&#xff0c;由0和1组成 八…

爬虫配置代理:保护隐私有效地抓取数据

爬虫配置代理的详细指南 在进行网络爬虫时&#xff0c;使用代理可以帮助我们更有效地抓取数据&#xff0c;避免IP被封禁&#xff0c;并提高隐私保护。本文将详细介绍如何在爬虫中配置代理&#xff0c;包括不同的代理类型、如何选择合适的代理以及在Python中实现代理的具体步骤…

中国软件评测中心:2024年最新人工智能大语言模型技术发展研究报告 (附文档)

人工智能作为引领新一轮科技产业革命的战略性技术和新质生产力重要驱动力&#xff0c;正在引发经济、社会、文化等领域的变革和重塑&#xff0c;2023 年以来&#xff0c;以 ChatGPT、GPT-4 为代表的大模型技术的出台&#xff0c;因其强大的内容生成及多轮对话能力&#xff0c;引…

python-A+B again

[题目描述] 小理有一个非常简单的问题给你&#xff0c;给你两个整数 A 和 B&#xff0c;你的任务是计算 AB。输入格式&#xff1a; 输入共 2∗T1 行。 输入的第一行包含一个整数 T 表示测试实例的个数&#xff0c;然后 2∗T 行&#xff0c;分别表示 A 和 B 两个正整数。注意整数…

调研在深度学习中如何读代码

这里调研了四个up主的内容&#xff0c;对他们讲的内容摘了一下主要的内容。想要看原文的画可以看原篇。 1.如何学习别人的代码&#xff08;代码量较大时&#xff09;_怎么学习别人的代码-CSDN博客 想要掌握的好&#xff0c;光阅读是不够的&#xff0c;一定要动手写、训练模型…

k8s 部署RuoYi-Vue-Plus之minio搭建

1.直接部署一个pod 需要挂载存储款, 可参考 之前文章设置 https://blog.csdn.net/weimeibuqieryu/article/details/140183843 2.部署yaml 创建部署文件 minio-deploy.yaml apiVersion: v1 kind: PersistentVolume metadata:name: minio-pvnamespace: ruoyi #使用ns ruoyi s…

MyCAT读写分离实现

1. 添加一个新的虚拟主机&#xff0c;设置ip为10.1.1.60,主机名为 mycat.yuanyu.zhangmin.关闭防火墙 SELinux NetworkManager 2. 上传jdk和mycat安装包 3. 解压并且添加到指定的位置 4. 查看并且配置jdk环境 、5. 测试启动myca就可以了 6. 找到server.xml和schema.xml 7. 配…

Python酷库之旅-第三方库Pandas(081)

目录 一、用法精讲 336、pandas.Series.str.rpartition方法 336-1、语法 336-2、参数 336-3、功能 336-4、返回值 336-5、说明 336-6、用法 336-6-1、数据准备 336-6-2、代码示例 336-6-3、结果输出 337、pandas.Series.str.slice方法 337-1、语法 337-2、参数 …