linux 文件系统

转载: https://www.cnblogs.com/liwei0526vip/p/4998614.html

 

转载摘录:https://juejin.cn/post/6844903668504854535#heading-10

转载摘录: http://cn.linux.vbird.org/linux_basic/fedora_4/0230filesystem-fc4.php

 

Linux的一切皆文件

Linux 中的各种事物比如像文档、目录(Mac OS X 和 Windows 系统下称之为文件夹)、键盘、监视器、硬盘、可移动媒体设备、打印机、调制解调器、虚拟终端,还有进程间通信(IPC)和网络通信等输入/输出资源都是定义在文件系统空间下的字节流。
一切都可看作是文件,其最显著的好处是对于上面所列出的输入/输出资源,只需要相同的一套 Linux 工具、实用程序和 API。你可以使用同一套api(read, write)和工具(cat , 重定向, 管道)来处理unix中大多数的资源.
设计一个系统的终极目标往往就是要找到原子操作,一旦锁定了原子操作,设计工作就会变得简单而有序。“文件”作为一个抽象概念,其原子操作非常简单,只有读和写,这无疑是一个非常好的模型。通过这个模型,API的设计可以化繁为简,用户可以使用通用的方式去访问任何资源,自有相应的中间件做好对底层的适配。
现代操作系统为解决信息能独立于进程之外被长期存储引入了文件,文件作为进程创建信息的逻辑单元可被多个进程并发使用。在 UNIX 系统中,操作系统为磁盘上的文本与图像、鼠标与键盘等输入设备及网络交互等 I/O 操作设计了一组通用 API,使他们被处理时均可统一使用字节流方式。换言之,UNIX 系统中除进程之外的一切皆是文件,而 Linux 保持了这一特性。为了便于文件的管理,Linux 还引入了目录(有时亦被称为文件夹)这一概念。目录使文件可被分类管理,且目录的引入使 Linux 的文件系统形成一个层级结构的目录树

在Linux系统中,一切都是文件,理解文件系统,对于学习Linux来说,是一个非常有必要的前提

Linux上的文件系统一般来说就是EXT2或EXT3,但这篇文章并不准备一上来就直接讲它们,而希望结合Linux操作系统并从文件系统建立的基础——硬盘开始,一步步认识Linux的文件系统。

1. 机械硬盘的物理存储机制

  • 现代计算机大部分文件存储功能都是由机械硬盘这种设备提供的。(现在的SSD和闪存从概念和逻辑上都部分继承自机械硬盘,所以使用机械硬盘来进行理解也是没有问题的)
  • 机械硬盘能实现信息存储的功能基于:磁性存储介质能够被磁化,且磁化后会长久保留被磁化的状态,这种被磁化状态能够被读取出来,同时这种磁化状态还能够不断被修改,磁化正好有两个方向,所以可以表示0和1。
    于是硬盘就是把这种磁性存储介质做成一个个盘片,每一个盘片上都分布着数量巨大的磁性存储单位,使用磁性读写头对盘片进行写入和读取(从原理上类似黑胶唱片的播放)。
  • 一个硬盘中的磁性存储单位数以亿计(1T硬盘就有约80亿个),所以需要一套规则来规划信息如何存取(比如一本存储信息的书我们还会分为页,每一页从上到下从左到右读取,同时还有章节目录)
    于是就有了这些物理、逻辑概念:
  1. 一个硬盘有多张盘片叠成,不同盘片有编号
  2. 每张盘片上的存储颗粒成环形一圈圈地排布,每一圈称为磁道,有编号
  3. 每条磁道上都有一圈存储颗粒,每512*8(512字节,0.5KB)个存储颗粒作为一个扇区,扇区是硬盘上存储的最小物理单位
  4. N个扇区可以组成簇,N取决于不同的文件系统或是文件系统的配置,簇是此文件系统中的最小存储单位
  5. 所有盘面上的同一磁道构成一个圆柱,称为柱面,柱面是系统分区的最小单位

磁头读写文件的时候,首先是分区读写的,由inode编号(区内唯一的编号后面介绍)找到对应的磁道和扇区,然后一个柱面一个柱面地进行读写。机械硬盘的读写控制系统是一个令人叹为观止的精密工程(一个盘面上有几亿个存储单位,每个磁道宽度不到几十纳米,磁盘每分钟上万转),同时关于读写的逻辑也是有诸多细节(比如扇区的编号并不是连续的),非常有意思,可以自行搜索文章拓展阅读。

有了硬盘并不意味着LInux可以立刻把它用来存储,还需要组合进Linux的文件体系才能被Linux使用。

2.Linux文件体系

Linux以文件的形式对计算机中的数据和硬件资源进行管理,也就是彻底的一切皆文件,反映在Linux的文件类型上就是:普通文件、目录文件(也就是文件夹)、设备文件、链接文件、管道文件、套接字文件(数据通信的接口)等等。而这些种类繁多的文件被Linux使用目录树进行管理, 所谓的目录树就是以根目录(/)为主,向下呈现分支状的一种文件结构。不同于纯粹的ext2之类的文件系统,我把它称为文件体系,一切皆文件和文件目录树的资源管理方式一起构成了Linux的文件体系,让Linux操作系统可以方便使用系统资源。
所以文件系统比文件体系涵盖的内容少很多,Linux文件体系主要在于把操作系统相关的东西用文件这个载体实现:文件系统挂载在操作系统上,操作系统整个系统又放在文件系统里。但本文中文件体系的相关内容不是很多,大部分地方都可以用文件系统代替文件体系。

1. Linux中的文件类型:

1.1. 普通文件(-)

从Linux的角度来说,类似mp4、pdf、html这样应用层面上的文件类型都属于普通文件
Linux用户可以根据访问权限对普通文件进行查看、更改和删除

1.2. 目录文件(d,directory file)

目录文件对于用惯Windows的用户来说不太容易理解,目录也是文件的一种
目录文件包含了各自目录下的文件名和指向这些文件的指针,打开目录事实上就是打开目录文件,只要有访问权限,你就可以随意访问这些目录下的文件(普通文件的执行权限就是目录文件的访问权限),但是只有内核的进程能够修改它们
虽然不能修改,但是我们能够通过vim去查看目录文件的内容

1.3. 符号链接(l,symbolic link)

这种类型的文件类似Windows中的快捷方式,是指向另一个文件的间接指针,也就是我们常说的软链接

1.4. 块设备文件(b,block)和字符设备文件(c,char)

这些文件一般隐藏在/dev目录下,在进行设备读取和外设交互时会被使用到
比如磁盘光驱就是块设备文件,串口设备则属于字符设备文件
系统中的所有设备要么是块设备文件,要么是字符设备文件,无一例外

1.5. FIFO(p,pipe)

管道文件主要用于进程间通讯。比如使用mkfifo命令可以创建一个FIFO文件,启用一个进程A从FIFO文件里读数据,启动进程B往FIFO里写数据,先进先出,随写随读。

1.6. 套接字(s,socket)

用于进程间的网络通信,也可以用于本机之间的非网络通信
这些文件一般隐藏在/var/run目录下,证明着相关进程的存在

