zoukankan      html  css  js  c++  java
  • 十一、FHS基础原理

     

    文件系统: http://note.youdao.com/noteshare?id=298f02714da5b9483429a40dda667f35&sub=6120396419BA477EBF7F15F1799E4DC4

    更详细可参考 :骏马金龙的博客

    一、一些常见的文件系统

    • 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同类理解为目录。

    (a)inode 记录的文件数据:inode唯独不包含文件名(文件名在目录上)。
    根目录是自引用的,目录是一个映射表不是容器
        1、该文件的存取模式
          2、该文件的属主和属组
          3、该文件的大小
          4、该文件建立或状态改变的时间(ctime)
          5、最近一次的读取时间(mtime)
          6、最近修改的时间

    地址指针:

    •   直接指针
    •   间接指针
    •   三级指针

    使用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数量有1024*1024个,所以在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

    5.4 Reserved GDT(保留GDT)

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

    完整的文件系统结构图

     

    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/并不能读取到该目录下的文件信息,只能读到目录本身的信息。

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

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

    • 硬链接:指向同一个inode的多个文件路径,但需在同一个分区中
      • 目录不支持硬链接;
      • 硬链接不能跨文件系统(跨分区);
      • 创建硬链接即为为inode创建新的引用路径,会增加inode引用计数
      • 修改符号链接文件的权限,变动的是源文件的权限
      • 无论修改硬链接中的哪个文件,全部的文件都会跟着改动权限
      • ln file1 file2
    • 软链接:指向一个文件路径的另一个文件的路径
      • 符号链接与文件是两人个各自独立的文件,各有自己的inode;对原文件创建符号链接不会增加或减少目标文件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的数据块并读取它们。

     
  • 相关阅读:
    Educational Codeforces Round 86 (Rated for Div. 2) D. Multiple Testcases
    Educational Codeforces Round 86 (Rated for Div. 2) C. Yet Another Counting Problem
    HDU
    HDU
    HDU
    HDU
    Good Bye 2019 C. Make Good (异或的使用)
    Educational Codeforces Round 78 (Rated for Div. 2) C. Berry Jam
    codeforces 909C. Python Indentation
    codeforces1054 C. Candies Distribution
  • 原文地址:https://www.cnblogs.com/liuzhiyun/p/11348464.html
Copyright © 2011-2022 走看看