Linux EXT 文件系统 详解
上几章我们讲到了Linux启动的一些问题,接下来我们来看一下硬盘分割和EXT格式文件系统的问题。前面提到了分区表的问题,分区表位于MBR, 占用64个字节。所谓的硬盘分区也就是对硬盘进行规划,填写分区表的配置。硬盘默认分区表仅能写入四组分区信息。这四个主要分区我们称之为主分区和拓展分区,而后拓展分区里面又可以划分多个逻辑分区。
先让我们模拟一块硬盘:
dd if=/dev/zero of=zero bs=1M count=100
这样就创建了一个数据块,我们将之看待为一块硬盘,对它进行分区。先看一下这个硬盘
root@hpdm-machine:~/test# fdisk zero
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x536c709e.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Command (m for help): p
Disk zero: 104 MB, 104857600 bytes
255 heads, 63 sectors/track, 12 cylinders, total 204800 sectors 磁头 扇区 磁柱 扇区总数
Units = sectors of 1 * 512 = 512 bytes 一个扇区512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x536c709e
Device Boot Start End Blocks Id System
我们看了一下 大概也就是说这块硬盘当前没有一个有效的分区表,fdisk 就帮我们建立一个DOS 分区表 并表示磁盘标识是0x536c709e。当我们输入w时写入生效。下面我们来尝试一下分区。m可查看帮助。
Command (m for help): p
Disk zero: 104 MB, 104857600 bytes
255 heads, 63 sectors/track, 12 cylinders, total 204800 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x87bc6369
Device Boot Start End Blocks Id System
zero1 2048 100000 48976+ 83 Linux
zero2 100001 150000 25000 83 Linux
zero3 150001 170000 10000 83 Linux
zero4 170001 190000 10000 5 Extended
Command (m for help): n
All primary partitions are in use
Adding logical partition 5
First sector (172049-190000, default 172049):
这里我做了四个分区 3个主分区,一个拓展分区,当我再试图分区时 只能够添加逻辑分区。所以也就是最多可以有四个主分区。这也就是第一个逻辑分区为什么是/dev/sda5开始的原因。
下面再看一下EXT文件系统:
这边将一个50M的数据块格式化EXT4格式。
root@hpdm-machine:~/test# mkfs -t ext4 zero
mke2fs 1.42.9 (4-Feb-2014)
zero is not a block special device.
Proceed anyway? (y,n) y
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
12824 inodes, 51200 blocks
2560 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=52428800
7 block groups ----------> 分组
8192 blocks per group, 8192 fragments per group
1832 inodes per group
Superblock backups stored on blocks:
8193, 24577, 40961
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
格式化之后 硬盘布局大概是这样
我们也可以看到被分成了几个组。然我们看一下文件系统的信息
root@hpdm-machine:~/test# dumpe2fs zero
dumpe2fs 1.42.9 (4-Feb-2014)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: cdbabe16-2374-4f77-b177-dc44602d51e8
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent flex_bg sparse_super huge_file uninit_bg dir_nlink extra_isize
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 12824
Block count: 51200
Reserved block count: 2560
Free blocks: 44668
Free inodes: 12813
First block: 1
Block size: 1024
Fragment size: 1024
Reserved GDT blocks: 199
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 1832
Inode blocks per group: 229
Flex block group size: 16
Filesystem created: Tue Jan 3 13:01:46 2017
Last mount time: n/a
Last write time: Tue Jan 3 13:01:46 2017
Mount count: 0
Maximum mount count: -1
Last checked: Tue Jan 3 13:01:46 2017
Check interval: 0 (<none>)
Lifetime writes: 4387 kB
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 128
Journal inode: 8
Default directory hash: half_md4
Directory Hash Seed: 136ace1b-3264-4454-b473-c2941317098f
Journal backup: inode blocks
Journal features: (none)
Journal size: 4096k
Journal length: 4096
Journal sequence: 0x00000001
Journal start: 0
以上 superblock
以下 Group descriptors
Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
Checksum 0xb91b, unused inodes 1821
Primary superblock at 1, Group descriptors at 2-2
Reserved GDT blocks at 3-201
Block bitmap at 202 (+201), Inode bitmap at 218 (+217)
Inode table at 234-462 (+233)
6360 free blocks, 1821 free inodes, 2 directories, 1821 unused inodes
Free blocks: 229-233, 1838-8192
Free inodes: 12-1832
这些信息都是保存在超级块中, 包含inode 数 block数 挂载次数 剩余inode数 block数 块大小 还有日志的一些信息等等。
root@hpdm-machine:~/test# dumpe2fs zero | grep superblock
dumpe2fs 1.42.9 (4-Feb-2014)
Primary superblock at 1, Group descriptors at 2-2
Backup superblock at 8193, Group descriptors at 8194-8194
Backup superblock at 24577, Group descriptors at 24578-24578
Backup superblock at 40961, Group descriptors at 40962-40962
superblock有一个primary的还有几个用来备份恢复用的。一旦超级块全部被损坏 那这块硬盘就无能无力了。这里可以看到superblock占用了一个块 1024bytes
紧接着我们可以看到Group descriptors 也是1024bytes,这个区段可以描述每个 block group 的开始与结束的 block 号码,以及说明每个区段 (superblock, bitmap, inodemap, data block) 分别介于哪一个 block 号码之间。
现在我们看看一个Group中的 Block bitmap 和 Inode bitmap
先看一下什么是inode 和block
那么文件系统是如何运行的呢?这与操作系统的文件数据有关。较新的操作系统的文件数据除了文件实际内容外, 通常含有非常多的属性,例如 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 号码,
当然也就能够读出该文件的实际数据了。这是个比较有效率的作法,因为如此一来我们的磁盘就能够在短时间内读取出全部的数据, 读写的效能比较好啰。
这个解释够清楚吧。
block bitmap (区块对照表) 如果你想要新增文件时总会用到 block 吧!那你要使用哪个 block
来记录呢?当然是选择『空的 block 』来记录新文件的数据啰。 那你怎么知道哪个 block 是空的?这就得要透过 block
bitmap 的辅助了。从 block bitmap 当中可以知道哪些 block
是空的,因此我们的系统就能够很快速的找到可使用的空间来处置文件啰。同样的,如果你删除某些文件时,那么那些文件原本占用的 block 号码就得要释放出来, 此时在 block bitmap 当中相对应到该
block 号码的标志就得要修改成为『未使用中』啰!这就是 bitmap 的功能。inode bitmap (inode 对照表) 这个其实与 block bitmap 是类似的功能,只是 block bitmap
记录的是使用与未使用的 block 号码, 至于 inode bitmap 则是记录使用与未使用的 inode 号码啰!
让我们再刚才建立的文件系统里面分别创建一个目录和文件看一下两张表变化。
创建了一个目录
我们可以看到inode和block都减少了一个 挂载次数加了一
创建文件也好 inode 数量和 block数量时刻在变化, 由此可以看出目录也占用一个inode和一个block,至于文件的整个读取过程可以参考
目录树读取: 好了,经过上面的说明你也应该要很清楚的知道 inode 本身并不记录文件名,文件名的记录是在目录的 block 当中。
因此在第六章文件与目录的权限说明中, 我们才会提到『新增/删除/更名文件名与目录的 w 权限有关』的特色!那么因为文件名是记录在目录的
block 当中, 因此当我们要读取某个文件时,就务必会经过目录的 inode 与 block ,然后才能够找到那个待读取文件的 inode
号码, 最终才会读到正确的文件的 block 内的数据。由于目录树是由根目录开始读起,因此系统透过挂载的信息可以找到挂载点的 inode 号码(通常一个 filesystem 的最顶层 inode
号码会由 2 号开始喔!),此时就能够得到根目录的 inode 内容,并依据该 inode 读取根目录的 block
内的文件名数据,再一层一层的往下读到正确的档名。举例来说,如果我想要读取 /etc/passwd 这个文件时,系统是如何读取的呢?
[root@www ~]# ll -di / /etc /etc/passwd
2 drwxr-xr-x 23 root root 4096 Sep 22 12:09 / 1912545 drwxr-xr-x 105 root root 12288 Oct 14 04:02 /etc 1914888 -rw-r–r–
1 root root 1945 Sep 29 02:21 /etc/passwd 在鸟哥的系统上面与 /etc/passwd
有关的目录与文件数据如上表所示,该文件的读取流程为(假设读取者身份为 vbird 这个一般身份使用者):/ 的 inode: 透过挂载点的信息找到 /dev/hdc2 的 inode 号码为 2 的根目录 inode,且 inode
规范的权限让我们可以读取该 block 的内容(有 r 与 x) ;/ 的 block: 经过上个步骤取得 block 的号码,并找到该内容有 etc/ 目录的 inode 号码 (1912545);
etc/ 的 inode: 读取 1912545 号 inode 得知 vbird 具有 r 与 x 的权限,因此可以读取 etc/ 的
block 内容;etc/ 的 block: 经过上个步骤取得 block 号码,并找到该内容有 passwd 文件的 inode 号码 (1914888);
passwd 的 inode: 读取 1914888 号 inode 得知 vbird 具有 r 的权限,因此可以读取 passwd 的
block 内容;passwd 的 block: 最后将该 block 内容的数据读出来。
整个EXT文件系统详细内容可参考:http://cn.linux.vbird.org/linux_basic/0230filesystem.php#fdisk