Linux 的文件是没有所谓的扩展名的,一个 Linux文件能不能被执行与它是否可执行的属性有关,只要你的权限中有 x ,比如[ -rwx-r-xr-x ] 就代表这个文件可以被执行,与文件名没有关系。跟在 Windows下能被执行的文件扩展名通常是 .com .exe .bat 等不同。
不过,可以被执行跟可以执行成功不一样。比如在 root 主目彔下的 install.log 是一个文本文件,修改权限成为 -rwxrwxrwx 后这个文件能够真的执行成功吗? 当然不行,因为它的内容根本就没有可以执行的数据。所以说,这个 x 代表这个文件具有可执行的能力, 但是能不能执行成功,当然就得要看该文件的内容了。
虽然如此,不过我们仍然希望能从扩展名来了解该文件是什么东西,所以一般我们还是会以适当的扩展名来表示该文件是什么种类的。
所以Linux 系统上的文件名真的只是让你了解该文件可能的用途而已, 真正的执行与否仍然需要权限的规范才行。比如常见的/bin/ls 这个显示文件属性的指令要是权限被修改为无法执行,那么ls 就变成不能执行了。这种问题最常发生在文件传送的过程中。例如你在网络上下载一个可执行文件,但是偏偏在你的 Linux 系统中就是无法执行,那就可能是档案的属性被改变了。而且从网络上传送到你 的 Linux 系统中,文件的属性权限确实是会被改变的

2. Linux目录树

对Linux系统和用户来说,所有可操作的计算机资源都存在于目录树这个逻辑结构中,对计算机资源的访问都可以认为是目录树的访问。就硬盘来说,所有对硬盘的访问都变成了对目录树中某个节点也就是文件夹的访问,访问时不需要知道它是硬盘还是硬盘中的文件夹。
目录树的逻辑结构也非常简单,就是从根目录(/)开始,不断向下展开各级子目录。

3.硬盘分区

硬盘分区是硬盘结合到文件体系的第一步,本质是「硬盘」这个物理概念转换成「区」这个逻辑概念,为下一步格式化做准备。
所以分本身并不是必须的,你完全可以把一整块硬盘作为一个区。但从数据的安全性以及系统性能角度来看,分区还是有很多用处的,所以一般都会对硬盘进行分区。

讲分区就不得不先提每块硬盘上最重要的第一扇区,这个扇区中有硬盘主引导记录(Master boot record, MBR) 及分区表(partition table), 其中 MBR 占有 446 bytes,而分区表占有 64 bytes。硬盘主引导记录放有最基本的引导加载程序,是系统开机启动的关键环节,在附录中有更详细的说明。而分区表则跟分区有关,它记录了硬盘分区的相关信息,但因分区表仅有 64bytes , 所以最多只能记彔四块分区(分区本身其实就是对分区表进行设置)。

只能分四个区实在太少了,于是就有了扩展分区的概念,既然第一个扇区所在的分区表只能记录四条数据, 那我可否利用额外的扇区来记录更多的分区信息。
把普通可以访问的分区称为主分区,扩展分区不同于主分区,它本身并没有内容,它是为进一步逻辑分区提供空间的。在某块分区指定为扩展分区后,就可以对这块扩展分区进一步分成多个逻辑分区。操作系统规定:

  1. 四块分区每块都可以是主分区或扩展分区
  2. 扩展分区最多只能有一个(也没必要有多个)
  3. 扩展分区可以进一步分割为多个逻辑分区
  4. 扩展分区只是逻辑概念,本身不能被访问,也就是不能被格式化后作为数据访问的分区,能够作为数据访问的分区只有主分区和逻辑分区
  5. 逻辑分区的数量依操作系统而不同,在 Linux 系统中,IDE 硬盘最多有 59 个逻辑分区(5 号到 63 号), SATA 硬盘则有 11 个逻辑分区(5 号到 15 号)
    一般给硬盘进行分区时,一个主分区一个扩展分区,然后把扩展分区划分为N个逻辑分区是最好的

是否可以不要主分区呢?不知道,但好像不用管,你创建分区的时候会自动给你配置类型
特殊的,你最好单独分一个swap区(内存置换空间),它独为一类,功能是:当有数据被存放在物理内存里面,但是这些数据又不是常被 CPU 所取用时,那么这些不常被使用的程序将会被丢到硬盘的 swap 置换空间当中, 而将速度较快的物理内存空间释放出来给真正需要的程序使用

 

4.格式化

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

标准的Linux文件系统Ext2是使用「基于inode的文件系统」

我们知道一般操作系统的文件数据除了文件实际内容外, 还带有很多属性,例如 Linux 操作系统的文件权限(rwx)与文件属性(拥有者、群组、 时间参数等),文件系统通常会将属性和实际内容这两部分数据分别存放在不同的区块
在基于inode的文件系统中,权限与属性放置到 inode 中,实际数据放到 data block 区块中,而且inode和data block都有编号
Ext2 文件系统在此基础上

文件系统最前面有一个启动扇区(boot sector)
这个启动扇区可以安装开机管理程序, 这个设计让我们能将不同的引导装载程序安装到个别的文件系统前端,而不用覆盖整个硬盘唯一的MBR, 也就是这样才能实现多重引导的功能
把每个区进一步分为多个块组 (block group),每个块组有独立的inode/block体系
如果文件系统高达数百 GB 时,把所有的 inode 和block 通通放在一起会因为 inode 和 block的数量太庞大,不容易管理
这其实很好理解,因为分区是用户的分区,实际计算机管理时还有个最适合的大小,于是计算机会进一步的在分区中分块
(但这样岂不是可能出现大文件放不了的问题?有什么机制善后吗?)
每个块组实际还会分为分为6个部分,除了inode table 和 data block外还有4个附属模块,起到优化和完善系统性能的作用
所以整个分区大概会这样划分:

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 为1
block 与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 号码

5.挂载

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

由于整个 Linux 系统最重要的是根目录,因此根目录一定需要挂载到某个分区。 而其他的目录则可依用户自己的需求来给予挂载到不同的分去。

到这里Linux的文件体系的构建过程其实已经大体讲完了,总结一下就是:硬盘经过分区和格式化,每个区都成为了一个文件系统,挂载这个文件系统后就可以让Linux操作系统通过VFS访问硬盘时跟访问一个普通文件夹一样。这里通过一个在目录树中读取文件的实际例子来细讲一下目录文件和普通文件


 

6.目录树的读取过程

首先我们要知道

  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的设计。

创建文件过程

当在ext2下建立一个一般文件时, ext2 会分配一个inode 与相对于该文件大小的block 数量给该文件

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

创建目录过程

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

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

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

  1. 因为文件名是记录在目录的block当中,因此当我们要读取某个文件时,就一定会经过目录的inode与block ,然后才能够找到那个待读取文件的inode号码,最终才会读到正确的文件的block内的资料。
  2. 由于目录树是由根目录开始,因此操作系统先通过挂载信息找到挂载点的inode号,由此得到根目录的inode内容,并依据该inode读取根目录的block信息,再一层一层的往下读到正确的文件。
    举例来说,如果我想要读取/etc/passwd 这个文件时,系统是如何读取的呢?
    先看一下这个文件以及有关路径文件夹的信息:
1$ ll -di / /etc /etc/passwd
2     128 dr-xr-x r-x . 17 root root 4096 May 4 17:56 /
333595521 drwxr-x r-x . 131 root root 8192 Jun 17 00:20 /etc
436628004 -rw-r-- r-- . 1 root root 2092 Jun 17 00:20 /etc/passwd
复制代码

于是该文件的读取流程为:

  1. /的inode:
    通过挂载点的信息找到inode号码为128的根目录inode,且inode规定的权限让我们可以读取该block的内容(有r与x)
  2. /的block:
    经过上个步骤取得block的号码,并找到该内容有etc/目录的inode号码(33595521)
  3. etc/的inode:
    读取33595521号inode得知具有r与x的权限,因此可以读取etc/的block内容
  4. etc/的block:
    经过上个步骤取得block号码,并找到该内容有passwd文件的inode号码(36628004)
  5. passwd的inode:
    读取36628004号inode得知具有r的权限,因此可以读取passwd的block内容
  6. passwd的block:
    最后将该block内容的资料读出来

