• Linux文件系统简单探究


    前言

    前不久,稍微了解一下Linux kernel pwn的一小部分内容,觉得有些概念性的东西不够了解,例如fop结构体等,然后就发现牵连到虚拟文件系统的内容,然后今天打算去了解虚拟文件系统(VFS)的时候,又发现涉及到Linux的文件系统的内容。加之自己之前就对Linux的文件系统比较感兴趣,因为一直很想了解Linux的一切皆文件这个概念,所以就去简单探究一下Linux文件系统。

    一切皆文件

    在LINUX系统中有一个重要的概念:一切都是文件。 其实这是UNIX哲学的一个体现,而Linux是重写UNIX而来,所以这个概念也就传承了下来。在UNIX系统中,把一切资源都看作是文件,包括硬件设备。UNIX系统把每个硬件都看成是一个文件。一切都可看作是文件,其最显著的好处是对于Linux系统中的输入/输出资源,只需要相同的一套 Linux 工具、实用程序和 API。你可以使用同一套api(read, write)和工具(cat , 重定向, 管道)来处理unix中大多数的资源。

    现代操作系统为解决信息能独立于进程之外被长期存储引入了文件,文件作为进程创建信息的逻辑单元可被多个进程并发使用。在 UNIX 系统中,操作系统为磁盘上的文本与图像、鼠标与键盘等输入设备及网络交互等 I/O 操作设计了一组通用 API,使他们被处理时均可统一使用字节流方式。换言之,UNIX 系统中除进程之外的一切皆是文件,而 Linux 保持了这一特性。为了便于文件的管理,Linux 还引入了目录(有时亦被称为文件夹)这一概念。目录使文件可被分类管理,且目录的引入使 Linux 的文件系统形成一个层级结构的目录树。

    Linux文件体系

    Linux以文件的形式对计算机中的数据和硬件资源进行管理,也就是彻底的一切皆文件,反映在Linux的文件类型上就是:普通文件、目录文件(也就是文件夹)、设备文件、链接文件、管道文件、套接字文件(数据通信的接口)等等。而这些种类繁多的文件被Linux使用目录树进行管理, 所谓的目录树就是以根目录(/)为主,向下呈现分支状的一种文件结构。不同于纯粹的ext2之类的文件系统,我把它称为文件体系,一切皆文件和文件目录树的资源管理方式一起构成了Linux的文件体系,让Linux操作系统可以方便使用系统资源。

    但是我看到网上不少文章已经把文件系统基本等同于文件体系,因为文件系统会把目录树的内容囊括进去。所以接下来的介绍我也不会把文件系统和文件体系区分开。

    Linux中的七种文件类型

    • 普通文件类型
      Linux中最多的一种文件类型, 包括 纯文本文件(ASCII);二进制文件(binary);数据格式的文件(data);各种压缩文件.第一个属性为 [-]
    • 目录文件
      就是目录, 能用 # cd 命令进入的。第一个属性为 [d],例如 [drwxrwxrwx]
    • 块设备文件
      块设备文件 : 就是存储数据以供系统存取的接口设备,简单而言就是硬盘。例如一号硬盘的代码是 /dev/hda1等文件。第一个属性为 [b]
    • 字符设备文件
      字符设备文件:即串行端口的接口设备,例如键盘、鼠标等等。第一个属性为 [c]
    • 套接字文件
      这类文件通常用在网络数据连接。可以启动一个程序来监听客户端的要求,客户端就可以通过套接字来进行数据通信。第一个属性为 [s],最常在 /var/run目录中看到这种文件类型
    • 管道文件
      FIFO也是一种特殊的文件类型,它主要的目的是,解决多个程序同时存取一个文件所造成的错误。FIFO是first-in-first-out(先进先出)的缩写。第一个属性为 [p]
    • 链接文件
      类似Windows下面的快捷方式。第一个属性为 [l],例如 [lrwxrwxrwx]

    文件系统

    注: 这里的文件系统是通过介绍EXT2这个文件系统。

    Linux 最传统的磁盘文件系统 (filesystem) 使用的是 EXT2 这个;所以要了解 Linux 的文件系统就得要由认识 EXT2 开始!

    磁盘的组成与分块

    磁盘的组成

    文件系统是创建在磁盘上面的,因此我们得了解磁盘的物理组成才行。
    我们在学习计算机组成原理以及计算机概论的时候了解过磁盘的组成主要有盘片、机械手臂、磁头与主轴马达所组成, 而数据的写入其实是在盘片上面。盘片上面又可细分出扇区(Sector)与磁道(Track)两种单位, 其中扇区的物理量设计有两种大小,分别是 512Bytes 与 4KBytes。假设磁盘只有一个盘片,那么盘片有点像下面这样:

    这个图可能看得有点简略;我们看看以下两幅图

    • 扇区
    1. 盘片在转动时,磁头在盘面上画过的一段圆弧,成称扇区,即sector
    2. 扇区并不是连续的,在磁道上被划分成一段一段的,从1开始编号
    3. 是硬盘最小的物理存取单位,每个扇区为512byte
    • 磁道
    1. 磁盘在格式化时被划分成许多同心圆,这些同心圆轨迹叫做磁道,即track
    2. 磁道从外向内从0开始编号,盘面的容量越大,包含的磁道数越多
    3. 磁道是看不见的,只是盘面上一些被磁化的区域
    • 柱面
    1. 所有盘面上相同编号的磁道构成的圆柱,称为柱面,即 cylinder
    2. 每个柱面上的磁头(header)由上到下从0开始编号
    3. 数据的读写是按照柱面进行的,而非按照盘面进行
      4.柱面是分区的最小单位

    磁盘的分块

    硬盘分区是硬盘结合到文件体系的第一步,本质是「硬盘」这个物理概念转换成「区」这个逻辑概念,为下一步格式化做准备。
    在上面磁盘的组成有说到,扇区的概念。那么我们想一下是不是每个扇区都是一样重要呢。其实整块磁盘的第一个扇区特别重要。因为它记录了整块扇区的重要信息,分别是:

    1. 硬盘主引导记录(Master boot record, MBR),有446 Bytes
    2. 分区表(partition table),有64 Bytes

    其中硬盘主引导记录放有最基本的引导加载程序,是系统开机启动的关键环节,这里不展开细讲,对着部分感兴趣的同学可以参考以下《鸟哥的Linux私房菜》第三版P72,3.2.4章节的内容。
    而对于我们的分区表,就是我们磁盘分区的具体实现形式。

    由于分区表所在区块仅有64 Bytes容量,因此最多仅能有四组记录区,每组记录区记录了该区段的启始与结束的柱面号码。 若将硬盘以长条形来看,然后将柱面以直条图来看,那么那64 Bytes的记录区段有点像下面的图示:

    这四个分区的记录被称为主要(Primary)或延伸(Extended)分区。 根据上面的图示与说明,我们可以得到几个重点信息:

    • 其实所谓的“分区”只是针对那个64 Bytes的分区表进行设置而已!
    • 硬盘默认的分区表仅能写入四组分区信息
    • 这四组分区信息我们称为主要(Primary)或延伸(Extended)分区
    • 当系统要写入磁盘时,一定会参考磁盘分区表,才能针对某个分区进行数据的处理

    既然分区表只有记录四组数据的空间,那么是否代表我一颗硬盘最多只能分区出四个分区?那多个分区又是如何达到的呢?在Windows/Linux系统中, 我们是通过延伸分区(Extended)的方式来处理的啦!延伸分区的想法是: 既然第一个扇区所在的分区表只能记录四笔数据, 那我可否利用额外的扇区来记录更多的分区信息?实际上图示有点像下面这样:

    在上图当中,我们知道硬盘的四个分区记录区仅使用到两个,P1为主要分区,而P2则为延伸分区。请注意, 延伸分区的目的是使用额外的扇区来记录分区信息,延伸分区本身并不能被拿来格式化。 然后我们可以通过延伸分区所指向的那个区块继续作分区的记录。

    如上图右下方那个区块有继续分区出五个分区, 这五个由延伸分区继续切出来的分区,就被称为逻辑分区(logical partition)。
    所以,我们可以得出以下结论:

    1. 主要分区与延伸分区最多可以有四笔(硬盘的限制);
    2. 延伸分区最多只能有一个(操作系统的限制);
    3. 逻辑分区是由延伸分区持续切割出来的分区;
    4. 能够被格式化后,作为数据存取的分区为主要分区与逻辑分区。延伸分区无法格式化(格式化的概念见下文介绍);
    5. 逻辑分区的数量依操作系统而不同,在Linux系统中SATA硬盘已经可以突破63个以上的分区限制;

    格式化

    Linux操作系统支持很多不同的文件系统,比如ext2、ext3、XFS、FAT等等,而Linux把对不同文件系统的访问交给了VFS(虚拟文件系统),VFS能访问和管理各种不同的文件系统。所以有了区之后就需要把它格式化成具体的文件系统以便VFS访问。

    文件系统特性

    我们都知道磁盘分区完毕后还需要进行格式化(format),之后操作系统才能够使用这个文件系统。 为什么需要进行“格式化”呢?这是因为每种操作系统所设置的文件属性/权限并不相同, 为了存放这些文件所需的数据,因此就需要将分区进行格式化,以成为操作系统能够利用的“文件系统格式(filesystem)”。

    传统的磁盘与文件系统之应用中,一个分区就是只能够被格式化成为一个文件系统,所以我们可以说一个 filesystem 就是一个 partition。但是由于新技术的利用,例如我们常听到的LVM与软件磁盘阵列(software raid), 这些技术可以将一个分区格式化为多个文件系统(例如LVM),也能够将多个分区合成一个文件系统(LVM, RAID)! 所以说,目前我们在格式化时已经不再说成针对 partition 来格式化了, 通常我们可以称呼一个可被挂载的数据为一个文件系统而不是一个分区喔!

    那么文件系统是如何运行的呢?这与操作系统的文件数据有关。较新的操作系统的文件数据除了文件实际内容外, 通常含有非常多的属性,例如 Linux 操作系统的文件权限(rwx)与文件属性(拥有者、群组、时间参数等)。 文件系统通常会将这两部份的数据分别存放在不同的区块,权限与属性放置到 inode 中,至于实际数据则放置到 data block 区块中。 另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息,包括 inode 与 block 的总量、使用量、剩余量等。
    每个 inode 与 block 都有编号,至于这三个数据的意义可以简略说明如下:

    • superblock:记录此 filesystem 的整体信息,包括inode/block的总量、使用量、剩余量, 以及文件系统的格式与相关信息等;
    • inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block 号码;
    • block:实际记录文件的内容,若文件太大时,会占用多个 block 。

    由于每个 inode 与 block 都有编号,而每个文件都会占用一个 inode ,inode 内则有文件数据放置的 block 号码。 因此,我们可以知道的是,如果能够找到文件的 inode 的话,那么自然就会知道这个文件所放置数据的 block 号码, 当然也就能够读出该文件的实际数据了。这是个比较有效率的作法,因为如此一来我们的磁盘就能够在短时间内读取出全部的数据, 读写的性能比较好啰。

    我们将 inode 与 block 区块用图解来说明一下,如下图所示,文件系统先格式化出 inode 与 block 的区块,假设某一个文件的属性与权限数据是放置到 inode 4 号(下图较小方格内),而这个 inode 记录了文件数据的实际放置点为 2, 7, 13, 15 这四个 block 号码,此时我们的操作系统就能够据此来排列磁盘的读取顺序,可以一口气将四个 block 内容读出来! 那么数据的读取就如同下图中的箭头所指定的模样了。

    这种数据存取的方法我们称为索引式文件系统(indexed allocation)。那有没有其他的惯用文件系统可以比较一下啊? 有的,那就是我们惯用的U盘(闪存),U盘使用的文件系统一般为 FAT 格式。FAT 这种格式的文件系统并没有 inode 存在,所以 FAT 没有办法将这个文件的所有 block 在一开始就读取出来。每个 block 号码都记录在前一个 block 当中, 他的读取方式有点像下面这样:

    上图中我们假设文件的数据依序写入1->7->4->15号这四个 block 号码中, 但这个文件系统没有办法一口气就知道四个 block 的号码,他得要一个一个的将 block 读出后,才会知道下一个 block 在何处。 如果同一个文件数据写入的 block 分散的太过厉害时,则我们的磁头将无法在磁盘转一圈就读到所有的数据, 因此磁盘就会多转好几圈才能完整的读取到这个文件的内容!

    而标准的Linux文件系统Ext2是使用「基于inode的文件系统」;inode 的内容在记录文件的权限与相关属性,至于 block 区块则是在记录文件的实际内容。 而且文件系统一开始就将 inode 与 block 规划好了,除非重新格式化(或者利用 resize2fs 等指令变更文件系统大小),否则 inode 与 block 固定后就不再变动。但是如果仔细考虑一下,如果我的文件系统高达数百GB时, 那么将所有的 inode 与 block 通通放置在一起将是很不智的决定,因为 inode 与 block 的数量太庞大,不容易管理。

    为此之故,因此 Ext2 文件系统在格式化的时候基本上是区分为多个区块群组 (block group) 的,每个区块群组都有独立的 inode/block/superblock 系统。感觉上就好像我们在当兵时,一个营里面有分成数个连,每个连有自己的联络系统, 但最终都向营部回报连上最正确的信息一般!这样分成一群群的比较好管理啦!整个来说,Ext2 格式化后有点像下面这样:

    在整体的规划当中,文件系统最前面有一个开机扇区(boot sector),这个开机扇区可以安装开机管理程序, 这是个非常重要的设计,因为如此一来我们就能够将不同的开机管理程序安装到个别的文件系统最前端,而不用覆盖整颗磁盘唯一的 MBR, 这样也才能够制作出多重开机的环境啊!至于每一个区块群组(block group)的六个主要内容说明如后:

    1. inode table
      主要记录文件的属性以及该文件实际数据是放置在哪些block中,它记录的信息至少有这些:大小、真正内容的block号码(一个或多个)访问模式(read/write/excute)拥有者与群组(owner/group)各种时间:建立或状态改变的时间、最近一次的读取时间、最近修改的时间没有文件名!文件名在目录的block中!一个文件占用一个 inode,每个inode有编号Linux 系统存在 inode 号被用完但磁盘空间还有剩余的情况注意,这里的文件不单单是普通文件,目录文件也就是文件夹其实也是一个文件,还有其他的也是inode 的数量与大小在格式化时就已经固定了,每个inode 大小均固定为128 bytes (新的ext4 与xfs 可设定到256 bytes)文件系统能够建立的文件数量与inode 的数量有关,存在空间还够但inode不够的情况系统读取文件时需要先找到inode,并分析inode 所记录的权限与使用者是否符合,若符合才能够开始实际读取 block 的内容inode 要记录的资料非常多,但偏偏又只有128bytes , 而inode 记录一个block 号码要花掉4byte ,假设我一个文件有400MB 且每个block 为4K 时, 那么至少也要十万条block 号码的记录!inode 哪有这么多空间来存储?为此我们的系统很聪明的将inode 记录block 号码的区域定义为12个直接,一个间接, 一个双间接与一个三间接记录区。
    2. data block
      放置文件内容数据的地方在格式化时block的大小就固定了,且每个block都有编号,以方便inode的记录原则上,block 的大小与数量在格式化完就不能够再改变了(除非重新格式化)在Ext2文件系统中所支持的block大小有1K, 2K及4K三种,由于block大小的区别,会导致该文件系统能够支持的最大磁盘容量与最大单一文件容量各不相同:Block 大小 1KB 2KB 4KB最大单一档案限制 16GB 256GB 2TB最大档案系统总容量 2TB 8TB 16TB每个block 内最多只能够放置一个文件的资料,但一个文件可以放在多个block中(大的话)若文件小于block ,则该block 的剩余容量就不能够再被使用了(磁盘空间会浪费)所以如果你的档案都非常小,但是你的block 在格式化时却选用最大的4K 时,可能会产生容量的浪费既然大的block 可能会产生较严重的磁碟容量浪费,那么我们是否就将block 大小定为1K ?这也不妥,因为如果block 较小的话,那么大型档案将会占用数量更多的block ,而inode 也要记录更多的block 号码,此时将可能导致档案系统不良的读写效能事实上现在的磁盘容量都太大了,所以一般都会选择4K 的block 大小
    3. superblock
      记录整个文件系统相关信息的地方,一般大小为1024bytes,记录的信息主要有:block 与inode 的总量未使用与已使用的inode / block 数量一个valid bit 数值,若此文件系统已被挂载,则valid bit 为0 ,若未被挂载,则valid bit 为1block 与inode 的大小 (block 为1, 2, 4K,inode 为128bytes 或256bytes);其他各种文件系统相关信息:filesystem 的挂载时间、最近一次写入资料的时间、最近一次检验磁碟(fsck) 的时间Superblock是非常重要的, 没有Superblock ,就没有这个文件系统了,因此如果superblock死掉了,你的文件系统可能就需要花费很多时间去挽救每个块都可能含有superblock,但是我们也说一个文件系统应该仅有一个superblock 而已,那是怎么回事?事实上除了第一个块内会含有superblock 之外,后续的块不一定含有superblock,而若含有superblock则该superblock主要是做为第一个块内superblock的备份,这样可以进行superblock的救援
    4. Filesystem Description
      文件系统描述这个区段可以描述每个block group的开始与结束的block号码,以及说明每个区段(superblock, bitmap, inodemap, data block)分别介于哪一个block号码之间
    5. block bitmap
      块对照表如果你想要新增文件时要使用哪个block 来记录呢?当然是选择「空的block」来记录。那你怎么知道哪个block 是空的?这就得要通过block bitmap了,它会记录哪些block是空的,因此我们的系统就能够很快速的找到可使用的空间来记录同样在你删除某些文件时,那些文件原本占用的block号码就得要释放出来, 此时在block bitmap 中对应该block号码的标志位就得要修改成为「未使用中」
    6. inode bitmap
      与block bitmap 是类似的功能,只是block bitmap 记录的是使用与未使用的block 号码, 至于inode bitmap 则是记录使用与未使用的inode 号码

    挂载

    在一个区被格式化为一个文件系统之后,它就可以被Linux操作系统使用了,只是这个时候Linux操作系统还找不到它,所以我们还需要把这个文件系统「注册」进Linux操作系统的文件体系里,这个操作就叫「挂载」 (mount)。挂载是利用一个目录当成进入点(类似选一个现成的目录作为代理),将文件系统放置在该目录下,也就是说,进入该目录就可以读取该文件系统的内容,类似整个文件系统只是目录树的一个文件夹(目录)。这个进入点的目录我们称为「挂载点」。

    由于整个 Linux 系统最重要的是根目录,因此根目录一定需要挂载到某个分区。 而其他的目录则可依用户自己的需求来给予挂载到不同的分去。
    到这里Linux的文件体系的构建过程其实已经大体讲完了,总结一下就是:硬盘经过分区和格式化,每个区都成为了一个文件系统。

    目录树的读取过程

    首先我们要知道

    1. 每个文件(不管是一般文件还是目录文件)都会占用一个inode
    2. 依据文件内容的大小来分配一个或多个block给该文件使用
    3. 创建一个文件后,文件完整信息分布在3处地方,生成2个新文件:
      3.1 文件名记录在该文件所在目录的目录文件的block中,没有新文件生成
      3.2 文件属性、权限信息、记录具体内容的block编号记录在inode中,inode是新生成文件
      3.3 文件具体内存记录在block中,block是新生成文件
    4. 因为文件名的记录是在目录的block当中,「新增/删除/更名文件名」与目录的w权限有关
      所以在Linux/Unix中,文件名称只是文件的一个属性,叫别名也好,叫绰号也罢,仅为了方便用户记忆和使用,但系统内部并不需要用文件名来定为文件位置,这样处理最直观的好处就是,你可以对正在使用的文件改名,换目录,甚至放到废纸篓,都不会影响当前文件的使用,这在Windows里是无法想象的。比如你打开个Word文件,然后对其进行重命名操作,Windows会告诉你门儿都没有,关闭文件先!但在Mac里就毫无压力,因为Mac的操作系统同样采用了inode的设计。

    创建文件过程

    1. 当在ext2下建立一个一般文件时, ext2 会分配一个inode 与相对于该文件大小的block 数量给该文件
    2. 例如:假设我的一个block 为4 Kbytes ,而我要建立一个100 KBytes 的文件,那么linux 将分配一个inode 与25 个block 来储存该文件
    3. 但同时请注意,由于inode 仅有12 个直接指向,因此还要多一个block 来作为区块号码的记录

    创建目录过程

    当在ext2文件系统建立一个目录时(就是新建了一个目录文件),文件系统会分配一个inode与至少一块block给该目录

    1. inode记录该目录的相关权限与属性,并记录分配到的那块block号码
    2. 而block则是记录在这个目录下的文件名与该文件对应的inode号
    3. block中还会自动生成两条记录,一条是.文件夹记录,inode指向自身,另一条是..文件夹记录,inode指向父文件夹

    从目录树中读取某个文件过程

    因为文件名是记录在目录的block当中,因此当我们要读取某个文件时,就一定会经过目录的inode与block ,然后才能够找到那个待读取文件的inode号码,最终才会读到正确的文件的block内的资料。

    由于目录树是由根目录开始,因此操作系统先通过挂载信息找到挂载点的inode号,由此得到根目录的inode内容,并依据该inode读取根目录的block信息,再一层一层的往下读到正确的文件。

    总结

    本文主要是概念性的知识点,意在学习虚拟文件系统的一些基础。所以,接下来更新的博文是对应虚拟文件系统对应的实现,主要是一些数据结构吧。

    参考

    https://juejin.im/post/6844903668504854535#heading-0

    《鸟哥的Linux私房菜》第四版

    鸟哥的Linux私房菜第三版纸质版

  • 相关阅读:
    【解决】Git failed with a fatal error. Authentication failed for ‘http://......’
    利用BenchmarkDotNet 测试 .Net Core API 同步和异步方法性能
    mysql通过event和存储过程实时更新简单Demo
    执行命令npm install XXX后仍然提示 Cannot find Module XXX
    c#调用微信接口获取token值
    同一对象多条数据同时插入数据库
    常用的正则表达式(持续更新。。)
    【半转贴】解决SQL SERVER 2008数据库表中修改字段后不能保存
    asp.net中的服务器控件button的属性
    关于在asp.net中textbox文本输入框中的汉语标点符号显示位置的问题
  • 原文地址:https://www.cnblogs.com/T1e9u/p/13875943.html
Copyright © 2020-2023  润新知