认识 Linux 文件系统
文件系统特性
例如 Linux 操作系统的文件权限(rwx)与文件属性(拥有者、群组、时间参数等)。 文件系统通常会将这两部份的数据分别存放在不同的区块,权限与属性放置到 inode 中,至于实际数据则放置到 data block 区块中。 另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息,包括 inode 与 block 的总量、使用量、剩余量等。
每个 inode 与 block 都有编号,至于这三个数据的意义可以简略说明如下:
superblock:记录此 filesystem 的整体信息,包括inode/block的总量、使用量、剩余量,以及文件系统的格式与相关信息等;
inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block号码;
block:实际记录文件的内容,若文件太大时,会占用多个 block 。
某一个文件的属性与权限数据是放置到 inode 4 号(下图较小方格内),而这个 inode 记录了文件数据的实际放置点为 2, 7, 13, 15 这四个 block 号码
这种数据存取的方法我们称为索引式文件系统(indexed allocation)
文件的数据依序写入1->7->4->15号这四个 block 号码中, 但这个文件系统没
有办法一口气就知道四个 block 的号码,他得要一个一个的将 block 读出后,才会知道下一个block 在何处。 如果同一个文件数据写入的 block 分散的太过厉害时,则我们的磁头将无法在磁盘转一圈就读到所有的数据, 因此磁盘就会多转好几圈才能完整的读取到这个文件的内容!
Linux 的 EXT2 文件系统(inode)
Ext2 文件系统在格式化的时候基本上是区分为多个区块群组 (block group) 的,每个区块群组都有独立的 inode/block/superblock 系统。
- 文件系统最前面有一个开机扇区(boot sector),这个开机扇区可以安装开机管理程序
每一个区块群组(block group)的六个主要内容:
data block (数据区块)
data block 是用来放置文件内容数据地方。
Block 大小1KB 2KB 4KB 最大单一文件限制 16GB 256GB 2TB 最大文件系统总容量 2TB 8TB 16TB Ext2 文件系统的 block 基本限制如下:
- 原则上,block 的大小与数量在格式化完就不能够再改变了(除非重新格式化);
- 每个 block 内最多只能够放置一个文件的数据;
- 承上,如果文件大于 block 的大小,则一个文件会占用多个 block 数量;
- 承上,若文件小于 block ,则该 block 的剩余容量就不能够再被使用了(磁盘空间会浪费)。
inode table (inode 表格)
inode 是记录文件的属性以及该文件实际数据是放置在哪几个 block。
该文件的存取模式(read/write/excute);
该文件的拥有者与群组(owner/group);
该文件的容量;
该文件创建或状态改变的时间(ctime);
最近一次的读取时间(atime);
最近修改的时间(mtime);
定义文件特性的旗标(flag),如 SetUID…;
该文件真正内容的指向 (pointer);
其他特点:
每个 inode 大小均固定为 128 Bytes (新的 ext4 与 xfs 可设置到 256 Bytes);
每个文件都仅会占用一个 inode 而已;
承上,因此文件系统能够创建的文件数量与 inode 的数量有关;
系统读取文件时需要先找到 inode,并分析 inode 所记录的权限与使用者是否符合,若符合才能够开始实际读取 block 的内容。
上图最左边为 inode 本身 (128 Bytes),里面有 12 个直接指向 block 号码的对照,这 12 笔记录就能够直接取得 block 号码啦! 至于所谓的间接就是再拿一个 block 来当作记录 block 号码的记录区,如果文件太大时, 就会使用间接的 block 来记录号码。
Superblock (超级区块)
Superblock 是记录整个 filesystem 相关信息的地方, 没有 Superblock ,就没有这个filesystem 了
记录的信息主要有:
block 与 inode 的总量;
未使用与已使用的 inode / block 数量;
block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128Bytes 或 256Bytes);
filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。
此外,每个 block group 都可能含有 superblock。
一般除第一个block group会有superblock,后续如果有的话,就是给第一个 block group 内 superblock 的备份,以后可以做恢复使用。
Filesystem Description(文件系统描述说明)
这个区段可以描述每个 block group 的开始与结束的 block 号码,以及说明每个区段(superblock, bitmap, inodemap, data block) 分别介于哪一个 block 号码之间。
block bitmap (区块对照表)
记录的是使用与未使用的 block 号码
inode bitmap (inode 对照表)
记录使用与未使用的 inode 号码
dumpe2fs: 查询 Ext 家族 superblock 信息的指令
1 | [root@study ~]$ dumpe2fs [-bh] 设备文件名 |
与目录树的关系
目录
当我们在 Linux 下的文件系统创建一个目录时,文件系统会分配一个 inode 与至少一块 block 给该目录。其中,inode 记录该目录的相关权限与属性,并可记录分配到的那块 block 号码;而 block 则是记录在这个目录下的文件名与该文件名占用的 inode 号码数据。也就是说目录所占用的 block 内容在记录如下的信息:
文件
当我们在 Linux 下的 ext2 创建一个一般文件时, ext2 会分配一个 inode 与相对于该文件大小的 block 数量给该文件。例如:假设我的一个 block 为 4 KBytes ,而我要创建一个 100 KBytes 的文件,那么 linux 将分配一个 inode 与 25 个 block 来储存该文件! 但同时请注意,由于 inode 仅有 12 个直接指向,因此还要多一个 block 来作为区块号码的记录喔!
目录树读取
1 | [root@study ~]$ ll -di / /etc /etc/passwd |
该文件的读取流程为:
/
的 inode: 通过挂载点的信息找到 inode 号码为 128 的根目录 inode,且 inode 规范的权限让我们可以读取该 block 的内容(有 r 与 x) ;/
的 block: 经过上个步骤取得 block 的号码,并找到该内容有etc/
目录的 inode 号码(33595521);etc/
的 inode: 读取 33595521 号 inode 得知 dmtsai 具有 r 与 x 的权限,因此可以读取etc/ 的 block 内容;etc/
的 block: 经过上个步骤取得 block 号码,并找到该内容有 passwd 文件的 inode 号码 (36628004);passwd
的 inode: 读取 36628004 号 inode 得知 dmtsai 具有 r 的权限,因此可以读取 passwd 的 block 内容;passwd
的 block: 最后将该 block 内容的数据读出来。- filesystem 大小与磁盘读取性能:如果文件写入的 block 真的分的很散, 此时就会有所谓的文件数据离散的问题发生了。(文件太大)
EXT2/EXT3/EXT4 文件的存取与日志式文件系统的功能
新增一个文件,此时文件系统的行为是:
- 先确定使用者对于欲新增文件的目录是否具有 w 与 x 的权限,若有的话才能新增;
- 根据 inode bitmap 找到没有使用的 inode 号码,并将新文件的权限/属性写入;
- 根据 block bitmap 找到没有使用中的 block 号码,并将实际的数据写入 block 中,且更新 inode 的 block 指向数据;
- 将刚刚写入的 inode 与 block 数据同步更新 inode bitmap 与 block bitmap,并更新superblock 的内容。
inode table 与 data block 称为数据存放区域。
superblock、block bitmap 与 inode bitmap 等区段就被称为 metadata (中介数据),因为 superblock, inode bitmap 及 block bitmap 的数据是经常变动的,每次新增、移除、编辑时都可能会影响到这三个部分的数据,所以被称为中介数据。
数据的不一致 (Inconsistent) 状态
意外导致文件数据的不完整。
日志式文件系统 (Journaling filesystem)
- 预备:当系统要写入一个文件时,会先在日志记录区块中纪录某个文件准备要写入的信息;
- 实际写入:开始写入文件的权限与数据;开始更新 metadata 的数据;
- 结束:完成数据与 metadata 的更新后,在日志记录区块当中完成该文件的纪录。
Linux 文件系统的运行
- 系统会将常用的文件数据放置到内存的缓冲区,以加速文件系统的读/写;
- 承上,因此 Linux 的实体内存最后都会被用光!这是正常的情况!可加速系统性能;
- 你可以手动使用 sync 来强迫内存中设置为 Dirty 的文件回写到磁盘中;
- 若正常关机时,关机指令会主动调用 sync 来将内存的数据回写入磁盘内;
- 但若不正常关机(如跳电、死机或其他不明原因),由于数据尚未回写到磁盘内, 因此重新开机后可能会花很多时间在进行磁盘检验,甚至可能导致文件系统的损毁(非磁盘损毁)。
挂载点的意义 (mount point)
每个 filesystem 都有独立的 inode / block / superblock 等信息,这个文件系统要能够链接到目录树才能被我们使用。 将文件系统与目录树结合的动作我们称为“挂载”。
其他 Linux 支持的文件系统与 VFS
- 传统文件系统:ext2 / minix / MS-DOS / FAT (用 vfat 模块) / iso9660 (光盘)等等;
- 日志式文件系统: ext3 / ext4 / ReiserFS / Windows’ NTFS / IBM’s JFS / SGI’s XFS / ZFS
- 网络文件系统: NFS / SMBFS
Linux VFS (Virtual Filesystem Switch)
整个 Linux 的系统都是通过一个名为 Virtual Filesystem Switch 的核
心功能去读取 filesystem 的。
XFS 文件系统简介
CentOS 7 开始,默认的文件系统已经由原本的 EXT4 变成了 XFS 文件系统
为什么要从EXT4 -> XFS?
- EXT 家族当前较伤脑筋的地方:支持度最广,但格式化超慢!
查看XFS xfs_info
1 | [root@study ~]$ xfs_info 挂载点 | 设备文件名 |
上面的输出讯息可以这样解释:
- 第 1 行里面的 isize 指的是 inode 的容量,每个有 256Bytes 这么大。至于 agcount 则是前面谈到的储存区群组 (allocation group) 的个数,共有 4 个, agsize 则是指每个储存区群组具有 65536 个 block 。配合第 4 行的 block 设置为 4K,因此整个文件系统的容量应该就是 4655364K 这么大!
- 第 2 行里面 sectsz 指的是逻辑扇区 (sector) 的容量设置为 512Bytes 这么大的意思。
- 第 4 行里面的 bsize 指的是 block 的容量,每个 block 为 4K 的意思,共有 262144 个 block 在这个文件系统内。
- 第 5 行里面的 sunit 与 swidth 与磁盘阵列的 stripe 相关性较高。
- 第 7 行里面的 internal 指的是这个登录区的位置在文件系统内,而不是外部设备的意思。且占用了 4K * 2560 个 block,总共约 10M 的容量。
- 第 9 行里面的 realtime 区域,里面的 extent 容量为 4K。不过目前没有使用。
文件系统的简单操作
磁盘与目录的容量
df
:列出文件系统的整体磁盘使用量;du
:评估文件系统的磁盘使用量(常用在推估目录所占容量)
df
1 | [root@study ~]$ df [-ahikHTm] [目录或文件名] |
- Filesystem:代表该文件系统是在哪个 partition ,所以列出设备名称;
- 1k-blocks:说明下面的数字单位是 1KB 呦!可利用 -h 或 -m 来改变容量;
- Used:顾名思义,就是使用掉的磁盘空间啦!
- Available:也就是剩下的磁盘空间大小;
- Use%:就是磁盘的使用率啦!如果使用率高达 90% 以上时, 最好需要注意一下了,免得容量不足造成系统问题喔!(例如最容易被灌爆的 /var/spool/mail 这个放置邮件的磁盘)
- Mounted on:就是磁盘挂载的目录所在啦!(挂载点啦!)
du
1 | [root@study ~]$ du [-ahskm] 文件或目录名称 |
与 df 不一样的是,du 这个指令其实会直接到文件系统内去搜寻所有的文件数据
实体链接与符号链接: ln
Hard Link (实体链接, 硬式链接或实际链接)
- 每个文件都会占用一个 inode ,文件内容由 inode 的记录来指向;
- 想要读取该文件,必须要经过目录记录的文件名来指向到正确的 inode 号码才能读取。
1 | [root@study ~]$ ll -i /etc/crontab |
“链接” 从 1 变成 2 ,这个字段的意义为:“有多少个文件名链接到这个 inode 号码”的意思。
hard link 的制作中,其实还是可能会改变系统的 block 的,那就是当你新增这笔数据却刚好将目录的 block 填满时,就可能会新加一个 block 来记录文件名关连性,而导致磁盘空间的变化!不过,一般 hard link 所用掉的关连数据量很小,所以通常不会改变 inode 与磁盘空间的大小喔!
一般来说,使用 hard link 设置链接文件时,磁盘的空间与 inode 的数目都不会改变! 由图中可以知道, hard link 只是在某个目录下的 block 多写入一个关连数据而已,既不会增加 inode 也不会耗用 block 数量哩!
hard link 是有限制的:
- 不能跨 Filesystem;
- 不能 link 目录。
Symbolic Link (符号链接,亦即是捷径)
Symbolic link 可就好理解多了,基本上, Symbolic link 就是在创建一个
独立的文件,而这个文件会让数据的读取指向他 link 的那个文件的文件名!由于只是利用文件来做为指向的动作, 所以,当来源文件被删除之后,symbolic link 的文件会“开不了”, 会一直说“无法打开某文件!”。实际上就是找不到原始“文件名”而已啦!
1 | [root@localhost ~]$ ll /etc/crontab ./crontab.* |
这里还是得特别留意,这个 Symbolic Link 与 Windows 的捷径可以给他划上等号,由Symbolic link 所创建的文件为一个独立的新的文件,所以会占用掉 inode 与 block 喔!
1 | [root@study ~]$ ln [-sf] 来源文件 目标文件 |
磁盘的分区、格式化、检验与挂载
- 对磁盘进行分区,以创建可用的 partition ;
- 对该 partition 进行格式化 (format),以创建系统可用的 filesystem;
- 若想要仔细一点,则可对刚刚创建好的 filesystem 进行检验;
- 在 Linux 系统上,需要创建挂载点 (亦即是目录),并将他挂载上来;
观察磁盘分区状态
lsblk 列出系统上的所有磁盘列表
lsblk 可以看成“ list block device ”的缩写,就是列出所有储存设备的意思!
1 | [root@study ~]$ lsblk [-dfimpt] [device] |
- NAME:就是设备的文件名啰!会省略 /dev 等前导目录
- MAJ:MIN:其实核心认识的设备都是通过这两个代码来熟悉的!分别是主要:次要设备代码
- RM:是否为可卸载设备 (removable device),如光盘、USB 磁盘等等
- SIZE:容量
- RO:是否为只读设备的意思
- TYPE:是磁盘 (disk)、分区 (partition) 还是只读存储器 (rom)等输出
- MOUTPOINT:挂载点
blkid 列出设备的 UUID 等参数
什么是 UUID 呢?UUID 是全域单一识别码(universally unique identifier),Linux 会将系统内所有的设备都给予一个独一无二的识别码, 这个识别码就可以拿来作为挂载或者是使用这个设备/文件系统之用了。
1 | [root@localhost ~]$ blkid |
parted 列出磁盘的分区表类型与分区信息
1 | [root@study ~]$ parted device_name print |
磁盘分区: gdisk/fdisk
MBR 分区表请使用 fdisk 分区, GPT 分区表请使用 gdisk 分区
gdisk
1 | [root@study ~]$ gdisk 设备名称 |
你应该要通过
lsblk
或blkid
先找到磁盘,再用parted /dev/xxx print
来找出内部的分区表类型,之后才用gdisk
或fdisk
来操作系统。
partprobe 更新 Linux 核心的分区表信息
1 | [root@study ~]$ partprobe [-s] # 你可以不要加 -s !那么屏幕不会出现讯息! |
磁盘格式化(创建文件系统)
XFS 文件系统 mkfs.xfs
1 | [root@study ~]$ mkfs.xfs [-b bsize] [-d parms] [-i parms] [-l parms] [-L label] [-f] \ |
EXT4 文件系统 mkfs.ext4
1 | [root@study ~]$ mkfs.ext4 [-b size] [-L label] 设备名称 |
文件系统检验
xfs_repair 处理 XFS 文件系统
1 | [root@study ~]$ xfs_repair [-fnd] 设备名称 |
fsck.ext4 处理 EXT4 文件系统
1 | [root@study ~]$ fsck.ext4 [-pf] [-b superblock] 设备名称 |
文件系统挂载与卸载
- 单一文件系统不应该被重复挂载在不同的挂载点(目录)中;
- 单一目录不应该重复挂载多个文件系统;
- 要作为挂载点的目录,理论上应该都是空目录才是。
1 | [root@study ~]$ mount -a |
重新挂载根目录与挂载不特定目录
1 | 范例:将 / 重新挂载,并加入参数为 rw 与 auto |
重点是那个“ -o remount,xx ”的选项与参数!请注意,要重新挂载 (remount) 时, 这是个非常重要的机制!尤其是当你进入单人维护模式时,你的根目录常会被系统挂载为只读,这个时候这个指令就太重要了!
1 | 范例:将 /var 这个目录暂时挂载到 /data/var 下面: |
看起来,其实两者链接到同一个 inode 嘛! ^_^ 没错啦!通过这个 mount –bind 的功能, 您
可以将某个目录挂载到其他目录去喔!而并不是整块 filesystem 的啦!所以从此进入
/data/var 就是进入 /var 的意思喔!
umount (将设备文件卸载)
1 | [root@study ~]$ umount [-fn] 设备文件名或挂载点 |
磁盘/文件系统参数修订
mknod
1 | [root@localhost ~]$ ll /dev/sda* |
上表当中 252 为主要设备代码 (Major) 而 0~5 则为次要设备代码 (Minor)。 我们的Linux 核心认识的设备数据就是通过这两个数值来决定的!举例来说,常见的磁盘文件名/dev/sda
与 /dev/loop0
设备代码如下所示:
磁盘文件名 | Major | Minor |
---|---|---|
/dev/sda | 8 | 0-15 |
/dev/sdb | 8 | 16-31 |
/dev/loop0 | 7 | 0 |
/dev/loop1 | 7 | 1 |
1 | [root@study ~]$ mknod 设备文件名 [bcp] [Major] [Minor] |
xfs_admin 修改 XFS 文件系统的 UUID 与 Label name
如果你当初格式化的时候忘记加上标头名称,后来想要再次加入时,不需要重复格式化!直接使用这个 xfs_admin 即可。 这个指令直接拿来处理 LABEL name 以及 UUID 即可啰!
1 | [root@study ~]$ xfs_admin [-lu] [-L label] [-U uuid] 设备文件名 |
tune2fs 修改 ext4 的 label name 与 UUID
1 | [root@study ~]$ tune2fs [-l] [-L Label] [-U uuid] 设备文件名 |
设置开机挂载
开机挂载 /etc/fstab 及 /etc/mtab
系统挂载的一些限制:
- 根目录 / 是必须挂载的﹐而且一定要先于其它 mount point 被挂载进来。
- 其它 mount point 必须为已创建的目录﹐可任意指定﹐但一定要遵守必须的系统目录架构原则 (FHS)
- 所有 mount point 在同一时间之内﹐只能挂载一次。
- 所有 partition 在同一时间之内﹐只能挂载一次。
- 如若进行卸载﹐您必须先将工作目录移到 mount point(及其子目录) 之外。
1 | [root@localhost ~]$ cat /etc/fstab |
[设备/UUID等] [挂载点] [文件系统] [文件系统参数] [dump] [fsck]
文件系统参数 | 内容意义 |
---|---|
async/sync非同步/同步 | 设置磁盘是否以非同步方式运行!默认为 async(性能较佳) |
auto/noauto自动/非自动 | 当下达 mount -a 时,此文件系统是否会被主动测试挂载。默认为 auto。 |
rw/ro 可读写/只读 | 让该分区以可读写或者是只读的型态挂载上来,如果你想要分享的数据是不给使用者随意变更的, 这里也能够设置为只读。则不论在此文件系统的文件是否设置 w 权限,都无法写入喔! |
exec/noexec可执行/不可执行 | 限制在此文件系统内是否可以进行“执行”的工作?如果是纯粹用来储存数据的目录, 那么可以设置为 noexec 会比较安全。不过,这个参数也不能随便使用,因为你不知道该目录下是否默认会有可执行文件。举例来说,如果你将 noexec 设置在 /var ,当某些软件将一些可执行文件放置于 /var 下时,那就会产生很大的问题喔! 因此,建议这个 noexec 最多仅设置于你自订或分享的一般数据目录。 |
user/nouser允许/不允许使用者挂载 | 是否允许使用者使用 mount指令来挂载呢?一般而言,我们当然不希望一般身份的 user 能使用 mount 啰,因为太不安全了,因此这里应该要设置为 nouser 啰! |
suid/nosuid具有/不具有suid 权限 | 该文件系统是否允许 SUID 的存在?如果不是可执行文件放置目录,也可以设置为 nosuid 来取消这个功能! |
defaults | 同时具有 rw, suid, dev, exec, auto, nouser, async 等参数。 基本上,默认情况使用 defaults 设置即可! |
特殊设备 loop 挂载 (镜像文件不烧录就挂载使用)
挂载光盘/DVD镜像文件
1 | [root@study ~]$ ll -h /tmp/CentOS-7.0-1406-x86_64-DVD.iso |
创建大文件以制作 loop 设备文件!
如果当初在分区时, 你只有分区出一个根目录,假设你已经没有多余的容量可以进行额外的分区的!偏偏根目录的容量还很大! 此时你就能够制作出一个大文件,然后将这个文件挂载!如此一来感觉上你就多了一个分区啰!用途非常的广泛啦!
创建大型文件
1 | [root@study ~]$ dd if=/dev/zero of=/srv/loopdev bs=1M count=512 |
大型文件的格式化
1 | [root@study ~]$ mkfs.xfs -f /srv/loopdev |
挂载
利用 mount 的特殊参数,那个 -o loop 的参数来处理!
1 | [root@study ~]$ mount -o loop UUID="7dd97bd2-4446-48fd-9d23-a8b03ffdd5ee" /mnt |
CentOS 7.x 越来越聪明了,现在你不需要下达 -o loop 这个选项与参数,它同样可以被系统挂上来! 连直接输入 blkid 都会列出这个文件内部的文件系统耶!相当有趣!不过,为了考虑向下兼容性,鸟哥还是建议你加上 loop 比较妥当喔! 现在,请将这个文件系统永远的自动挂载起来吧!
内存交换空间(swap)之创建
使用实体分区创建swap
创建 swap 分区的步骤:
- 分区:先使用 gdisk 在你的磁盘中分区出一个分区给系统作为 swap 。由于 Linux 的
gdisk 默认会将分区的 ID 设置为 Linux 的文件系统,所以你可能还得要设置一下 system ID 就是了。 - 格式化:利用创建 swap 格式的“mkswap 设备文件名”就能够格式化该分区成为 swap 格式
- 使用:最后将该 swap 设备启动,方法为:“swapon 设备文件名”。
- 观察:最终通过 free 与 swapon -s 这个指令来观察一下内存的用量吧!
文件系统的特殊观察与操作
磁盘空间之浪费问题
整个文件系统中包括 superblock, inode table 与其他中介数据等其实都会浪费磁盘容量喔!
ls -l
第一行的 total 就是该目录下所有数据所使用的 *实际block数量 * block大小的值*。
1 | [root@study ~]$ ll -sh |
利用 GNU 的 parted 进行分区行为(Optional)
1 | [root@study ~]# parted [设备] [指令 [参数]] |
可以使用man parted
,或者是parted /dev/vda help mkpart
去查询更详细的数据。