本文摘录自:http://www.cnblogs.com/bellkosmos/p/detail_of_linux_file_system.html。
 

 

 

 

Linux磁盘管理之元数据、文件和目录、链接文件03

一、存储设备分区简述

文件系统最终目的是把大量数据有组织的放入持久性的存储设备,如硬盘。硬盘存储能力具有持久性,不会因为断电而消失,存储量大,但读取速度慢。操作系统读取硬盘的时候,不会一个一个扇区读取,效率太低,而是一次性连续读取多个扇区,即一次性读取一个“块”。这种由扇区组成的块,是文件存取的最小单位,块最常见的是4KB,即连续的8个sector组成一个block。文件数据都存储在块中,那么显然必须找到一个地方存储文件的元信息,比如文件的创建者、创建日期、文件大小等等。这种存储文件元信息的区域就叫做inode,中文译名为“索引节点”。

硬盘最开始的区域是MBR,用于Linux开机启动。剩余空间可分成若干分区。每个分区有一个相关的分区表,记录分区的相关信息,这个分区表是存储在分区之外的,分区表说明了对应分区的起始位置和分区的大小。

分区01
一个典型的Linux分区包含有下面各个部分:
分区02

1、分区的第一个部分是启动分区(Boot Block),它主要是为计算机开机服务的。Linux开机启动后,会首先载入MBR,随后MBR从某个硬盘的启动区加载程序。该程序负责进一步的操作系统的加载和启动。为了方便管理,即使某个分区中没有安装操作系统,Linux也会在该分区预留启动区。
2、启动区之后的是超级区,它存储有文件系统的相关信息,包括文件系统的类型,inode的数目,数据块的数目;貌似一个inode位图和block位图,用一些空间的二进制来表示inode和block的使用情况,1表示使用过,0表示未使用,快速定位,具体怎么存储不知道。
3、随后是多个inodes,它们是实现文件存储的关键。在Linux系统中,一个文件可以分成几个数据块存储,每个文件对应一个inode,这个inode中包含多个指针,指向属于该文件的各个数据块。当操作系统需要读取文件时,就根据inode指向的数据块进行读取。
4、最后一部分,就是真正存储数据的数据块了。

二、iNode节点

在Linux文件管理中,文件除了自身数据之外,还有附属信息,即文件的元数据;这个元数据用于记录文件的许多信息,比如文件大小,属主,属组,修改日期等等。元数据并不包含在文件数据中,由系统维护,也就是包含在inode中。每个inode有一个唯一的整数编号表示(inode number)。

三、inode的内容

inode包含文件的元信息,具体来说具有以下内容:
**文件的字节数
**文件拥有者的UID
**文件的组ID
**文件的读、写、执行权限
**文件的时间戳,共有三个:ctime指inode创建时间,mtime文件内容上一次修改时间,atime指文件最后一次访问的时间。
**链接数,即有多少个文件名指向这个inode
**文件数据块block的位置
可以使用stat命令,查看某个文件的inode信息:stat example.txt
总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

四、inode大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,系统自动将硬盘分成2个区域;一个是数据区,存放文件数据,另一个是inode区,存放inode所包含的信息。每个inode节点大小一般是128字节。inode节点的总数,在格式化时就给定,一般是每1KB或2KB就设置一个inode,此数值不确实是不是正确。
查看每个硬盘分区的inode总数和已使用的数量,可以使用df命令:df –i
查看每个inode节点的大小,可以用如下命令:dumpe2fs –h /dev/hda | grep “Inode size”
由于每个文件都必须有inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况,这时就无法在硬盘上创建新文件。

五、inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或绰号。表面上用户通过文件名打开文件,实际上系统内部过程分三步:首先系统找到这个文件名对应的inode号码;其次通过inode号获取inode信息;最后根据inode信息找到文件数据所在的block,读出数据。
使用ls -i命令,可以看到文件名对应的inode号:
ls –i example.txt

六、目录文件

Linux系统中,目录也是一种文件。打开目录,实际上就是打开目录文件。目录文件的结构非常简单,就是一系列目录项的列表。每个目录项由2部分组成:所包含文件的文件名和文件对应的inode号码。
ls命令只能列出目录文件中的所有文件名:ls /etc/
-i可以列出整个目录文件,即文件名和inode号码:ls –i /etc
如果要查看文件的详细信息,就必须根据inode号码,访问inode节点,读取信息。

举例:文件系统查找文件/var/log/message的过程
首先确定/目录文件的数据块,事实上/目录是由系统决定在哪里存储的,我们不用关心太多;读取/目录文件,找到var的文件名,找到对应的inode号,根据var的inode号查找到inode节点,从inode节点中读取var的block,得知var是个目录文件;打开var目录文件,查找到名称为log的文件名,找出log对应的inode号,查询inode对应的inode节点,通过inode节点找到log的block,读取log的block,发现log是个目录文件;打开log目录文件,查找到名称message文件名,找出message对应的inode号,查询inode对应的inode节点,通过inode节点找到message的block,即找到了message文件;由此可以看到操作系统经过了哪些曲折的步骤。

七、硬链接

一般情况下,文件名和inode号码是"一一对应"关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为"硬链接"(hard link)。

ln命令可以创建硬链接:ln 源文件 目标文件

运行上面这条命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做"链接数",记录指向该inode的文件名总数,这时就会增加1。反过来,删除一个文件名,就会使得inode节点中的"链接数"减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。删除一个文件本质上就是去删除这个文件的硬链接。

顺便说一下目录文件的链接数。目录文件是不可以通过ln命令来创建硬链接的,但是目录文件本身却有硬链接,且至少2个硬链接数。创建目录时,默认会生成两个目录项:"."和".."。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录),这里的2是原目录对其的“硬链接”和当前目录下的".硬链接“。

八、软连接

除了硬链接以外,还有一种特殊情况。文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的"软链接"(soft link)或者"符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:"No such file or directory"。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode"链接数"不会因此发生变化。创建软连接时会增加一个inode节点,而硬链接不会。

其它区别:
软连接可以连接目录,而硬链接不可以链接目录。
软连接可以跨分区进行链接,而硬链接不可以跨分区,更不可以跨磁盘作链接,这是由于硬链接的inode号是针对分区来说的,inode在分区里才有实际意义。

九、inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象:
1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用:find . -inum 69905165 -exec rm -i {} \;
2. 移动文件或重命名文件,只是改变文件名,不影响inode号码。
3. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。
      第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。

十、实际问题

在一台配置较低的Linux服务器(内存、硬盘比较小)的/data分区内创建文件时,系统提示磁盘空间不足,用df -h命令查看了一下磁盘使用情况,发现/data分区只使用了66%,还有12G的剩余空间,按理说不会出现这种问题。 后来用df -i查看了一下/data分区的索引节点(inode),发现已经用满(IUsed=100%),导致系统无法创建新目录和文件。

查找原因:
/data/cache目录中存在数量非常多的小字节缓存文件,占用的Block不多,但是占用了大量的inode。

解决方案:
1、删除/data/cache目录中的部分文件,释放出/data分区的一部分inode。
2、用软连接将空闲分区/opt中的newcache目录连接到/data/cache,使用/opt分区的inode来缓解/data分区inode不足的问题:ln -s /opt/newcache /data/cache

