|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
#2Linux 文件系统结构. g5 q. }5 |6 j- N) g8 | ^
Linux 下文件系统是对复杂系统进行合理抽象的一个经典的例子,它通过一套统一的接口函数对不同的文件进行操作(普通文件,字符设备文件,块设备文件和socket 文件)。
) f: C* W# o. H0 G3 v
. k6 a3 }* O1 R a z) l
, K/ R( Z% F; G/ O; q, |- 索引节点(inode): 用来存储数据的信息,如大小,属主,归属和读写权限
- 等。
- 块(block): 存储数据。; U; F! b- f: k' Z. J
4 W, |1 Y7 l& S5 _+ x) ^ 虚拟文件系统VFS(virtual File System)是Linux 内核层实现的一种构架,
# h# [5 a& m3 j: Q4 X6 ^ 为用户空间提供统一的文件操作借口。3 }; g: T8 P1 h, [- e
- x" K/ r7 f5 c# s
& R8 k% n) E3 _, Y+ P+ @9 _
v; c$ j. n- K% I8 j" e) M8 o
Y) {4 _" S- e' l$ \
! L* O1 G& c. \* U
##2.1ext2 文件系统的总体存储布局
. L8 P" b1 {- S7 x# C- R. `/ I+ x2 H% `3 s* y) y) _0 x% t
/ Y9 R9 ?. ?! [! ?
9 m! O- ?$ ~% H5 p8 n1 j' i2 A+ r2 N
1 @' \7 ?2 Q9 ?7 U7 A
###2.1.1Block
4 d4 f4 Z9 b/ Z4 b5 _文件系统中存储的最小单位是块(Block) ,一个块究竟多大在格式化时确定的,例如mke2fs 的-b 选项可以设定块大小为1024、2048 或4096 字节。
0 P/ {/ N k+ q. |- L###2.1.2Boot block
9 b `' Y& P6 s- u而上图中启动块(Boot Block) 的大小是确定的,就是1KB,启动块是由PC 标准规定的,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。启动块BootBlock之后才是ext2 文件系统的开始。
. ~! O/ R# z1 T) w6 ^###2.1.3Block Group:
, B3 C# {% H S) dext2 文件系统将整个分区划成n 个同样大小的块,多个块组成块组(Block2 k% A0 E$ p& m: }6 E' w, o4 R
Group) ,每个块组都由以下部分组成。
% r! q8 }9 W( ^$ E* R1 R# ]& \" q####Super Block:
& @% E0 W: {5 _( T. i超级块 (Super Block)(占用一个块大小)描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次mount的时间等等。超级块在每个块组的开头都有一份拷贝(也就是说每个块组组开头1024 字节为超级快,内容相同,估计是出于安全考量)。占用每个块组的第一个块,共1024 字节,记录着magic(魔数),block_size(块大小),block_count(块数),block_per_group(每组的块数)等信息。
4 N- V6 i) ]! ^9 z$ {####GDT,Group Descriptor Table
p0 C! X- J' l/ _& ^# U" H% R1 O' q块组描述符表(GDT,Group Descriptor Table)(占用一个块大小)每个块组的块组描述符占用32 个字节(见下面),整个分区的块组的块组描述符连成一片放在一个块里面就组成了块组描述符表。每个块组描述符(GroupDescriptor) 存储一个块组的描述信息,例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的inode 和数据块还有多少个等等。( J U6 n# u- V! k
和超级块类似,块组描述符表在每个块组的开头也都有一份拷贝,这些信息& M0 J9 v9 o# b) d* F$ v5 A
是非常重要的,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝。通常内核只用到第0 个块组中的拷贝,当执行e2fsck 检查文件系统一致性时,第0 个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0 个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。3 Z5 w1 I1 F# o p" P+ q5 p8 w$ I
注意,硬盘分区上的所有这些 group descriptors 要能塞在一个 block 里5 z: ]1 s$ F3 f5 ~
面。也就是说 groups_count * descriptor_size 必须小于等于 block_size。' i0 }7 A( L' P6 u6 E
知道了硬盘分区上一共有多少个 block group,我们就可以把这么多个0 R$ d% o; X; ]3 F2 z
group descriptors 读出来了。先来看看 group descriptor 是什么样子的。
: @/ R9 l2 Z. Y! S/ m. }. H2 {' u
$ C# J& W& ? U7 {7 J# j1 q. G$ c) ?, P3 p8 ^$ [
; L/ v0 _, R; u$ H, G' X' L8 Z$ E+ N6 C% b' {% z$ n% o
- struct ext3_group_desc
- {
- __u32 bg_block_bitmap; /* block 指针指向 block bitmap */
- __u32 bg_inode_bitmap; /* block 指针指向 inode bitmap */
- __u32 bg_inode_table; /* block 指针指向 inodes table */
- __u16 bg_free_blocks_count; /* 空闲的 blocks 计数 */
- __u16 bg_free_inodes_count; /* 空闲的 inodes 计数 */
- __u16 bg_used_dirs_count; /* 目录计数 */
- __u16 bg_pad; /* 可以忽略 */
- __u32 bg_reserved[3]; /* 可以忽略 */
- };7 W: Z6 P) x( J5 l" r) W
H) T5 f2 G& T, B7 x/ G" U, V
! a7 G7 Q' T4 ?) U, F
/ W7 ^6 }7 F# P) s, N: K \
每个 group descriptor 是 32 bytes 那么大。从上面,我们看到了三个
: j2 i+ |7 Y4 c# Y5 G) T关键的 block 指针,这三个关键的 block 指针,我们已经在前面都提到过了。
" ~, |& Z0 i* o####块位图 (Block Bitmap)(占用一个块)
* s* ]: f0 ^( b$ C& A* S一个块组中的块是这样利用的:数据块存储所有文件的数据,比如某个分& c" e3 Z8 E5 i/ o
区的块大小是1024 字节,某个文件是2049 字节,那么就需要三个数据块来存,即使第三个块只存了一个字节也需要占用一个整块;超级块、块组描述符表、块位图、inode 位图、inode 表这几部分存储该块组的描述信息。那么如何知道哪些块已经用来存储文件数据或其它描述信息,哪些块仍然空闲可用呢?块位图就是用来描述整个块组中哪些块已用哪些块空闲的,它本身占一个块,其中的每个bit 代表本块组中的一个块,这个bit 为1 表示该块已用,这个bit 为0 表示该块空闲可用。8 Y- n' g* [2 |4 X# E
2 C' F5 |% r: ?* m# U
5 q% J* s) q( p2 U
. d1 j2 z: D" v! d B8 b8 }2 }1 n+ c
- S9 r/ D8 }; F/ z" e, O1 [+ A为什么用df 命令统计整个磁盘的已用空间非常快呢?因为只需要查看每
% v* m" u. K2 q- \: c个块组的块位图即可,而不需要搜遍整个分区(所有块组信息通过GDT形成了链表)。相反,用du 命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。: v' x& S- T/ O8 x( U+ O# s
& x$ y. m3 { ~$ O' W6 J9 e7 n
" k m% E, r( o1 p
+ z# [( h! [6 k( G/ p' t1 S
+ B" e3 I) `3 [) J& T4 i
8 G t/ \0 Q7 y! K: _与此相联系的另一个问题是:
7 _) R5 S6 ^2 z+ M3 {/ o& r在格式化一个分区时究竟会划出多少个块组呢?主要的限制在于块位图本身必须只占一个块。用mke2fs 格式化时默认块大小是1024 字节,可以用-b 参数指定块大小,现在设块大小指定为b 字节,那么一个块可以有8b 个bit,这样大小的一个块位图就可以表示8b 个块的占用情况,因此一个块组最多可以有 8b 个块,如果整个分区有s 个块,那么就可以有s/(8b)个块组。格式化时可以用-g 参数指定一个块组有多少个块,但是通常不需要手动指定,mke2fs 工具会计算出最优的数值。
9 Q6 R5 ]" }8 a4 S9 f' t####inode 位图(inode Bitmap)(占用一个块); D+ P4 S6 b* k/ E* S
和块位图类似,本身占一个块,其中每个bit 表示一个inode 是否空闲可用。9 b) x. z9 J3 B/ x; u
####inode 表(inode Table)(占用n 个块)% G; p# F- @' M& P& Y
我们知道,一个文件除了数据需要存储之外,一些描述信息也需要存储,
/ a4 R M# W8 d" W( v例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访问时间等,也就是ls -l 命令看到的那些信息,这些信息存在inode 中而不是数据块中。每个文件都有一个inode,一个块组中的所有 inode 组成了inode 表。
$ p( @/ X" S$ {% }2 o! w2 _& x* u* e! A) y8 X& h
" P* m. D6 U, T3 [: }$ ]( V
; N F4 p6 ]( |1 m
/ J7 Y+ {+ o" r- q5 j; Z& j0 a& g: D! @9 q9 l2 {
, P1 a$ v7 R( x" ]7 h/ R' T
& U1 q, _% e7 w( F. t# N8 ^ |
|