zoukankan      html  css  js  c++  java
  • 理解ext文件系统

    理解ext文件系统

    @(0001学习博客)
    注意:本文参考骏马金龙的博客,详情请移步浏览

    一、一些常见的文件系统

    • Linux的文件系统: ext2(无日志功能), ext3, ext4, xfs, reiserfs, btrfs
    • 光盘:iso9660
    • 交换文件系统:swap (虚拟内存)
    • 网络文件系统:nfs, cifs
    • 集群文件系统:gfs2, ocfs2
    • 内核级分布式文件系统:ceph
    • windows的文件系统:vfat, ntfs
    • 伪文件系统:proc, sysfs, tmpfs, hugepagefs
    • Unix的文件系统:UFS, FFS, JFS
    • 用户空间的分布式文件系统:mogilefs, moosefs, glusterfs

    二、文件系统的组成

    1. block:块

    Linux文件系统中使用“block”块为读写单元,块的大小一般为1024bytes(1k)或2048bytes(2k)或4096bytes(4k)。比如需要读一个或多个块时,文件系统的IO管理器通知磁盘控制器要读取哪些块的数据,硬盘控制器将这些块按扇区读取出来,再通过硬盘控制器将这些扇区数据重组返回给计算机。但是其缺点就是会照成空间浪费,比如一个只有96字节的文件也要完整的占有一个块,那么剩余的空间就会造成浪费。

    2. inode(index node,索引节点)、inode表

    inode的作用主要是高效、有序的查找文件,而文件存储于数量不一的block中。
    inode中存储了inode号、文件类型、权限、文件所有者、大小、时间戳等元数据信息,还存储了指向属于该文件block的指针,这样读取inode就可以找到属于该文件的block,进而读取这些block并获得该文件的数据。可以将inode同类理解为目录。
    使用stat /FILE命令可以查看文件的元数据:

    [root@CentOS7 ~]#stat /etc/passwd
      File: ‘/etc/passwd’  
      Size: 2400      	Blocks: 8          IO Block: 4096   regular file
    Device: 802h/2050d	Inode: 68307519    Links: 1
    Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
    Context: system_u:object_r:passwd_file_t:s0
    Access: 2018-11-23 08:05:31.449000177 +0800
    Modify: 2018-11-20 18:53:20.214867983 +0800
    Change: 2018-11-20 18:53:20.215867983 +0800
     Birth: -
    

    这里再提出一个概念:inode表。
    假如每个inode128字节,一个4K的block就可以存放32个inode,再将这些存放inode的块组合起来就行成了inode table(inode表)。
    举个例子,每一个家庭都要向派出所登记户口信息,通过户口本可以知道家庭住址,而每个镇或街道的派出所将本镇或本街道的所有户口整合在一起,要查找某一户地址时,在派出所就能快速查找到。inode table就是这里的派出所。

    3. bmap(bitmap index)、imap(inode map)位图索引

    3.1 bmap

    在向硬盘存储数据时,文件系统需要知道哪些块是空闲的,哪些块是已经占用了的,bmap的作用就是总览硬盘中哪些block被占用,哪些是空闲的,这样可以高效写入数据。
    位图只使用0和1标识对应block是空闲还是被占用,0和1在位图中的位置和block的位置一一对应,第一位标识第一个块,第二个位标识第二个块,依次下去直到标记完所有的block。比如:对于一个block大小为1KB、容量为1G的文件系统而言,block数量有10241024个,所以在bmap位图中使用10241024个位共1024*1024/8=131072字节=128K,即1G的文件只需要128个block做位图就能完成一一对应。通过扫描这100多个block就能知道哪些block是空闲的,速度提高了非常多。

    注意:bmap优化针对的是写入优化,对读取优化使用的是inode。

    3.2 imap

    imap的作用同理bmap,也是为了让系统迅速了解哪些inode在使用,哪些处于空闲状态

    4.块组

    为解决bmap、inode table和imap太大的问题,比如100G文件就需要128k*100=12.5M的bmap空间,系统扫描这个空间也是蛮费时间,因此我们将占用的block分成block groups(块组)。
    注意:在物理层面上的划分是将磁盘按柱面划分为多个分区,即多个文件系统;在逻辑层面上的划分是将文件系统划分成块组。每个文件系统包含多个块组,每个块组包含多个元数据区和数据区:元数据区就是存储bmap、inode table、imap等的数据;数据区就是存储文件数据的区域。注意块组是逻辑层面的概念,所以并不会真的在磁盘上按柱面、按扇区、按磁道等概念进行划分。

    下面介绍如何划分块组:

    它只需确定一个数据——每个block的大小,再根据bmap至多只能占用一个完整的block的标准就能计算出块组如何划分。如果文件系统非常小,所有的bmap总共都不能占用完一个block,那么也只能空闲bmap的block了。
    (注意:每个block的大小在创建文件系统时可以人为指定,不指定也有默认值。)
    假如现在block的大小是1KB,一个bmap完整占用一个block能标识1024*8= 8192个block(当然这8192个block是数据区和元数据区共8192个,因为元数据区分配的block也需要通过bmap来标识)。每个block是1K,每个块组是8192K即8M,创建1G的文件系统需要划分1024/8=128个块组,如果是1.1G的文件系统呢?128+12.8=128+13=141个块组。
    可以使用dumpe2fs /dev/sda1 查看相关信息:
    ext4文件系统的信息:

    [root@CentOS6 ~]#dumpe2fs /dev/sda1
    dumpe2fs 1.41.12 (17-May-2010)
    Filesystem volume name:   <none>
    Last mounted on:          /boot    # 挂载点
    Filesystem UUID:          db5da648-a6e9-41e2-b9bb-1ec771e61499
    Filesystem magic number:  0xEF53
    Filesystem revision #:    1 (dynamic)
    Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
    Filesystem flags:         signed_directory_hash 
    Default mount options:    user_xattr acl    # 开通acl功能
    Filesystem state:         clean 
    Errors behavior:          Continue   #错误时继续进行
    Filesystem OS type:       Linux
    Inode count:              65536   # inode号数量
    Block count:              262144  # block数量
    Reserved block count:     13107   # 保存的block数量
    Free blocks:              239740  # 空闲的block数量
    Free inodes:              65497   # 空闲的inode数量
    First block:              0       # 第一个block号
    Block size:               4096    # block大小4k
    Fragment size:            4096
    Reserved GDT blocks:      63      # 保留的GDT block数量
    Blocks per group:         32768   # 每个块组的block数量
    Fragments per group:      32768
    Inodes per group:         8192    # 每个块组的inode数量
    Inode blocks per group:   512     # 每个块组inode占用的块数量,即inode表大小,512*4k
    Flex block group size:    16      # 
    Filesystem created:       Tue Oct 23 18:35:07 2018  
    Last mount time:          Fri Nov 23 02:43:10 2018
    Last write time:          Fri Nov 23 02:43:10 2018
    Mount count:              31
    Maximum mount count:      -1
    Last checked:             Tue Oct 23 18:35:07 2018
    Check interval:           0 (<none>)
    Lifetime writes:          88 MB
    Reserved blocks uid:      0 (user root)
    Reserved blocks gid:      0 (group root)
    First inode:              11
    Inode size:	          256        # inode大小
    Required extra isize:     28
    Desired extra isize:      28
    Journal inode:            8      # 日志文件的inode数
    Default directory hash:   half_md4
    Directory Hash Seed:      50ef58d8-3fa1-473a-84d0-2e0a5156c604
    Journal backup:           inode blocks
    Journal features:         (none)
    Journal size:             32M   
    Journal length:           8192
    Journal sequence:         0x0000002b
    Journal start:            0
    

    可见:该分区中共有262144个block,每个块大小4k,所以该分区容量为1G,每个块组包含32768个块,一个分了8个块组。

    5.块组里的其他block

    5.1 Boot Block 引导块

    Boot Block也称为boot sector。它位于分区上的第一个块,占用1024字节,并非所有分区都有这个boot sector,只有装了操作系统的主分区和装了操作系统的逻辑分区才有。里面存放的也是boot loader,这段boot loader称为VBR(主分区装操作系统时)或EBR(扩展分区装操作系统时),这里的Boot loader和mbr上的boot loader是存在交错关系的。开机启动的时候,首先加载mbr中的bootloader,然后定位到操作系统所在分区的boot serctor上加载此处的boot loader。

    5.2 Superblock(超级块)

    超级块(superblock)用于存储文件系统本身的属性信息:如各种时间戳、block总数量和空闲数量、inode总数量和空闲数量、当前文件系统是否正常、什么时候需要自检等等。
    超级块占用1024字节,也需要一个block,所以这个块称为superblock,他的块号可能为0,也可能为1。如果block大小为1K,则引导块正好占用一个block,这个block号为0,所以superblock的号为1;如果block大小大于1K,则引导块和超级块同置在一个block中,这个block号为0。总之superblock的起止位置是第二个1024(1024-2047)字节。
    df 命令读取的就是每个文件系统的超级块内的信息,所以其速度非常快。相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要遍历整个目录的所有文件。

    [root@CentOS6 ~]#df
    Filesystem     1K-blocks    Used Available Use% Mounted on
    /dev/sda2       50264772 4376228  43328544  10% /
    tmpfs            1019176      76   1019100   1% /dev/shm
    /dev/sda1         999320   40360    906532   5% /boot
    /dev/sda3       30106576   45032  28525544   1% /data
    /dev/sr0         3878870 3878870         0 100% /media/CentOS_6.9_Final
    

    superblock对于文件系统而言是至关重要的,超级块丢失或损坏必将导致文件系统的损坏,所以超级块的信息会在块组上有备份。

    dumpe2fs -h /dev/sda1获取超级块信息(dumpe2fs /dev/sda1 略同)

    [root@CentOS6 ~]#dumpe2fs -h /dev/sda1
    
    dumpe2fs 1.41.12 (17-May-2010)
    Filesystem volume name:   <none>
    Last mounted on:          /boot
    Filesystem UUID:          db5da648-a6e9-41e2-b9bb-1ec771e61499
    Filesystem magic number:  0xEF53
    Filesystem revision #:    1 (dynamic)
    Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent flex_bg sparse_super large_file 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:              65536
    Block count:              262144
    Reserved block count:     13107
    Free blocks:              239740
    Free inodes:              65497
    First block:              0
    Block size:               4096
    Fragment size:            4096
    Reserved GDT blocks:      63
    Blocks per group:         32768
    Fragments per group:      32768
    Inodes per group:         8192
    Inode blocks per group:   512
    Flex block group size:    16
    Filesystem created:       Tue Oct 23 18:35:07 2018
    Last mount time:          Fri Nov 23 02:43:10 2018
    Last write time:          Fri Nov 23 02:43:10 2018
    Mount count:              31
    Maximum mount count:      -1
    Last checked:             Tue Oct 23 18:35:07 2018
    Check interval:           0 (<none>)
    Lifetime writes:          88 MB
    Reserved blocks uid:      0 (user root)
    Reserved blocks gid:      0 (group root)
    First inode:              11
    Inode size:	          256
    Required extra isize:     28
    Desired extra isize:      28
    Journal inode:            8
    Default directory hash:   half_md4
    Directory Hash Seed:      50ef58d8-3fa1-473a-84d0-2e0a5156c604
    Journal backup:           inode blocks
    Journal features:         (none)
    Journal size:             32M
    Journal length:           8192
    Journal sequence:         0x0000002b
    Journal start:            0
    

    5.3 GDT(块组描述符表)

    记录每个块组的信息和属性等元数据,大小为32个字节。

    虽然每个块组都需要块组描述符来记录块组的信息和属性元数据,但是不是每个块组中都存放了块组描述符。ext文件系统的存储方式是:将它们组成一个GDT,并将该GDT存放于某些块组中,存放GDT的块组和存放superblock和备份superblock的块相同,也就是说它们是同时出现在某一个块组中的。读取时也总是读取Group0中的块组描述符表信息。

    假如block大小为4KB的文件系统划分了143个块组,每个块组描述符32字节,那么GDT就需要143*32=4576字节即两个block来存放。这两个GDT block中记录了所有块组的块组信息,且存放GDT的块组中的GDT都是完全相同的。
    dumpe2fs /dev/sda1命令后面的显示信息就是GDT
    Alt text

    5.4 Reserved GDT(保留GDT)

    保留GDT用于以后扩容文件系统使用,防止扩容后块组太多,使得块组描述符超出当前存储GDT的blocks。保留GDT和GDT总是同时出现,当然也就和superblock同时出现了。

    完整的文件系统结构图

    Alt text

    6.Data block

    数据所占用的block由文件对应inode记录中的block指针找到,不同的文件类型,数据block中存储的内容是不一样的。以下是Linux中不同类型文件的存储方式:

    • 对于常规文件,文件的数据正常存储在数据块中。
    • 对于目录,该目录下的所有文件和一级子目录的目录名存储在数据块中。
      • 文件名不是存储在其自身的inode中,而是存储在其所在目录的data block中。
    • 对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
    • 设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。

    6.1 目录文件

    对于目录文件,其inode记录中存储的是目录的inode号、目录的属性元数据和目录文件的block指针,这里面没有存储目录自身文件名的信息。
    目录的data block中并没有直接存储目录中文件的inode号,它存储的是指向inode table中对应文件inode号的指针。
    * block指针:每个inode号指向的block
    * inode指针:目录文件其inode指向inode表
    比如:对于没有执行权限的目录文件,我们ll /FILE/TO/并不能读取到该目录下的文件信息,只能读到目录本身的信息。
    Alt text

    所以,目录文件的读权限(r)和写权限(w),都是针对目录文件的数据块本身。由于目录文件内只有文件名、文件类型和inode指针,所以如果只有读权限,只能获取文件名和文件类型信息,无法获取其他信息,尽管目录的data block中也记录着文件的inode指针,但定位指针是需要x权限的,因为其它信息都储存在文件自身对应的inode中,而要读取文件inode信息需要有目录文件的执行权限通过inode指针定位到文件对应的inode记录上。

    补充:硬链接与软链接的区别:

    • 硬链接:指向同一个inode的多个文件路径,但需在同一个分区中

      • 目录不支持硬链接;
      • 硬链接不能跨文件系统(跨分区);
      • 创建硬链接会增加inode引用计数
      • 修改符号链接文件的权限,变动的是源文件的权限
      • 无论修改硬链接中的哪个文件,全部的文件都会跟着改动权限
      • ln file1 file2
    • 软链接:指向一个文件路径的另一个文件路径

      • 符号链接与文件是两人个各自独立的文件,各有自己的inode;对原文件创建符号链接不会增加引用计数;
      • 支持对目录创建符号链接,可以跨文件系统;
      • 删除符号链接文件不影响原文件;但删除原文件,符号指定的路径即不存在,此时会变成无效链接;
      • 符号链接文件的大小是其指定的文件的路径字符串的字节数
      • ln -s file1 file2

    三、示例解读:cat /var/log/messages

    1. 找到根文件系统的块组描述符表所在的blocks,读取GDT(已在内存中)找到inode table的block号。
    1. 在inode table的block中定位到根"/"的inode,找出"/"指向的data block。
    2. 在"/"的datablock中记录了var目录名和指向var目录文件inode的指针,并找到该inode记录,inode记录中存储了指向var的block指针,所以也就找到了var目录文件的data block。
    3. 在var的data block中记录了log目录名和其inode指针,通过该指针定位到该inode所在的块组及所在的inode table,并根据该inode记录找到log的data block。
    4. 在log目录文件的data block中记录了messages文件名和对应的inode指针,通过该指针定位到该inode所在的块组及所在的inode table,并根据该inode记录找到messages的data block。
    5. 最后读取messages对应的datablock。

    简言之:找到GDT-->找到"/"的inode-->找到/的数据块读取var的inode-->找到var的数据块读取log的inode-->找到log的数据块读取messages的inode-->找到messages的数据块并读取它们。

  • 相关阅读:
    tcp笔记
    sublime使用技巧
    mysql笔记
    ubuntu安装mysql
    正则表达式笔记
    网络编程笔记
    swoole安装异步reids
    mysql的时间存储格式
    nginx环境的搭建
    php的闭包函数use的使用
  • 原文地址:https://www.cnblogs.com/sstjustdoit/p/10135281.html
Copyright © 2011-2022 走看看