十一、文件共享

在Linux的进程中,当我们打开一个文件时,返回的是一个文件描述符。这个文件描述符是一个数组的下标,对应数组元素为一个指针。有趣的是,这个指针并没有直接指向文件的inode,而是指向了一个文件表格,再通过该表格,指向加载到内存中的目标文件的inode。如下图,一个进程打开了两个文件。

 

可以看到,每个文件表格中记录了文件打开的状态(status flags),比如只读,写入等,还记录了每个文件的当前读写位置(offset)。当有两个进程打开同一个文件时,可以有两个文件表格,每个文件表格对应的打开状态和当前位置不同,从而支持一些文件共享的操作,比如同时读取。要注意的是进程fork之后的情况,子进程将只复制文件描述符的数组,而和父进程共享内核维护的文件表格和inode。此时要特别小心程序的编写

 

 

 

Linux 磁盘与文件系统管理

 

磁盘分区 ( Partition ):

在了解了硬盘的物理组件之后,再接着下来介绍的就是硬盘的分割( Partition )啰! 为什么要进行硬盘分割啊?!因为我们必须要告诉操作系统:『 我这块硬盘可以存取的区域是由 A 磁柱到 B 磁柱』,如此一来, 操作系统才能够控制硬盘磁头去 A-B 范围内的磁柱存取数据;如果没有告诉操作系统这个信息, 那么操作系统就无法利用我们的硬盘来进行数据的存取了, 因为操作系统将无法知道他要去哪里读取数据啊!这就是磁盘分区( Partition )的重点了: 也就是记录每一个分割区( Partition )的起始与结束磁柱!

好了,那么这个分割区的起始与结束磁柱的数据放在哪里呢?!那就是我们在 Linux 安装与多重引导技巧 那个章节提到的 主要启动扇区( Master Boot Recorder, MBR )啰!事实上, MBR 就是在一块硬盘的第零轨上面,这也是计算机开机之后要去利用该硬盘时, 必须要读取的第一个区域!在这个区域内记录的就是硬盘里面的所有分割信息, 以及开机的时候可以进行开机管理程序的写入的处所啊!所以,当一个硬盘的 MBR 坏掉时,由于分割的数据不见了,呵呵,那么这个硬盘也就几乎可以说是寿终正寝了, 因为操作系统不知道该去哪个磁柱上读取数据啊~~

那么 MBR 有什么限制呢?他最大的限制来自于他的大小不够大到储存所有分割与开机管理程序的信息, 因此,MBR 仅提供最多四个 partition 的记忆,这就是所谓的 Primary (P)与 Extended (E) 的 partition 最多只能有四个的原因了。所以说,如果你预计分割超过 4 个 partition 的话,那么势必需要使用 3P + 1E ,并且将所有的剩余空间都拨给 Extended 才行( 记得呦! Extended 最多只能有一个 ),否则只要 3P + E 之后还有剩下的空间, 那么那些容量将成为废物而浪费了,所以结论就是『 如果您要分割硬盘时,并且已经预计规划使用掉 MBR 所提供的 4 个 partition ( 3P + E 或 4P )那么磁盘的全部容量需要使用光,否则剩下的容量也不能再被使用』。 不过,如果您仅是分割出 1P + 1E 的话,那么剩下的空间就还能再分割两个 primary partition !

 

MBR:

 

文件系统:

在告知系统我的 partition 所在的起始与结束磁柱之后,再来则是需要将 partition 格式化为『我的操作系统认识的文件系统( Filesystem )』啰!因为每个操作系统认识的 filesystem 并不相同!例如 Windows 操作系统在默认状态下就无法认识 Linux 的文件系统 ( 这里指 Linux 的标准文件系统 ext2 )。所以当然要针对我们的操作系统来格式化 partition 啰!

我们可以说,每一个 partition 就是一个 Filesystem ,那么一个 partition 是否可以具有两个 Filesystem 呢?!理论上应该是不行的!因为每个文件系统都有其独特的支持方式,例如 Linux 的 ext3 就无法被 Windows 系统所读取!而你将一个 partition 格式化的时候,总不能格式化为 ext3 也同时格式化为 fat32 吧?!那是不可能的啊!

不论是哪一种 filesystem ,数据总是需要储存的吧!既然硬盘是用来储存数据的,想当然尔, 数据就必须写入硬盘啦!刚刚我们提到硬盘的最小储存单位是 sector ,不过数据所储存的最小单位并不是 sector 喔,因为用 sector 来储存太没有效率了。怎么说呢?因为一个 sector 只有 512 Bytes ,而磁头是一个一个 sector 的读取,也就是说,如果我的档案有 10 MBytes ,那么为了读这个档案, 我的磁头必须要进行读取 (I/O) 20480 次!

为了克服这个效率上的困扰,所以就有逻辑区块( Block )的产生了! 逻辑区块是在 partition 进行 filesystem 的格式化时, 所指定的『最小储存单位』,这个最小储存单位当然是架构在 sector 的大小上面( 因为 sector 为硬盘的最小物理储存单位啊! ),所以啦, Block 的大小为 sector 的 2 的次方倍数。此时,磁头一次可以读取一个 block ,如果假设我们在格式化的时候,指定 Block 为 4 KBytes ( 亦即由连续的八个 sector 所构成一个 block ),那么同样一个 10 MBytes 的档案, 磁头要读取的次数则大幅降为 2560 次,这个时候可就大大的增加档案的读取效能啦!

不过,Block 单位的规划并不是越大越好喔!怎么说呢?因为一个 Block 最多仅能容纳一个档案 (这里指 Linux 的 ext2 文件系统)!这有什么问题呢?举例来说好了,假如您的 Block 规划为 4 KBytes ,而您有一个档案大小为 0.1 KBytes ,这个小档案将占用掉一个 Block 的空间,也就是说,该 Block 虽然可以容纳 4 Kbytes 的容量,然而由于档案只占用了 0.1 Kbytes ,所以,实际上剩下的 3.9 KBytes 是不能再被使用了,所以,在考虑 Block 的规划时,需要同时考虑到:

  • 档案读取的效能
  • 档案大小可能造成的硬盘空间浪费

因此,在规划您的磁盘时,需要留意到您主机的用途来进行规划较佳!例如 BBS 主机由于文章较短, 也就是说档案较小,那么 Block 小一点的好;而如果您的主机主要用在储存大容量的档案, 那么考虑到效能,当然 Block 理论上,规划的大一点会比较妥当啦!

Superblock:如同前面说的,当我们在进行磁盘分区( partition )时,每个磁盘分区槽( partition )就是一个文件系统( filesystem ), 而每个文件系统开始的位置的那个 block 就称为 superblock ,superblock 的作用是储存像是文件系统的大小、空的和填满的区块,以及他各自的总数和其他诸如此类的信息等等, 这也就是说,当您要使用这一个磁盘分区槽( 或者说是文件系统 )来进行数据存取的时候,第一个要经过的就是 superblock 这个区块了,所以啰, superblock 坏了,您的这个磁盘槽大概也就回天乏术了!

 

EXT2/EXT3 档案的存取与日志式文件系统的功能

综合上面谈的种种,我们可以知道,当一个 ext2 的 filesystem 被建立时, 他拥有 superblock / group description / block bitmap / inode bitmap / inode table / data blocks 等等区域。要注意的是,每个 ext2 filesystem 在被建立的时候,会依据 partition 的大小, 给予数个 block group ,而每个 block group 就有上述的这些部分。整个 filesystem 的架构可以下图展现:


