文件系统的组成部分
block
Linux中通过文件系统控制使用"块"为读写单元
现在的文件系统上,块的大小一般为1024bytes(1K)或2048bytes(2K)或4096bytes(4K)
inode
【文件存储位置的索引】
通过扫描索引找到对应的数据,而且索引可以存储部分数据。
在文件系统上索引技术具体化为索引节点(index node),在索引节点上存储的部分数据即为文件的属性元数据及其他少量信息。
在inode中存储了inode号、文件类型、权限、文件所有者、大小、时间戳等元数据信息,最重要的是还存储了指向属于该文件block的指针,
这样读取inode就可以找到属于该文件的block,进而读取这些block并获得该文件的数据
一般inode大小为128字节或256字节
Inode: 12 Type: regular Mode: 0644 Flags: 0x0
Generation: 1454951771 Version: 0x00000000:00000001
User: 0 Group: 0 Size: 5
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5b628db2:15e0aff4 -- Thu Aug 2 12:50:58 2018
atime: 0x5b628db2:15e0aff4 -- Thu Aug 2 12:50:58 2018
mtime: 0x5b628db2:15e0aff4 -- Thu Aug 2 12:50:58 2018
crtime: 0x5b628db2:15e0aff4 -- Thu Aug 2 12:50:58 2018
Size of extra inode fields: 28
BLOCKS:
(0):1024
TOTAL: 1
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
unsigned int i_flags;
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
/* Stat data, not accessed from path walking */
unsigned long i_ino;
/*
* Filesystems may only read i_nlink directly. They shall use the
* following functions for modification:
*
* (set|clear|inc|drop)_nlink
* inode_(inc|dec)_link_count
*/
union {
const unsigned int i_nlink;
unsigned int __i_nlink;
};
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/* Misc */
unsigned long i_state;
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned long dirtied_time_when;
struct hlist_node i_hash;
struct list_head i_wb_list; /* backing dev IO list */
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list;
union {
struct hlist_head i_dentry;
struct rcu_head i_rcu;
};
u64 i_version;
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_marks;
#endif
void *i_private; /* fs or device private pointer */
};
bmap
【使用bit位标记block那些空闲和占用】
位图只使用0和1标识对应block是空闲还是被占用,0和1在位图中的位置和block的位置一一对应,第一位标识第一个块,第二个位标识第二个块,依次下去直到标记完所有的block。
bmap的优化针对的是写优化,因为只有写才需要找到空闲block并分配空闲block
虽然bmap已经极大的优化了扫描,但是仍有其瓶颈,如果容量超过100G,bmap会占用12800个1KB大小的block,扫描不连续的bmap也会占用很多时间
struct bmap {
struct dbmap db_bmap; /* on-disk aggregate map descriptor */
struct inode *db_ipbmap; /* ptr to aggregate map incore inode */
struct mutex db_bmaplock; /* aggregate map lock */
atomic_t db_active[MAXAG]; /* count of active, open files in AG */
u32 *db_DBmap;
};
imap
在格式化创建文件系统后所有的inode号都是被事先设定好存放在inode table中的,因此产生了问题:要为文件分配哪一个inode号呢?又如何知道某一个inode号是否已经被分配了呢?
标识inode号是否被分配的位图称为inodemap简称为imap。这时要为一个文件分配inode号只需扫描imap即可知道哪一个inode号是空闲的。
块组的出现
将文件系统占用的block划分成块组(block group),解决bmap、inode table和imap太大的问题。
在物理层面上的划分是将磁盘按柱面划分为多个分区,即多个文件系统;在逻辑层面上的划分是将文件系统划分成块组。
每个文件系统包含多个块组,每个块组包含多个元数据区和数据区:元数据区就是存储bmap、inode table、imap等的数据;数据区就是存储文件数据的区域
块组的划分
它只需确定一个数据——每个block的大小,再根据bmap至多只能占用一个完整的block的标准就能计算出块组如何划分
block为默认的1KB,一个bmap完整占用一个block能标识1024*8= 8192个block,每个块组是8192K即8M
创建100M的文件系统需要创建的快组=100/8=12.5=13,与格式化mkfs.ext4 /dev/loop0打印的13 block groups相符
每个组设定多少个inode号呢?inode table占用多少block呢?这需要由系统决定了
超级块(superblock)
存储信息:一个文件系统会分多个块组,每个块组又有多少block多少inode号等等信息,
文件系统本身的属性信息如各种时间戳、block总数量和空闲数量、inode总数量和空闲数量、当前文件系统是否正常、什么时候需要自检等等,
superblock的起止位置是第二个1024(1024-2047)字节(前面1024字节是硬盘的引导区,占据1K字节用来存储分区信息,文件系统不能使用)。查看loop.img十六进制文件可以看出:
00000400h: 58 64 00 00 00 90 01 00 00 14 00 00 40 6D 01 00 ; Xd...?.....@m..
00000410h: 4D 64 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ; Md..............
00000420h: 00 20 00 00 00 20 00 00 B8 07 00 00 0E E7 FC 5B ; . ... ..?...琰[
00000430h: 0E E7 FC 5B 01 00 FF FF 53 EF 01 00 01 00 00 00 ; .琰[..S?.....
00000440h: DB E6 FC 5B 00 00 00 00 00 00 00 00 01 00 00 00 ; 坻黐............
00000450h: 00 00 00 00 0B 00 00 00 80 00 00 00 3C 00 00 00 ; ........€...<...
00000460h: C6 02 00 00 79 00 00 00 20 E9 3F EE 0A 18 48 46 ; ?..y... ??.HF
00000470h: AD 69 A9 C6 D6 C0 3C E6 00 00 00 00 00 00 00 00 ; 璱┢掷<?.......
00000480h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000490h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000004a0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000004b0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000004c0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ; ................
000004d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000004e0h: 08 00 00 00 00 00 00 00 00 00 00 00 26 DF 2A A0 ; ............&??
000004f0h: CC 39 46 D8 98 B0 0A 48 A6 42 18 67 01 01 40 00 ; ?F貥?H.g..@.
00000500h: 0C 00 00 00 00 00 00 00 DB E6 FC 5B 0A F3 01 00 ; ........坻黐.?.
00000510h: 04 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 ; ................
00000520h: 01 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .?.............
00000530h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000540h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 ; ..............@.
00000550h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000560h: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000570h: 00 00 00 00 04 00 00 00 5F 11 00 00 00 00 00 00 ; ........_.......
且可以看到有5个superblock的备份
使用dumpe2fs命令是读设备的超级快(没有实时更新的),df命令是读内存中文件系统superblock信息(实时更新),所以速度很快
在块组0、1和3、5、7幂次方的块组中保存超级块的信息
0x80 0400 == 8193*1024
块组描述符表(GDT)
ext文件系统每一个块组信息使用32字节描述,这32个字节称为块组描述符,所有块组的块组描述符组成块组描述符表GDT(group descriptor table)。
将它们组成一个GDT,并将该GDT存放于某些块组中,存放GDT的块组和存放superblock和备份superblock的块相同,也就是说它们是同时出现在某一个块组中的。
读取时也总是读取Group0中的块组描述符表信息。
格式化后:
Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
Checksum 0x0229, unused inodes 1965
主 superblock at 1, Group descriptors at 2-2
保留的GDT块位于 3-258
Block bitmap at 259 (+258), Inode bitmap at 275 (+274)
Inode表位于 291-537 (+290)
4683 free blocks, 1965 free inodes, 2 directories, 1965个未使用的inodes
可用块数: 3510-8192
可用inode数: 12-1976
11 drwx------. 2 root root 12288 11月 27 14:40 lost+found【lost+found在格式化完成后就创建了占用inode号11】
block分布:
0 1 2 3-258 259 (260-274) 275 (276-290) 291-537 (538-3509) 3510-8192
null super GDT GDT_BACK bmap imap Inode table data block
创建文件1.c 和目录abc:
Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
Checksum 0x0f88, unused inodes 1964
主 superblock at 1, Group descriptors at 2-2
保留的GDT块位于 3-258
Block bitmap at 259 (+258), Inode bitmap at 275 (+274)
Inode表位于 291-537 (+290)
4681 free blocks, 1964 free inodes, 2 directories, 1964个未使用的inodes
可用块数: 3512-8192
可用inode数: 13-1976
1.c a2 abc lost+found
Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
Checksum 0xd264, unused inodes 1964
主 superblock at 1, Group descriptors at 2-2
保留的GDT块位于 3-258
Block bitmap at 259 (+258), Inode bitmap at 275 (+274)
Inode表位于 291-537 (+290)
4680 free blocks, 1964 free inodes, 2 directories, 1964个未使用的inodes
可用块数: 3513-8192
可用inode数: 13-1976
【创建文件和目录不消耗inode?】【不会实时更新到设备的superblock中,在umount或者mount时更新】
[yubo.wang@localhost dir]$ ll -i
总用量 18
12 -rwxr-xr-x. 1 root root 0 11月 27 19:30 1.c
13 -rw-r--r--. 1 root root 0 11月 27 20:11 2.c
1978 drwxr-xr-x. 2 root root 1024 11月 27 19:54 a2
1977 drwxr-xr-x. 2 root root 1024 11月 27 19:34 abc
11 drwx------. 2 root root 12288 11月 27 14:40 lost+found
【创建空文件会保存在Group 0中,inode加1,空文件不占用空间就不会占用block】
【创建目录会保存在Group 1中,inode加1,占用1个block】
下一篇手动破坏超级块和inode再进行恢复;