linux 支持的文件系统类型
Ext2:
有点像 UNIX 文件系统。有 blocks,inodes,directories 的概念。
Ext3:
Ext2 的加强版,添加了日志的功能。支持 POSIX ACL(Access control Lists,访问控制列表) 。
Isofs(iso9660):
CDROM 文件系统。
Sysfs:
基于 ram 的文件系统,起始于 ramfs。用于导出终端用户可以简便使用的内核对象(kernel objects)。
Procfs:
proc 文件系统作为一个内核内部数据结构的界面。可以用来获取关于系统的信息,也可以用 sysctl 命令在系统运行时改变内核的参数。
比如,可以通过下面的命令来查看 cpu 信息:
-
# cat /proc/cpuinfo
也可以打开或者关闭 IP 数据包的路由与转发( routing/forwarding ):
-
# cat /proc/sys/net/ipv4/ip_forward # echo "1" > /proc/sys/net/ipv4/ip_forward # echo "0" > /proc/sys/net/ipv4/ip_forward
NFS:
网络文件操作系统(network file system),允许不同的用户和系统使用 C/S 的方式来共享同一个文件。NFS 允许共享以上的所有文件系统。
Linux 还支持 Microsoft NTFS,vfat 等其它文件系统。在 Linux 内核源码树中,Documentation/filesystem 目录下列有所有支持的文件系统。
使用 mount 命令可以查看当前挂载的文件系统:
-
$ mount
文件系统的几个基本概念
Inodes
每个文件都被 inode 数据结构所表示(以前也被写作i-node)。每个 inode 包含系统中一个物理文件的信息。这个文件可以是目录,socket,buffer,字符或者字块设备,符号链接或者常规文件。所以一个 inode 可以视为一个实体的信息块,描述它在磁盘中的位置,大小,时间戳,文件类型,访问权限和所有者。
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205703388-1125533982.png)
Directories
目录是树状的结构。每个目录可以同时包含文件和子目录。
目录被当作特殊的文件来实现。实际上,一个目录就是一个包含了目录项(directory entry)的文件。每个目录项包含一个 inode 号以及一个文件的名字。当一个进程查找一个文件,内核代码通过路径在目录中查找相应的 inode 号。找到 inode 号后,相应的 inode 被加载进内存以作后续使用。
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205703997-241437426.png)
Links
Unix 文件系统实现了链接的概念。
inode 一般关联一个目录项。但是,使用硬链接(hard link)可以让一个 inode 关联多个目录项。其 inode 中有一个域包含着文件关联的数量。
创建一个硬链接:
-
# ln /root/file1 /root/file2
创建一个软链接(symbolic link or soft link):
-
ln -s /path/to/file1.txt /path/to/file2.txt
原理:
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205704981-1782551908.jpg)
从图中可以看到,硬链接与原始文件共用一个 inode - 345666,345666 号 inode 现在关联两个文件。如果创建的是软链接,它本身会再新建一个 inode - 966600,这个 96660 号 inode 数据块地址指向 966699 号 inode。软链接有点像 windows 的快捷方式。快捷方式本身是个文件,但却指向另外一个地址。而硬链接更像是具有计数功能的别名。
理解了这一张图就不难理解:
-
$ ln FileAFileB#创建一个硬链接 $ ls -il FileAFileB#具有相同的 inode 号 1482256-rw-r--r-- 2 bruno bruno 21May515:55FileA 1482256-rw-r--r-- 2 bruno bruno 21May515:55FileB $ rm FileA $ cat FileB#将看到FileA的内容 $ ln -s FileBFileC#创建软链接 $ ls -il FileBFileC#FileC文件类型是l-链接文件,且与 FileB 具有不同的 inode 1482256-rw-r--r-- 1 bruno bruno 21May515:55FileB 1482226 lrwxrwxrwx 1 bruno bruno 5May516:22FileC->FileB $ rm FileB $ cat FileC#提示不存在该文件,皮之不存毛将焉附?
硬链接 VS 软链接
硬链接 | 软链接 |
硬链接不能在目录间创建 | 可以在不同目录间创建 |
不能跨越文件系统边界 | 可以跨文件系统边界 |
指向源头,即使源头被移动或者移除 | 不会更新 |
Device files
类unix操作系统中,可以能过特别的文件来访问设备。这些文件并不占用文件系统的空间,只是一个设备驱动的访问点。
The Second Extended File system(EXT2)
ext2 是目前 Linux 社区中最成功的文件系统。
像很多其它文件系统,ext2 中的文件也保存在数据块中。数据块的大小是固定的,即使不同的 ext2 文件系统数据块的大小可能是不同的,但一旦 ext2 被创建(mke2fs),这个文件系统中数据块的大小就被固定了。
每个文件的大小都被向上取整成整数个块。如果块的大小为 1024B,一个文件的大小为 1025B,那这个文件需要两个块来存储。
物理布局:
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205706013-498878655.jpg)
其中,block 0 总是指向磁盘或者分区的第一个扇区,如果启动记录(boot record)存在,那么一定在 block 0 中。
超级块(super block)总是位于磁盘或者分区开始的第 1024 字节处。如果一个块大小为 1 KB,那么超级块位于 block 1。如果一个块的大小大于 1024B,那么超级块位于 block 0。
两个例子:
表-Floppy Disk 布局,块大小为 1KB
Block Offset | Length | Description |
---|---|---|
byte 0 | 512 bytes | boot record (if present) |
byte 512 | 512 bytes | additional boot record data (if present) |
-- block group 0, blocks 1 to 1439 -- | ||
byte 1024 | 1024 bytes | superblock |
block 2 | 1 block | block group descriptor table |
block 3 | 1 block | block bitmap |
block 4 | 1 block | inode bitmap |
block 5 | 23 blocks | inode table |
block 28 | 1412 blocks | data blocks |
表-20M 的 ext2 文件系统,使用 1KB 的块
Block Offset | Length | Description |
---|---|---|
byte 0 | 512 bytes | boot record (if present) |
byte 512 | 512 bytes | additional boot record data (if present) |
-- block group 0, blocks 1 to 8192 -- | ||
byte 1024 | 1024 bytes | superblock |
block 2 | 1 block | block group descriptor table |
block 3 | 1 block | block bitmap |
block 4 | 1 block | inode bitmap |
block 5 | 214 blocks | inode table |
block 219 | 7974 blocks | data blocks |
-- block group 1, blocks 8193 to 16384 -- | ||
block 8193 | 1 block | superblock backup |
block 8194 | 1 block | block group descriptor table backup |
block 8195 | 1 block | block bitmap |
block 8196 | 1 block | inode bitmap |
block 8197 | 214 blocks | inode table |
block 8408 | 7974 blocks | data blocks |
-- block group 2, blocks 16385 to 24576 -- | ||
block 16385 | 1 block | block bitmap |
block 16386 | 1 block | inode bitmap |
block 16387 | 214 blocks | inode table |
block 16601 | 3879 blocks | data blocks |
ext2—Inode
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205706935-77628117.png)
表—Inode 数据结构
Offset (bytes) | Size (bytes) | Description |
---|---|---|
0 | 2 | i_mode |
2 | 2 | i_uid |
4 | 4 | i_size |
8 | 4 | i_atime |
12 | 4 | i_ctime |
16 | 4 | i_mtime |
20 | 4 | i_dtime |
24 | 2 | i_gid |
26 | 2 | i_links_count |
28 | 4 | i_blocks |
32 | 4 | i_flags |
36 | 4 | i_osd1 |
40 | 15 x 4 | i_block |
100 | 4 | i_generation |
104 | 4 | i_file_acl |
108 | 4 | i_dir_acl |
112 | 4 | i_faddr |
116 | 12 |
i_osd2
|
i_mode:
16 bit,用来表明所描述文件的类型以及访问权限,可能的值有:
Constant | Value | Description |
---|---|---|
-- file format -- | ||
EXT2_S_IFSOCK | 0xC000 | socket |
EXT2_S_IFLNK | 0xA000 | symbolic link |
EXT2_S_IFREG | 0x8000 | regular file |
EXT2_S_IFBLK | 0x6000 | block device |
EXT2_S_IFDIR | 0x4000 | directory |
EXT2_S_IFCHR | 0x2000 | character device |
EXT2_S_IFIFO | 0x1000 | fifo |
-- process execution user/group override -- | ||
EXT2_S_ISUID | 0x0800 | Set process User ID |
EXT2_S_ISGID | 0x0400 | Set process Group ID |
EXT2_S_ISVTX | 0x0200 | sticky bit |
-- access rights -- | ||
EXT2_S_IRUSR | 0x0100 | user read |
EXT2_S_IWUSR | 0x0080 | user write |
EXT2_S_IXUSR | 0x0040 | user execute |
EXT2_S_IRGRP | 0x0020 | group read |
EXT2_S_IWGRP | 0x0010 | group write |
EXT2_S_IXGRP | 0x0008 | group execute |
EXT2_S_IROTH | 0x0004 | others read |
EXT2_S_IWOTH | 0x0002 | others write |
EXT2_S_IXOTH | 0x0001 | others execute |
i_uid:
16 bit,文件所属用户的用户 id
ext2—Block Groups,块组
多个块组成一个块组(Block Group),这样可以减少碎片化,并且在读取大量连续的数据时可以减少寻道时间(head seeking)。
ext2—Superblock,超级块
超级块包含文件系统的所有配置信息,包括文件系统中有多少 inodes 和 blocks,以及它们中有多少是空闲的,在每个块组(block group)中有多少 inodes 和 blocks。
超级块的主拷贝被存储在磁盘开始 1024 字节处。由于它对于文件系统的挂载至关重要,整个文件系统的块组(block group)将会保存它的备份拷贝。
ext2 的第一个版本(修订号为 0)在每个块组的开始处都保存一份超级块副本。对于大型文件系统来说,这种方法会消耗大量的空间,所有在接下的版本中(修订号为 1),可以将副本保存在指定的块组中。块组号可以选择 0 ,1 以及以 3,5,7 为底的幂。
在超级块中的所有域都以小端模式存储在磁盘中(其它的 ext2 数据结构也是这样),这样 ext2 文件系统在机器间就很方便移植,因为不需要知道文件系统是哪类机器创建的。
超级块持有的信息有:
魔数(magic number):
标志 ext2 文件系统的超级块,挂载软件(mounting software)以此来确认这是 ext2 文件系统的超级块。比如 0xEF53。
修订级别(revision level):
通过主要和次要的修订级别,可以知道当前文件系统是否支持只有在一些特殊修订版的文件系统中才有的功能特性,以及哪些新的功能特性能安全地应用在当前的文件系统中。
挂载次数及最大挂载次数:
两个数合起来决定文件系统是否需要全面检查。每当文件系统被挂载,挂载的次数自增,当它等于最大的挂载次数时,将显示“达到最大挂载次数,推荐运行 e2fsck”
块组号:
当前超级块副本所在的块组号
块大小:
文件系统中块的大小,以字节(byte)为单位。
每组的块数:
在一个组内块的数目。跟块大小一样,当文件系统被创建时,这个数量就被固定了。
空闲的块:
文件系统中空闲的块。
空间的 inodes:
文件系统中空闲的 inode。
第一个 inode:
ext2 根文件系统中的第一个 inode 是根目录 ‘/’。
ext2—Block Group Descriptor,组描述符
为文件系统中的每个块组创建一个组描述符。每个组描述符都代表文件系统中的一个块组,组描述符中所有信息都只是所描述的块组的信息。
每个组描述符(group descriptor)包含以下信息:
Offset (bytes) | Size (bytes) | Description |
---|---|---|
0 | 4 | bg_block_bitmap |
4 | 4 | bg_inode_bitmap |
8 | 4 | bg_inode_table |
12 | 2 | bg_free_blocks_count |
14 | 2 | bg_free_inodes_count |
16 | 2 | bg_used_dirs_count |
18 | 2 | bg_pad |
20 | 12 | bg_reserved |
bg_block_bitmap:
所表示的块组中 block bitmap 的块号(block id),32 bit
bg_inode_bitmap:
所表示的块组中 inode bitmap 的块号,32 bit
bg_inode_table:
所表示的块组中 inode table 的块号,32 bit
bg_free_blocks_count:
所表示的块组中空闲的块数,16 bit
bg_free_inodes_count:
所表示的块组中空闲的 inodes 数量,16 bit
bg_used_dirs_count:
所表示的块组中 inodes 给目录用的数量,16 bit
bg_pad:
16 bit,用来使得整个数据结构对齐 32 bit 的边界
bg_reserved:
12 个保留字节,为以后的版本留出的空间
ext2—Block Group Descriptor Table,组描述符表
组描述符表(block group descriptor table)是一个组描述符的数组,用来定义所有块组的参数。包含 inode bitmap,inode table,block bitmap 的位置,以及空闲块、空闲 inodes 的数量,以及其它有用的信息。它包含所有块组的信息。
组描述符表跟在超级块后面。如果块大小为 1KB,那么它将在第三个块,或者如果块大小为 2KB 它将在第二个块。如果有超级块的拷贝副本,那么在超级块的拷贝副本后面同样有组描述符表的拷贝副本。
如果块组有很多,组描述表可以申请多个块来存储相应的组描述符信息。
ext2—Block Bitmap
在小一点的文件系统中,Block Bitmap 位于组块的第一个块中(因为小,为了节省空间所以不放超级块或者组块描述符表的备份)。通常它的位置可以通过读取块组相关联的组描述符中的 bg_block_bitmap 来获得。
每个位表示块组中块的当前状态,1 表示“已使用”,0 表示 “空闲/可用”。块组中第一个块使用第 0 个字节的第 0 个 bit 来表示,相应地,第二个块用使用第 0 个字节的第 1 个 bit,第八个块使用第 0 个字节的第 7 个 bit 表示,第 9 个块使用第 1 个字节的第 0 个 bit 来表示。
ext2—Inode Bitmap
跟 Block Bitmap 类似,不同的是,每个位表示的是 Inode Table 中的 inode 而不是一个块。
每个块组都有一个 inode bitmap,并且位置同样由块组对应的组描述符中的 bg_inode_bitmap 决定。
ext2—Inode Table
Inode table 被用来保持追踪每个目录,常规文件,符号链接,或者特殊文件;它们的位置,大小,类型及访问权限都被保存在 inodes 中。文件名及 inode 是分开的,inode 并不保存文件名,文件名被保存在目录文件中。
每个块组都有一份 inode table,它的位置由组块对应描述符中的 bg_inode_table 决定。
inode table 中的前几项会被保留。在版本 0 中有 11 项为保留项,版本 1 或者后面几个版本被保留的 inode 项由超级块结构中的 s_first_ino 指定。
表—已知被保留的 inode 项:
Constant Name | Value | Description |
---|---|---|
EXT2_BAD_INO | 1 | bad blocks inode |
EXT2_ROOT_INO | 2 | root directory inode |
EXT2_ACL_IDX_INO | 3 | ACL index inode (deprecated?) |
EXT2_ACL_DATA_INO | 4 | ACL data inode (deprecated?) |
EXT2_BOOT_LOADER_INO | 5 | boot loader inode |
EXT2_UNDEL_DIR_INO | 6 | undelete directory inode |
ext2—Directories,目录
目录用来有层次地组织文件。每个目录都能包含其它目录,常规文件及特殊文件。
目录被当作数据块来保存,并用一个 inode 指向它。指向目录的 inode 中 i_mode 字段被设定为 EXT2_S_IFDIR,由此来表明文件类型为目录。
在 inode table 中每二个表项的 inode 指向根目录(root directory)的数据。它是 inode table 的保留项,被指定为 EXT2_ROOT_INO。
在版本 0 中目录以链表的形式存储。版本 1 及后面的版本引进索引目录(indexed directories)。
链表形式:
链表形式目录项结构:
Offset (bytes) | Size (bytes) | Description |
---|---|---|
0 | 4 | inode |
4 | 2 | rec_len |
6 | 1 | name_len[a] |
7 | 1 | file_type[b] |
8 | 0-255 | name |
举个栗子:
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205707653-1468776248.jpg)
inode:
目录项的 inode。
record length:
目录项的长度(以字节为单位)。
name length:
目录项名字的长度(以字节为单位)。
file type:
文件类型
典型的文件类型及取值:
Constant Name | Value | Description |
---|---|---|
EXT2_FT_UNKNOWN | 0 | Unknown File Type |
EXT2_FT_REG_FILE | 1 | Regular File |
EXT2_FT_DIR | 2 | Directory File |
EXT2_FT_CHRDEV | 3 | Character Device |
EXT2_FT_BLKDEV | 4 | Block Device |
EXT2_FT_FIFO | 5 | Buffer File |
EXT2_FT_SOCK | 6 | Socket File |
EXT2_FT_SYMLINK | 7 | Symbolic Link |
name:目录项的名字。
注:
1)版本 0 的 Ext2 使用 2 个字节的 name_len。由于大部分的实现文件名严格限制最大为 255 个字符,所以这两个字节被截断,低字节部分还是用来存储文件名,高字节用来存储文件类型 file_type。这个时候可以不通过 inode 来查询文件类型了。这个时候看起来会是这个样子:
![](https://images2015.cnblogs.com/blog/705187/201509/705187-20150902205708138-175179288.png)
2)目录项必须 4 字节对齐边界(没有任何目录项会横跨两个数据块)。如果一个目录项不能塞进一个数据块中,那么它就只能放到下一个数据块中了。
举个栗子:
Table . Sample Linked Directory Data Layout, 4KiB blocks
Offset (bytes) | Size (bytes) | Description |
---|---|---|
Directory Entry 0 | ||
0 | 4 | inode number: 783362 |
4 | 2 | record length: 12 |
6 | 1 | name length: 1 |
7 | 1 | file type: EXT2_FT_DIR =2 |
8 | 1 | name: . |
9 | 3 | padding |
Directory Entry 1 | ||
12 | 4 | inode number: 1109761 |
16 | 2 | record length: 12 |
18 | 1 | name length: 2 |
19 | 1 | file type: EXT2_FT_DIR =2 |
20 | 2 | name: .. |
22 | 2 | padding |
Directory Entry 2 | ||
24 | 4 | inode number: 783364 |
28 | 2 | record length: 24 |
30 | 1 | name length: 13 |
31 | 1 | file type: EXT2_FT_REG_FILE |
32 | 13 | name: .bash_profile |
45 | 3 | padding |
Directory Entry 3 | ||
48 | 4 | inode number: 783363 |
52 | 2 | record length: 16 |
54 | 1 | name length: 7 |
55 | 1 | file type: EXT2_FT_REG_FILE |
56 | 7 | name: .bashrc |
63 | 1 | padding |
Directory Entry 4 | ||
64 | 4 | inode number: 783377 |
68 | 2 | record length: 12 |
70 | 1 | name length: 4 |
71 | 1 | file type: EXT2_FT_REG_FILE |
72 | 4 | name: mbox |
Directory Entry 5 | ||
76 | 4 | inode number: 783545 |
80 | 2 | record length: 20 |
82 | 1 | name length: 11 |
83 | 1 | file type: EXT2_FT_DIR =2 |
84 | 11 | name: public_html |
95 | 1 | padding |
Directory Entry 6 | ||
96 | 4 | inode number: 669354 |
100 | 2 | record length: 12 |
102 | 1 | name length: 3 |
103 | 1 | file type: EXT2_FT_DIR =2 |
104 | 3 | name: tmp |
107 | 1 | padding |
Directory Entry 7 | ||
108 | 4 | inode number: 0 |
112 | 2 | record length: 3988 |
114 | 1 | name length: 0 |
115 | 1 | file type: EXT2_FT_UNKNOWN |
116 | 0 | name: |
116 | 3980 | padding |
索引形式:
一旦文件数量开始增长,链接形式的目录就会变得很慢。为了提高性能,基于 hash 的索引用来快速查找文件。
索引目录根(indexed directory root)
为了与链表形式的目录兼容,在 block 0 的开始处放置了一些假的目录项。 . 及 .. 文件夹目录项就是这些的一部分。
Offset (bytes) | Size (bytes) | Description |
---|---|---|
-- Linked Directory Entry: . -- | ||
0 | 4 | inode: this directory |
4 | 2 | rec_len: 12 |
6 | 1 | name_len: 1 |
7 | 1 | file_type: EXT2_FT_DIR =2 |
8 | 1 | name: . |
9 | 3 | padding |
-- Linked Directory Entry: .. -- | ||
12 | 4 | inode: parent directory |
16 | 2 | rec_len: (blocksize - this entry's length(12)) |
18 | 1 | name_len: 2 |
19 | 1 | file_type: EXT2_FT_DIR =2 |
20 | 2 | name: .. |
22 | 2 | padding |
-- Indexed Directory Root Information Structure -- | ||
24 | 4 | reserved, zero |
28 | 1 | hash_version |
29 | 1 | info_length |
30 | 1 | indirect_levels |
31 | 1 | reserved - unused flags |
hash_version:
1 字节,表示 hash 的版本
info_length:
1 字节,表示索引目录信息结构体(dx_root)的大小,即是上表中的 Indexed Directory Root Information。
indirect_levels:
1 字节,表示间接索引的层数。在 Linux 2.6.28 中这个值为 1。
索引目录项(index directory entry)
索引目录项关联一个文件名的哈希值,根据这个哈希值可以快速地查找到相应的 inode 节点。这些索引目录项跟在上面说的索引目录根后面。
它长得像这样:
Table 4-6. Indexed Directory Entry Structure (dx_entry)
Offset (bytes) | Size (bytes) | Description |
---|---|---|
0 | 4 | hash |
4 | 4 | block |
hash:
4 字节,文件名的哈希值
block:
4 字节,包含相应文件名的目录项的 inode 数据块的索引
目录的查找算法
1)计算文件名的哈希值
2)读取 index root
3)使用二分查找查找第一个包含该哈希的子目录
4)重复第 3 步直到到达树的最低一层
5)读取叶子目录项数据块,使用常规的查找
6)如果找到了这个文件名,返回该目录项及 buffer
7)否则,如果下个目录项的冲突位被设置了,那么在下一块数据块中继续查找
目录的插入算法
插入一个新的目录项到目录中比之目录查找要复杂得多。
1)探测要插入的目录项的索引,过程跟查找一样
2)如果目标叶子数据块已经满了,把它分成两份并标记这个数据块将被插入新的目录项
3)跟往常一样插入这个新的目录项到叶子数据块