图四、整个 filesystem 的展现示意图

 


我们将整个 filesystem 简单化, 假设仅有一个 block group ,那么上面的各个部分分别代表什么呢?

  • SuperBlock:如前所述, Superblock 是记录整个 filesystem 相关信息的地方, 没有 Superblock ,就没有这个 filesystem 了。他记录的信息主要有:
    • block 与 inode 的总量;
    • 未使用与已使用的 inode / block 数量;
    • 一个 block 与一个 inode 的大小;
    • filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
    • 一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。
  • Group Description:纪录此 block 由由何处开始记录;
  • Block bitmap:此处记录那个 block 有没有被使用;
  • Inode bitmap:此处记录那个 inode 有没有被使用;
  • Inode table:为每个 inode 数据存放区;
  • Data Blocks:为每个 block 数据存放区。

如果想要知道某个 ext2/ext3 的文件系统内,关于上述提到的相关信息时,可以使用 dumpe2fs 这个指令来读取,举例来说,鸟哥将我自己的主机 /dev/hda1 读出 ext3 的讯息:

[root@linux ~]# dumpe2fs /dev/hda1
Filesystem volume name:   /
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              1537088
Block count:              1536207
Free blocks:              735609
Free inodes:              1393089
First block:              0
Block size:               4096
Filesystem created:       Sat Jun 25 16:21:13 2005
Last mount time:          Sat Jul 16 23:45:04 2005
Last write time:          Sat Jul 16 23:45:04 2005
Last checked:             Sat Jun 25 16:21:13 2005
First inode:              11
Inode size:               128
Journal inode:            8

Group 0: (Blocks 0-32767)
  Primary superblock at 0, Group descriptors at 1-1
  Reserved GDT blocks at 2-376
  Block bitmap at 377 (+377), Inode bitmap at 378 (+378)
  Inode table at 379-1400 (+379)
  0 free blocks, 32424 free inodes, 11 directories
  Free blocks:
  Free inodes: 281-32704
Group 1: (Blocks 32768-65535)
  Backup superblock at 32768, Group descriptors at 32769-32769
  Reserved GDT blocks at 32770-33144
  Block bitmap at 33145 (+377), Inode bitmap at 33146 (+378)
  Inode table at 33147-34168 (+379)
  18 free blocks, 24394 free inodes, 349 directories
  Free blocks: 37882-37886, 38263-38275
  Free inodes: 38084-38147, 39283-39343, 41135, 41141-65408
# 因为数据很多,所以鸟哥略去了一些信息了~上面是比较精简的显示内容。
# 在 Group 0 之前的都是 Superblock 的内容,记录了 inode/block 的总数,
# 还有其他相关的讯息。至于由 Group 0 之后,则是说明各个 bitmap 及 inode table 
# 与 block area 等等。

透过这些记录,我们可以很轻易的就知道哪些 inode 没有被使用,哪些 block 还可以记录, 如此一来,在新增、建立档案与目录时,系统就会根据这些记录来将数据分别写入尚未被使用的 inode 与 block area 了! 不过,要注意的是,当我们新增一个档案(目录)时:

  1. 根据 inode bitmap / block bitmap 的信息,找到尚未被使用的 inode 与 block , 进而将档案的属性与数据分别记载进 inode 与 block ;
  2. 将刚刚被利用的 inode 与 block 的号码 (number) 告知 superblock、inode bitmap、block bitmap 等,让这些 metadata 更新信息。

一般来说,我们将 inode table 与 block area 称为数据存放区域,至于其他的例如 superblock、 block bitmap 与 inode bitmap 等记录就被称为 metadata 啰。经由上面两个动作,我们知道一笔数据写入硬盘时, 会有这两个动作。
 

  • 数据的不一致 (Inconsistent) 状态

那么万一您的档案在写入硬盘时,因为不知名原因导致系统中断(例如突然的停电啊、 系统核心发生错误啊~等等的怪事发生时),所以数据就只有纪录到动作一,而动作二尚未进行~ 这就会产生 metadata 与数据存放区产生不一致 (Inconsistent) 的情况发生了。

在早期的 EXT2 文件系统中,如果发生这个问题,那么系统在重新启动的时候, 就会藉由 Superblock 当中记录的 valid bit 与 filesystem state 等状态来判断是否强制进行数据一致性的检查!检查则以 e2fsck 这支程序来进行的。 不过,这样的检查真的是很费时~因为要针对 metadata 区域与实际数据存放区来进行比对, 呵呵~得要搜寻整个 partition 呢~哇!系统真忙碌~而且在对 Internet 提供服务的服务器主机上面, 这样的检查真的会造成主机复原时间的拉长~真是麻烦~这也就造成后来所谓日志式文件系统的兴起了。
 

稍微了解了所谓数据不一致的状态后,再来要了解的,就是,那么为何要有日志式文件系统的产生呢? 我们已经在 Linux 文件属性与目录配置 当中提到过一些文件系统的注意事项, 也提过日志式 (Journal) 文件系统的相关功能,这里我们再稍微深入的讨论一下。

刚刚提到了,在 EXT2 文件系统当中,要进行档案的写入时,会将数据分别在数据存放区与 metadata 区记录下来, 若当这两个动作无法一次完成时,就会造成所谓的不一致现象。若发生不一致现象, 因为系统不知道是那个档案发生不一致现象,所以就会将整个 filesystem 做一致性的检查,如此一来,很费时啊! 想一想,如果在我们的 filesystem 当中,要是能够规划出一个区块,专门来记录写入或修订档案时的步骤, 那不就可以简化一致性检查的步骤了?也就是说:

  1. 当系统要写入一个档案的时候,会先在日志记录区块中纪录:某个档案准备要写入磁盘了;
  2. 开始写入档案的权限与数据;
  3. 开始更新 metadata 的数据;
  4. 完成数据与 metadata 的更新后,在日志记录区块当中完成该档案的纪录。

在这样的程序当中,万一数据的纪录过程当中发生了问题,那么我们的系统只要去检查日志记录区块, 就可以知道那个档案发生了问题,针对该问题来做一致性的检查即可,而不必针对整块 filesystem 去检查, 真的就可以达到快速修复 filesystem 的能力了!这就是日志式档案最基础的功能啰~ 那么我们的 ext2 可达到这样的功能吗?当然可以啊! 就透过 ext3 即可! ext3 是 ext2 的升级版本,并且可向下兼容 ext2 版本呢! 所以啰,目前我们才建议大家,可以直接使用 ext3 这个 filesystem 啊! ^_^

如果您对于 EXT2 / EXT3 系统还有更多的兴趣,可以参考底下这几篇文章:

或者参考鸟哥由网络上面找到的相关中文翻译,不过.....原发文者的文章出处已经找不到了~ 真是不好意思~请参考:附录 B:EXT2/EXT3 文件系统

日志文件系统:

Linux 文件系统的运作:

好了,我们知道整个 ext2/ext3 的数据存取是透过 journal 与 metadata 还有数据存放区在纪录的。 不过,实际上, Linux 文件系统在运作的时候,真的要将数据直接存放到硬盘上面吗?! 有没有更有效率的作法?

我们来看看整部计算机的运作当中,那个数据的访问速度最慢呢?数据处理最快速的地方应该是 CPU 了, 接下来则是主存储器 (RAM) ,至于硬盘,哈哈!没错,速度可是比 CPU 还有 RAM 要慢的很多很多。 为了让 Linux 加快整个系统的存取效率,因此在 Linux 上面通常采取异步处理( asynchronously )的方式。

