LINUX 的EXT2文件系统学习理解
文件系统的构架非常复杂,在学习中使用了EXT2进行分析和学习,EXT3,EXT4虽然有改进但是基本原理一致
结合学习的东西必须记录一下。
磁盘的块分布情况,默认的我们的一个块是4K,在磁盘中块被划分成成组的形式。记为GROUP 0-GROUP N。
使用dumpe2fs 命令可以看到block group的使用情况。
块的组织方式:
磁盘第一个块:启动块 (boot block)
组0:(假设I个块)
组第一个块:超级块 (SUPER BLOCK)
组第二到第N个块:块组描述符表(GDT)
组第N+1个块:块位图块(block bit map)
组第N+2个块:Inode 位图块(Inode bit map block)
组第N+3到M个块:Inode 表块 (Inode table block)
组第M+1个块到I个块:data block
组1:也是如组0进行组织
组2:也是如组0进行组织
....
组N:也是如组0进行组织
接下来我们进行每个块的分开讨论:
1、启动块 (boot block):这是一个磁盘只有一个的块用来记录磁盘的分区和启动信息,这是不归入文件系统的一个块
2、超级块 (SUPER BLOCK):
描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次mount的时间等等。超级块在每个块组的开头都有一份拷贝。
3、块组描述符表(GDT):每个块组描述符(Group Descriptor)存储一个
块组的描述信息,例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的
inode和数据块还有多少个等等。和超级块类似,块组描述符表在每个块组的开头也都有一
份拷贝,这些信息是非常重要的,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组
描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝
4、块位图块(block bit map):
这里面的一位表示了块是否可用,没有使用记为0,使用了标记为1,考虑到这里ORACLE 11G的
一个数据文件的前128个块也是用于这个作用,而这里如果一个块为4K,那么一个块一共有
4096*8=32768个bit位,那么也就算出来一个组的最大大小为 32768*4096。使用dumpe2fs /sda1
也能有这样的显示:
Block size: 4096
Blocks per group: 32768
5、Inode 位图块(Inode bit map block):
同样为Inode的使用情况,没有使用记为0,使用了标记为1,同样也是占用一个块。如果通过计算发现
4096*8=32768个位是用不完的。见后面INODE分析
6、Inode 表块 (Inode table block)
每个文件都有一个Inode,而这些Inode记录实际是记录在Inode表中的,形成一行一行的Inode记录
Inode用于记录文件的基本信息,而不是文件真正的数据信息,如下:
root@bogon:~# stat log.log
File: ‘log.log’
Size: 35848 Blocks: 72 IO Block: 4096 regular file
Device: 801h/2049d Inode: 1332077 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2016-01-17 21:31:00.359863946 +0800
Modify: 2016-01-17 21:30:56.995864067 +0800
Change: 2016-01-17 21:30:56.995864067 +0800
Birth: -
可以看到这里信息非常多,
例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访
问时间等 ,这其实在内核中是一个数据结构。
23 struct stat {
24 unsigned long st_dev; /* Device. */
25 unsigned long st_ino; /* File serial number. */
26 unsigned int st_mode; /* File mode. */
27 unsigned int st_nlink; /* Link count. */
28 unsigned int st_uid; /* User ID of the file's owner. */
29 unsigned int st_gid; /* Group ID of the file's group. */
30 unsigned long st_rdev; /* Device number, if device. */
31 unsigned long __pad1;
32 long st_size; /* Size of file, in bytes. */
33 int st_blksize; /* Optimal block size for I/O. */
34 int __pad2;
35 long st_blocks; /* Number 512-byte blocks allocated. */
36 long st_atime; /* Time of last access. */
37 unsigned long st_atime_nsec;
38 long st_mtime; /* Time of last modification. */
39 unsigned long st_mtime_nsec;
40 long st_ctime; /* Time of last status change. */
41 unsigned long st_ctime_nsec;
42 unsigned int __unused4;
43 unsigned int __unused5;
44 };
除了记录这些结构数据,剩下就是指向数据块的指针一个指针为4BYTES,每个文件的inode的索引项一共有15 个,
从 Blocks[0] 到Blocks[14],0-11个块指针可以直接指向数据块,叫做直接寻址,而第12个块指针指向的实际是一个新的指针
块,那么如果一个块为4K那么这个块又可以指向4096/4=1024个块,这叫一级寻址这样一级寻址能够建立的最大文件
为11*4+1024*4 这样就上MB了,如果这样还不够还有 13 指针还可以使用二级寻址,甚至还有14号指针使用三级寻址,
如果启用三级寻址那么最大文件大小是:11*4+1024*4+1024*4096/4+1024*4096/4*4096/4=44+4096+1048576+1073741824=1074794540 个块
如果算上4K 1074794540*4/1024/1024~4T。
在格式化的时候INODE就进行了分配,那么一个GROUP到底分配多少个INODE合适呢,LINUX用一个小文件来
默认的操作系统认为一个文件为8K,那么如果一个group有多少个8K就分配多少个INODE,
这样算下:
32768*4/8=16384 个Inode
查看
Inodes per group: 16384
Inode blocks per group: 512
但是我查看新的Ubuntu 14.04.3 中这个改变了
Inodes per group: 8192
Inode blocks per group: 512
那么我们很容易的算出 这个他认为文件为16K,那么也就是说,如果大量的文件小于16K那么会出现 Inode耗尽的情况,
如果是以前是如果大量的文件小于8k。
理论上rm 一个文件只是将他的block bit map,inode bit map进行反转为0同时可能的释放指针指向的数据块,实际数据并没有真正删除,所以删除非常快,这个和数据库中的DROP有一样的原理。
除此以外我们需要理解我们的目录
目录实际上也是一个需要一个inode的文件,而初始Inode指向一个数据库为4096,同样他的各种信息
如例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访
问时间等
如下:
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 801h/2049d Inode: 1330599 Links: 2
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2016-01-19 12:19:56.896204445 +0800
Modify: 2016-01-16 13:56:06.586658881 +0800
Change: 2016-01-16 13:56:06.586658881 +0800
而在这个数据块中存放着关于文件的记录
包含文件名、Inode、记录长度、文件类型,文件名实际数存在目录的记录中的,而他又包含了Inode。
大家都知道在文件系统中都有一个/ 根目录,这个/ 目录的Inode固定而2,这样我们就能找到/目录的数据块,然后就能
进行依次定位了,比如我们方位/home/oracle/log.log文件那么找到log.log文件的次序如下:
1、先找到inode 2,然后通过 Inode 2的数据指针找到/根目录的数据块。
2、在根目录的数据块中寻找到home目录在 /目录数据块中的记录项,其中包含了
文件名、Inode、记录长度、文件类型
那么文件名就是HOME ,Inode指向的数据块就是Home目录实际的数据块
3、找到Home目录的数据块,定位ORACLE目录在Home目录数据块的记录项,同样包含
文件名、Inode、记录长度、文件类型
那么文件名就是oracle Inode指向的数据块就是oralce目录实际的数据块
4、在ORACLE目录的数据块中找到LOG.LOG的记录项目,同样为
文件名、Inode、记录长度、文件类型
文件名为LOG.LOG Inode指向的数据块就是log.log文件实际的数据块
那么我们归纳一下
/根目录的Inode 为2------》/根目录的数据块------》找到Home目录的记录项
--------》通过Inode定位到HOME目录的数据块-----》找到ORACLE目录的记录项
-------》根据Inode定位到ORACLE目录的数据块-----》找到LOG.LOG文件的记录项目
------》根据Inode定位到LOG.LOG的文件块
如此数据就找到了Inode也找到了
所以我们可以简单的认为目录越深,查找的代价越大
最后记录一下这几个时间
Access: 2016-01-24 19:57:38.228325273 +0800 访问时间
Modify: 2016-01-24 19:56:36.140325137 +0800 修改数据块时间
Change: 2016-01-25 04:56:58.941460486 +0800 修改Inode信息时间,换句话说比如修改了文件权限