什么是异步呢?举例来说:『当系统读取了某一个档案, 则该档案所在的区块数据会被加载到内存当中,所以该磁盘区块就会被放置在主存储器的缓冲快取区中, 若这些区块的数据被改变时,刚开始数据仅有主存储器的区块数据会被改变, 而且在缓冲区当中的区块数据会被标记为『 Dirty 』,这个时候磁盘实体区块尚未被修正! 所以亦即表示,这些『 Dirty 』区块的数据必需回写到磁盘当中, 以维持磁盘实体区块上的数据与主存储器中的区块数据的一致性。』

为什么要这么做呢?这是因为主存储器的运作速度比起硬盘来实在是快太多了, 万一系统当中有一个档案相当的大,而又持续性的存取,那么由于较慢的硬盘访问速度,将使得整个 Linux 速度被拖垮,所以才会使用异步方式的数据处理啊!不过, 也由于硬盘与主存储器的数据可能没有同步化,因此,如果 Linux 不正常关机( 例如跳电或者是当机 )时,则由于数据尚未回写入磁盘当中,会导致系统在再次开机时,会花相当多的时间进行磁盘检验, 同时也有可能造成磁盘的损毁啊!

 

dirty 块

 

其他 Linux 支持的文件系统

虽然 Linux 的标准文件系统是 ext2 ,且还有增加了日志功能的 ext3 之外,事实上,Linux 还有支持很多文件格式的, 尤其是最近这几年推出了好几种速度很快的日志式文件系统,包括 SGI 的 XFS 文件系统, 可以适用更小型档案的 Reiserfs 文件系统,以及 Windows 的 FAT 文件系统等等, 都能够被 Linux 所支持喔!常见的支持文件系统有:

  • 传统文件系统:ext2 / minix / MS-DOS / FAT (用 vfat 模块) / iso9660 (光盘)等等;
  • 日志式文件系统: ext3 / ReiserFS / Windows' NTFS / IBM's JFS / SGI's XFS
  • 网络文件系统: NFS / SMBFS

想要知道您的 Linux 支持的文件系统有哪些,可以察看底下这个目录:

[root@linux ~]# ls -l /lib/modules/`uname -r`/kernel/fs

系统目前已启用的文件系统则有:

[root@linux ~]# cat /proc/filesystems

假设您的 / 使用的是 /dev/hda1 ,用 ext3 ,而 /home 使用 /dev/hda2 ,用 reiserfs , 那么您取用 /home/dmtsai/.bashrc 时,有特别指定要用的什么文件系统的模块来读取吗?! 应该是没有吧!嘿嘿!这个就是我们 Linux kernel 的 Virtual Filesystem Switch (VFS) 的功能啦!透过这个 VFS 的功能来管理所有的 filesystem, 省去我们需要自行设定读取文件系统的定义啊~方便很多!

 

 

 

df

du  : du -sm ./

 

 

开机挂载 /etc/fstab 及 /etc/mtab

 

特殊装置 loop 挂载

除了常见的软、硬盘挂载之外,我们还可以挂载特殊装置喔!举例来说,利用我们的硬盘内的档案仿真出来的装置! 也就是说,当我的硬盘内有一个 2GB 的档案时,我可以将这个档案『模拟』成为一个独立的装置, 然后用这个装置来挂载使用喔!当然啦,这个 2GB 的大档案要能够被挂载时, 他必须是一个『被格式化过的档案』才行!底下我们就来玩一玩这个咚咚。

建立大型档案
首先,我们得先有一个大的档案吧!怎么建立这个大档案呢?在 Linux 底下我们有一支很好用的程序 dd 可以用来建立空的档案喔!详细的说明请先翻到后面一章 压缩指令的运用 来查阅,这里鸟哥仅作一个简单的范例而已。 假设我要建立一个空的档案在 /tmp/loopdev ,那可以这样做:

[root@linux ~]# dd if=/dev/zero of=/tmp/loopdev bs=1024k count=2048
2048+0 records in
2048+0 records out
# 这个指令在下一小节也会谈到,那个 if 是 input file,
# of 是 output file ,至于 bs 是每个 block 大小,
# count 则是总共几个 bs 的意思。不过,测试时,注意 /tmp 
# 那个 partition 的大小啊!

格式化
很简单就建立起一个 2GB 的档案了吶!!接下来当然是格式化啰!

[root@linux ~]# mke2fs -j /tmp/loopdev
mke2fs 1.35 (28-Feb-2004)
loopdev is not a block special device.
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
262144 inodes, 524288 blocks
26214 blocks (5.00%) reserved for the super user
....以下省略.....

挂载
那要如何挂载啊?利用 mount 的特殊参数,那个 -o loop 的参数来处理!

[root@linux ~]# mount -t ext3 -o loop /tmp/loopdev /media/cdrom/
[root@linux ~]# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/tmp/loopdev           2064208     35880   1923472   2% /media/cdrom

多了个独立的装置给您使用喔!其实就是那个 2GB 的档案内容啦! 这东西看起来似乎没有什么用途的样子,不过,如果您未来想要玩 Linux 上面的『虚拟主机』的话, 也就是以一部 Linux 主机再切割成为数个独立的主机系统时,类似 VMware 这类的软件, 在 Linux 上使用 xen 这个软件,他就可以配合这种 loop device 的文件类型来进行根目录的挂载, 真的非常有用的喔! ^_^

 

 

虚拟内存之建置

我们前面谈了很多各式各样的 filesystem ,不过,您晓得在安装的时候设定的那一个『 虚拟内存 (swap) 』要如何增加吗?举个简单的例子吧,鸟哥的 Sun 主机上面,由于跑的程序太庞大了,通常 swap 需要开启到 1GB 左右,但是呢,有的时候还是会不够的!在 Linux 当中,如果您需要使用到很大量的虚拟内存,偏偏当初给的 swap 扇区不够大,那要怎么办呢? 有什么方法可以来达成:

  • 设定一个 swap partition ?
  • 建立一个虚拟内存的档案?

怎么说呢?基本上,虚拟内存就是将硬盘规划出一个区间,让内存的数据可以经由硬盘来读取罢了, 那么如果有 swap file 也就够了对不对!是呀!所以这里我们使用两种方法来尝试建立一下 swap 的扩增吧! 另外, swap 的建立其实也很简单啊!同样的需要先建立出 swap 这个装置或者是档案后, 将他格式化成为 swap 的格式,最后将他挂载到系统上即可!那就来实作看看吧!
 


建立虚拟内存装置
第一种正规的方法是『直接再加一棵硬盘,并且将其中某个扇区规划为 swap 的 filesystem 』, 呵呵,说的容易,做起来更容易!实际的动作为:

  1. 以『 fdisk /dev/hd[a-d] 』先建立一个 partition ,还记得 fdisk 怎么做吗?回去复习一下吧!简单的来说,就是先 (1)建立一个 partition,然后 (2)将该 partition 的 ID 改为 82 这一个 swap 的磁盘文件格式代号就对 啦!这样这一步骤就 OK 啰!
     
  2. 以『 mkswap /dev/hd[a-d][1-16] 』的方式来将您刚刚建置出来的 partition 『格式化为 swap 的文件格式』,很简单吧!这样就格式化 OK 啰!
     
  3. 再来则是将 swap 启动,启动的指令为『 swapon /dev/hd[a-d][1-16] 』,这样就能启动了!很简单吧!这样 swap 就自动加入到内存容量里头去了!

那么如何将 swap 关掉呢?呵呵!很简单呀!就是直接给他 swapoff 就对了!
 

例题一:如果您的系统是以鸟哥建议的方式来安装的,那么系统应该有一块剩余的空间。 请将该剩余的空间格式化成为一个 swap device ,并且挂载到系统上!

 


建立虚拟内存档案
那么万一我不想新增加一个扇区呢?可不可以使用 swap file 的方式来新增硬盘呀!当然可以啰! 而且步骤还蛮简单的呢!基本的流程就是:

  1. 以 dd 指令来建立 swapfile ;
  2. 以 mkswap 来将 swapfile 格式化为 swap 的文件格式;
  3. 以 swapon 来启动该档案,使成为 swap ;
  4. 以 swapoff 来关闭该档案!

嗯!多说无益!我们来实际的将您的主机系统上面新增 64MB 的虚拟内存吧!如果可能的话, 请您在您的系统上面实际的操作一次底下的步骤,我想,您应该马上会了解实际的操作流程的! (底下的步骤是可以复原的!!别担心,不过 mkswap 这个指令的下达要小心一点就是了!)

  1. 使用 dd 这个指令来新增一个 64MB 的档案在 /tmp 底下:
    [root@linux ~]# dd if=/dev/zero of=/tmp/swap bs=4k count=16382
    16382+0 records in
    16382+0 records out
    # dd 这个指令是用来转换档案并且 copy 用的;
    #    if 指的是要被转换的输入文件格式 /dev/zero 可以由 man zero 来查看内容;
    #    of 指的是输出的档案,我们将之输出到 /tmp/swap 这个档案;
    #    bs 指的是一个扇区占用几个 kb ;
    #    count 指的是要使用多少个 bs ,所以最后的容量为 bs*count = 4k * 16382 ~ 64MB
    
    如上所述,我们将建立一个档名为 /tmp/swap 的档案,且其内容共有 64MB 左右大小的档案;
     
  2. 使用 mkswap 将 /tmp/swap 这个文件格式化为 swap 的文件格式:
    [root@linux ~]# mkswap /tmp/swap
    Setting up swapspace version 1, size = 67096576 bytes
    # 请注意,这个指令在下达的时候请『特别小心』,因为下错字元控制,
    # 将可能使您的 filesystem 挂掉!
    
  3. 使用 swapon 来将 /tmp/swap 启动啰!
    [root@linux ~]# free
                 total       used       free     shared    buffers     cached
    Mem:         62524      60200       2324          0        716      19492
    -/+ buffers/cache:      39992      22532
    Swap:       127004       2620     124384
    
    [root@linux ~]# swapon /tmp/swap
    # 不要怀疑!这样就已经将虚拟内存增加 64 MB 啰!如果您需要每次都启动该档案,
    那么将 swapon /tmp/swap 写入 /etc/rc.d/rc.local 当中即可!
    
    [root@linux ~]# free
                 total       used       free     shared    buffers     cached
    Mem:         62524      60240       2284          0        724      19492
    -/+ buffers/cache:      40024      22500
    Swap:       192524       2620     189904
    
  4. 使用 swapoff 关掉 swap file
    [root@linux ~]# swapoff /tmp/swap
    

没错,就这么简单的将虚拟内存给他新增啰!赶快来去试试看去! 不过,如果您的 FC4 从来没有经过 update 的话,那么可能会发生一些小困扰, 困扰的原因在此: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=164937 , 因为我们尚未学习如何以 rpm 安装软件,所以这里的练习您可以先略过!


虚拟内存的限制
说实话,虚拟内存在目前的桌面计算机来讲,存在的意义已经不大了!这是因为目前的 x86 主机所含的内存实在都太大了 (一般入门级至少也都有 256MB 了),所以,我们的 Linux 系统大概都用不到虚拟内存 (swap) 这个玩意儿的。不过, 如果是针对服务器或者是工作站这些常年上线的系统来说的话,那么,无论如何,swap 还是需要建立的。

因为 swap 主要的功能是当物理内存不够时,则某些在内存当中所占的程序会暂时被移动到 swap 当中,让物理内存可以被需要的程序来使用。另外,如果您的主机支持电源管理模式, 也就是说,您的 Linux 主机系统可以进入『休眠』模式的话,那么, 运作当中的程序状态泽会被纪录到 swap 去,以作为『唤醒』主机的状态依据!。 另外,有某些程序在运作时,本来就会利用 swap 的特性来存放一些数据段, 所以, swap 来是需要建立的!只是不需要太大!

不过, swap 在被建立时,是有限制的喔!

  • 在核心 2.4.10 版本以后,单一 swap 量已经没有 2GB 的限制了,
  • 但是,最多还是仅能建立到 32 个 swap 的数量!
  • 而且,由于目前 x86_64 (64位) 最大内存寻址到 64GB, 因此, swap 总量最大也是仅能达 64GB 就是了!

 

 

 

Linux虚拟文件系统

 

1. 虚拟文件系统概述

1.1 VFS简介

虚拟文件系统(Virtual File System,简称VFS)是Linux内核的子系统之一,它为用户程序提供文件和文件系统操作的统一接口,屏蔽不同文件系统的差异和操作细节。借助VFS可以直接使用open()read()write()这样的系统调用操作文件,而无须考虑具体的文件系统和实际的存储介质。

举个例子,Linux用户程序可以通过read() 来读取ext3NFSXFS等文件系统的文件,也可以读取存储在SSDHDD等不同存储介质的文件,无须考虑不同文件系统或者不同存储介质的差异。

通过VFS系统,Linux提供了通用的系统调用,可以跨越不同文件系统和介质之间执行,极大简化了用户访问不同文件系统的过程。另一方面,新的文件系统、新类型的存储介质,可以无须编译的情况下,动态加载到Linux中。

"一切皆文件"是Linux的基本哲学之一,不仅是普通的文件,包括目录、字符设备、块设备、套接字等,都可以以文件的方式被对待。实现这一行为的基础,正是Linux的虚拟文件系统机制。

1.2 VFS原理

VFS之所以能够衔接各种各样的文件系统,是因为它抽象了一个通用的文件系统模型,定义了通用文件系统都支持的、概念上的接口。新的文件系统只要支持并实现这些接口,并注册到Linux内核中,即可安装和使用。

举个例子,比如Linux写一个文件:

int ret = write(fd, buf, len);

调用了write()系统调用,它的过程简要如下:

  • 首先,勾起VFS通用系统调用sys_write()处理。
  • 接着,sys_write()根据fd找到所在的文件系统提供的写操作函数,比如op_write()
  • 最后,调用op_write()实际的把数据写入到文件中。

操作示意图如下:

2. 虚拟文件系统组成部分

Linux为了实现这种VFS系统,采用面向对象的设计思路,主要抽象了四种对象类型:

  • 超级块对象:代表一个已安装的文件系统。
  • 索引节点对象:代表具体的文件。
  • 目录项对象:代表一个目录项,是文件路径的一个组成部分。
  • 文件对象:代表进程打开的文件。

每个对象都包含一组操作方法,用于操作相应的文件系统。

备注:Linux将目录当做文件对象来处理,是另一种形式的文件,它里面包含了一个或多个目录项。而目录项是单独抽象的对象,主要包括文件名和索引节点号。因为目录是可以层层嵌套,以形成文件路径,而路径中的每一部分,其实就是目录项。

接下来介绍一下各个对象的作用以及相关操作。

2.1 超级块

超级块用于存储文件系统的元信息,由super_block结构体表示,定义在<linux/fs.h>中,元信息里面包含文件系统的基本属性信息,比如有:

  • 索引节点信息
  • 挂载的标志
  • 操作方法 s_op
  • 安装权限
  • 文件系统类型、大小、区块数
  • 等等等等

其中操作方法 s_op 对每个文件系统来说,是非常重要的,它指向该超级块的操作函数表,包含一系列操作方法的实现,这些方法有:

  • 分配inode
  • 销毁inode
  • 读、写inode
  • 文件同步
  • 等等

当VFS需要对超级块进行操作时,首先要在超级块的操作方法 s_op 中,找到对应的操作方法后再执行。比如文件系统要写自己的超级块:

superblock->s_op->write_supper(sb);

创建文件系统时,其实就是往存储介质的特定位置,写入超级块信息;而卸载文件系统时,由VFS调用释放超级块。

Linux支持众多不同的文件系统,file_system_type结构体用于描述每种文件系统的功能和行为,包括:

  • 名称、类型等
  • 超级块对象链表

当向内核注册新的文件系统时,其实是将file_system_type对象实例化,然后加入到Linux的根文件系统的目录树结构上。

2.2 索引

索引节点对象包含Linux内核在操作文件、目录时,所需要的全部信息,这些信息由inode结构体来描述,定义在<linux/fs.h>中,主要包含:

  • 超级块相关信息
  • 目录相关信息
  • 文件大小、访问时间、权限相关信息
  • 引用计数
  • 等等

一个索引节点inode代表文件系统中的一个文件,只有当文件被访问时,才在内存中创建索引节点。与超级块类似的是,索引节点对象也提供了许多操作接口,供VFS系统使用,这些接口包括:

  • create(): 创建新的索引节点(创建新的文件)
  • link(): 创建硬链接
  • symlink(): 创建符号链接。
  • mkdir(): 创建新的目录。

等等,我们常规的文件操作,都能在索引节点中找到相应的操作接口。

2.3 目录项

前面提到VFS把目录当做文件对待,比如/usr/bin/vimusrbinvim都是文件,不过vim是一个普通文件,usrbin都是目录文件,都是由索引节点对象标识。

由于VFS会经常的执行目录相关的操作,比如切换到某个目录、路径名的查找等等,为了提高这个过程的效率,VFS引入了目录项的概念。一个路径的组成部分,不管是目录还是普通文件,都是一个目录项对象。/usrbinvim都对应一个目录项对象。不过目录项对象没有对应的磁盘数据结构,是VFS在遍历路径的过程中,将它们逐个解析成目录项对象。

目录项由dentry结构体标识,定义在<linux/dcache.h>中,主要包含:

  • 父目录项对象地址
  • 子目录项链表
  • 目录关联的索引节点对象
  • 目录项操作指针
  • 等等

目录项有三种状态:

  • 被使用:该目录项指向一个有效的索引节点,并有一个或多个使用者,不能被丢弃。
  • 未被使用:也对应一个有效的索引节点,但VFS还未使用,被保留在缓存中。如果要回收内存的话,可以撤销未使用的目录项。
  • 负状态:没有对应有效的索引节点,因为索引节点被删除了,或者路径不正确,但是目录项仍被保留了。

将整个文件系统的目录结构解析成目录项,是一件费力的工作,为了节省VFS操作目录项的成本,内核会将目录项缓存起来。

2.4 文件

文件对象是进程打开的文件在内存中的实例。Linux用户程序可以通过open()系统调用来打开一个文件,通过close()系统调用来关闭一个文件。由于多个进程可以同时打开和操作同一个文件,所以同一个文件,在内存中也存在多个对应的文件对象,但对应的索引节点和目录项是唯一的。

文件对象由file结构体表示,定义在<linux/fs.h>中,主要包含:

  • 文件操作方法
  • 文件对象的引用计数
  • 文件指针的偏移
  • 打开文件时的读写标识
  • 等等等等

类似于目录项,文件对象也没有实际的磁盘数据,只有当进程打开文件时,才会在内存中产生一个文件对象。

每个进程都有自己打开的一组文件,由file_struct结构体标识,该结构体由进程描述符中的files字段指向。主要包括:

  • fdt
  • fd_array[NR_OPEN_DEFAULT]
  • 引用计数

fd_array数组指针指向已打开的文件对象,如果打开的文件对象个数 > NR_OPEN_DEFAULT,内核会分配一个新数组,并将 fdt 指向该数组。

除此之外,内核还为所有打开文件维持一张文件表,包括:

  • 文件状态标志
  • 文件偏移量

关于多进程打开同一文件以及文件共享更详细的信息,可以阅读《UNIX环境高级编程》第三章。

3. 总结

Linux支持了很多种类的文件系统,包含本地文件系统ext3ext4到网络文件系统NFSHDFS等,VFS系统屏蔽了不同文件系统的操作差异和实现细节,提供了统一的实现框架,也提供了标准的操作接口,这大大降低了操作文件和接入新文件系统的难度

 

文件 I/O

文件的读写方式各有千秋,对于文件的 I/O 分类也非常多,常见的有

  • 缓冲与非缓冲 I/O
  • 直接与非直接 I/O
  • 阻塞与非阻塞 I/O VS 同步与异步 I/O

接下来,分别对这些分类讨论讨论。

缓冲与非缓冲 I/O

文件操作的标准库是可以实现数据的缓存,那么根据「是否利用标准库缓冲」,可以把文件 I/O 分为缓冲 I/O 和非缓冲 I/O: - 缓冲 I/O,利用的是标准库的缓存实现文件的加速访问,而标准库再通过系统调用访问文件。 - 非缓冲 I/O,直接通过系统调用访问文件,不经过标准库缓存。

这里所说的「缓冲」特指标准库内部实现的缓冲。

比方说,很多程序遇到换行时才真正输出,而换行前的内容,其实就是被标准库暂时缓存了起来,这样做的目的是,减少系统调用的次数,毕竟系统调用是有 CPU 上下文切换的开销的。

直接与非直接 I/O

我们都知道磁盘 I/O 是非常慢的,所以 Linux 内核为了减少磁盘 I/O 次数,在系统调用后,会把用户数据拷贝到内核中缓存起来,这个内核缓存空间也就是「页缓存」,只有当缓存满足某些条件的时候,才发起磁盘 I/O 的请求。

那么,根据是「否利用操作系统的缓存」,可以把文件 I/O 分为直接 I/O 与非直接 I/O

  • 直接 I/O,不会发生内核缓存和用户程序之间数据复制,而是直接经过文件系统访问磁盘。
  • 非直接 I/O,读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户程序拷贝给内核缓存,再由内核决定什么时候写入数据到磁盘。

如果你在使用文件操作类的系统调用函数时,指定了 O_DIRECT 标志,则表示使用直接 I/O。如果没有设置过,默认使用的是非直接 I/O。

如果用了非直接 I/O 进行写数据操作,内核什么情况下才会把缓存数据写入到磁盘?

以下几种场景会触发内核缓存的数据写入磁盘: - 在调用 write 的最后,当发现内核缓存的数据太多的时候,内核会把数据写到磁盘上; - 用户主动调用 sync,内核缓存会刷到磁盘上; - 当内存十分紧张,无法再分配页面时,也会把内核缓存的数据刷到磁盘上; - 内核缓存的数据的缓存时间超过某个时间时,也会把数据刷到磁盘上;

 

 

 

 

参考:

https://www.cnblogs.com/liwei0526vip/p/4998614.html

http://cn.linux.vbird.org/linux_basic/fedora_4/0230filesystem-fc4.php

https://zhuanlan.zhihu.com/p/183238194

https://zhuanlan.zhihu.com/p/69289429

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值