inode由浅入深

转载于骏马金龙

基于上篇文章:数据块,每个文件都有一个inode,在将inode关联到文件后,系统将通过inode号来识别文件,而不是文件名。并且访问文件时将先找到inode(“inode指针”),通过inode中记录的block位置找到该文件(“block指针”)。

硬链接

虽然每个文件都有一个inode,但是存在一种可能:多个文件的inode相同,也即inode号、元数据、block的位置都相同,这是一种什么情况呢?能够想象这些inode相同的文件都是使用同一条inode记录,所以代表的都是同一个文件,这些文件所在目录的data block中的inode号都是一样的,只不过inode号对应的文件名互不相同而已。这种inode相同的文件在Linux中被称为"硬链接"。

硬链接文件的inode都相同,每个文件都有一个"硬链接数"的属性,使用ls -l的第二列就是被硬链接数,它表示的就是该文件有几个硬链接。

root@generalai-cpu-0:~# ls -l 
total 515904
-rw-r--r--  1 root root 319920128 Apr 16 14:22 MLNX_OFED_LINUX-5.3-1.0.0.1-ubuntu18.04-x86_64.iso
-rw-r--r--  1 root root 178089544 Dec 28 03:30 NVIDIA-Linux-x86_64-460.32.03.run
-rw-r--r--  1 root root  30205748 Jan 30 14:08 NVIDIA-Linux-x86_64-460.39.run
-rw-r--r--  1 root root      2097 May 11 16:38 dcgm-exporter.yaml
drwxrwxr-x  5 root root      4096 Jan 30 18:35 go
-rw-r--r--  1 root root       658 Apr 30 17:55 gpu-pod-slave5.yaml
drwxr-xr-x  3 root root      4096 Apr 17 14:08 kube-manifests

每创建一个文件的硬链接,实质上是多一个指向该inode记录的inode指针,并且硬链接数加1。

删除文件的实质是删除该文件所在目录datablock中的对应的inode行,所以也是减少硬链接次数,由于block指针是存储在inode中的,所以不是真的删除数据,如果仍有其他inode号链接到该inode,那么该文件的block指针仍然是可用的。当硬链接次数为1时再删除文件就是真的删除文件了,此时inode记录中block指针也将被删除。

不能跨分区创建硬链接,因为不同文件系统的inode号可能会相同,如果允许创建硬链接,复制到另一个分区时inode可能会和此分区已使用的inode号冲突。

硬链接只能对文件创建,无法对目录创建硬链接。之所以无法对目录创建硬链接,是因为文件系统已经把每个目录的硬链接创建好了,它们就是相对路径中的“.”和"..",分别标识当前目录的硬链接和上级目录的硬链接。每一个目录中都会包含这两个硬链接,它包含了两个信息:

  1. 一个没有子目录的目录文件的硬链接数是2,其一是目录本身,即该目录datablock中的".",其二是其父目录datablock中该目录的记录,这两者都指向同一个inode号;
  2. 一个包含子目录的目录文件,其硬链接数是2+子目录数,因为每个子目录都关联一个父目录的硬链接".."。

很多人在计算目录的硬链接数时认为由于包含了"."和"..",所以空目录的硬链接数是2,这是错误的,因为".."不是本目录的硬链接。

所以说,为什么硬链接不能对目录创建是受限于文件系统设计的。Linux文件系统中的目录均隐藏了两个特殊的目录 ,这两个都是硬链接,若系统运行对目录创建硬链接,则会产生目录环。

另外,还有一个特殊的目录应该纳入考虑,即"/"目录,它自身是一个文件系统的入口,是自引用(下文中会解释自引用)的,所以"/"目录下的"."和".."的inode号相同,它自身不占用硬链接,因为其datablock中只记录inode号相同的"."和"..",不再像其他目录一样还记录一个名为"/"的目录,所以"/"的硬链接数也是2+子目录数,但这个2是"."和".."的结果。

不过,通过mount工具的"--bind"选项,可以将一个目录挂载到另一个目录下,实现伪"硬链接",它们的内容和inode号是完全相同的。mount --bind和硬连接的区别一文中,详细的区分了这两者的区别。

软链接

软链接就是字符链接,链接文件默认指的就是字符链接文件(注意不是字符设备),使用"l"表示其类型。

硬链接不能跨文件系统创建,否则inode号可能会冲突。于是实现了软链接以便跨文件系统建立链接。既然是跨文件系统,那么软链接必须得有自己的inode号。

软链接在功能上等价与Windows系统中的快捷方式,它指向原文件,原文件损坏或消失,软链接文件就损坏。可以认为软链接inode记录中的指针内容是目标路径的字符串。注意不是目标文件的inode号码,所以说目标文件的“链接数”不会因此而发生变化。软链接是为了克服硬链接的不足而引入的,软链接不直接使用inode号作为文件指针,而是使用文件路径名作为指针。

软链接有自己的inode,并在磁盘上有一小片空间存放路径名。因此,软链接能够跨文件系统,也可以和目录链接!其二,软链接可以对一个不存在的文件名进行链接,但直到这个名字对应的文件被创建后,才能打开其链接。

创建方式: ln –s source_file softlink_name ,记住是source_file<--link_name的指向关系(反箭头),以前我老搞错位置。

查看软链接的值: readlink softlink_name

在设置软链接的时候,source_file虽然不要求是绝对路径,但建议给绝对路径。是否还记得软链接文件的大小?它是根据软链接所指向路径的字符数计算的,例如某个符号链接的指向方式为"rmt --> ../sbin/rmt",它的文件大小为11字节,也就是说只要建立了软链接后,软链接的指向路径是不会改变的,仍然是"../sbin/rmt"。如果此时移动软链接文件本身,它的指向是不会改变的,仍然是11个字符的"../sbin/rmt",但此时该软链接父目录下可能根本就不存在/sbin/rmt,也就是说此时该软链接是一个被破坏的软链接。

inode大小和划分

inode大小为128字节的倍数,最小为128字节。它有默认值大小,它的默认值由/etc/mke2fs.conf文件中指定。不同的文件系统默认值可能不同。

root@generalai-cpu-0:~/test# cat /etc/mke2fs.conf                                                                                                                           
[defaults]                                                                                                                                                                  
        base_features = sparse_super,large_file,filetype,resize_inode,dir_index,ext_attr                                                                                    
        default_mntopts = acl,user_xattr                                                                                                                                    
        enable_periodic_fsck = 0                                                                                                                                            
        blocksize = 4096                                                                                                                                                    
        inode_size = 256                                                                                                                                                    
        inode_ratio = 16384                                                                                                                                                 

[fs_types]
        ext3 = {
                features = has_journal
        }
        ext4 = {
                features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize
                inode_size = 256
        }

同样观察到这个文件中还记录了blocksize的默认值和inode分配比率inode_ratio。inode_ratio=16384表示每16384个字节即16KB就分配一个inode号,由于默认blocksize=4KB,所以每4个block就分配一个inode号。当然分配的这些inode号只是预分配,并不真的代表会全部使用,毕竟每个文件才会分配一个inode号。但是分配的inode自身会占用block,而且其自身大小256字节还不算小,所以inode号的浪费代表着空间的浪费。

如果文件系统中大量存储电影等大文件,inode号就浪费很多,inode占用的空间也浪费很多。但是没办法,文件系统又不知道你这个文件系统是用来存什么样的数据,多大的数据,多少数据。

当然inode size、inode分配比例、block size都可以在创建文件系统的时候人为指定。

ext文件系统预留的inode号

Ext预留了一些inode做特殊特性使用,如下:某些可能并非总是准确,具体的inode号对应什么文件可以使用"find / -inum NUM"查看。

  • Ext4的特殊inode
  • Inode号 用途
  • 0 不存在0号inode,可用于标识目录data block中已删除的文件
  • 1 虚拟文件系统,如/proc和/sys
  • 2 根目录
  • 3 ACL索引
  • 4 ACL数据
  • 5 Boot loader
  • 6 未删除的目录
  • 7 预留的块组描述符inode
  • 8 日志inode
  • 11 第一个非预留的inode,通常是lost+found目录

所以在ext4文件系统的dumpe2fs信息中,能观察到fisrt inode号可能为11也可能为12。

并且注意到"/"的inode号为2,这个特性在文件访问时会用上。

需要注意的是,每个文件系统都会分配自己的inode号,不同文件系统之间是可能会出现使用相同inode号文件的。例如:

root@generalai-cpu-0:~/test# find / -ignore_readdir_race -inum 2 -ls                                                                                                        
        2      4 drwxr-xr-x  23 root     root         4096 Jun 24 06:29 /                                                                                                   
        2      0 drwxr-xr-x   2 root     root           32 Jun 22 09:22 /snap/helm/344/meta                                                                                 
        2      0 drwxr-xr-x   2 root     root           32 May 28 00:13 /snap/helm/341/meta                                                                                 
        2      0 drwxr-xr-x   2 root     root         1678 Jun 12 01:31 /snap/core18/2074/bin                                                                               
        2      0 drwxr-xr-x   2 root     root         1678 May  7 13:50 /snap/core18/2066/bin                                                                               
        2   1893 -rwxr-xr-x   1 root     root      1938264 May 28 02:02 /snap/snapd/12159/bin/fc-cache-v6                                                                   
        2   1893 -rwxr-xr-x   1 root     root      1938264 May 20 01:14 /snap/snapd/12057/bin/fc-cache-v6                                                                   
        2      0 -rw-r--r--   1 root     root            0 Jun 25 14:55 /proc/sys/fs/binfmt_misc/status                                                                     
        2      0 dr-xr-xr-x   2 root     root            0 Jun 25 14:56 /var/lib/lxcfs/proc                                                                                 
        2      0 drwxrwxrwt   3 root     root          140 May 11 15:45 /var/lib/kubelet/pods/a312be4e-ee50-4def-9a8e-7be2e98698e1/volumes/kubernetes.io~secret/flannel-token-2b4gj
        2      0 drwxrwxrwt   3 root     root          140 May 11 16:13 /var/lib/kubelet/pods/433d964e-0926-4992-97d4-aea0469eabea/volumes/kubernetes.io~secret/multus-toke$-s6qtd
        2      0 drwxrwxrwt   3 root     root          140 Jun  3 10:45 /var/lib/kubelet/pods/e1fd7e2c-fb04-4dd0-bc5e-e5190a367d63/volumes/kubernetes.io~secret/kube-proxy-$oken-8hpd7
        2      0 drwxrwsrwt   3 root     nogroup       140 May 11 15:45 /var/lib/kubelet/pods/5ccc670e-7374-4881-8e77-90c743658a2d/volumes/kubernetes.io~secret/dns-autosca$er-token-b4d8c
        2      0 drwxrwxrwt   3 root     root          140 May 11 15:45 /var/lib/kubelet/pods/3302e272-44b6-455b-82af-af3ec86c7b17/volumes/kubernetes.io~secret/coredns-tok$n-6z5pt
        2      0 drwxr-xr-x  17 root     root         3720 May 11 14:19 /dev
        2      0 drwxrwxrwt   2 root     root           40 Jun  3 11:06 /dev/shm
        2      0 c---------   1 root     root       5,   2 May 11 14:19 /dev/pts/ptmx

        2      0 -r--r--r--   1 root     root            0 May 11 14:19 /sys/kernel/security/lsm
        2      0 drwxr-xr-x  11 root     root            0 May 11 14:19 /sys/fs
        2      0 drwxr-xr-x  15 root     root          380 May 11 14:19 /sys/fs/cgroup
        2      0 -rw-r--r--   1 root     root            0 May 11 15:43 /sys/fs/cgroup/blkio/cgroup.procs
        2      0 -rw-r--r--   1 root     root            0 May 11 15:43 /sys/fs/cgroup/devices/cgroup.procs
        2      0 -rw-r--r--   1 root     root            0 May 12 06:25 /sys/fs/cgroup/rdma/cgroup.procs
        2      0 -rw-r--r--   1 root     root            0 May 11 15:43 /sys/fs/cgroup/hugetlb/cgroup.procs

从结果中可见,除了根的Inode号为2,还有几个文件的inode号也是2,它们都属于独立的文件系统,有些是虚拟文件系统,如/proc和/sys。

ext2/3的inode直接、间接寻址

前文说过,inode中保存了blocks指针,但是一条inode记录中能保存的指针数量是有限的,否则就会超出inode大小(128字节或256字节)。

在ext2和ext3文件系统中,一个inode中最多只能有15个指针,每个指针使用i_block[n]表示。

前12个指针i_block[0]到i_block[11]是直接寻址指针,每个指针指向一个数据区的block。

第13个指针i_block[12]是一级间接寻址指针,它指向一个仍然存储了指针的block即i_block[12] --> Pointerblock --> datablock。

第14个指针i_block[13]是二级间接寻址指针,它指向一个仍然存储了指针的block,但是这个block中的指针还继续指向其他存储指针的block,即i_block[13] --> Pointerblock1 --> PointerBlock2 --> datablock。

第15个指针i_block[14]是三级间接寻址指针,它指向一个任然存储了指针的block,这个指针block下还有两次指针指向。即i_block[13] --> Pointerblock1 --> PointerBlock2 --> PointerBlock3 --> datablock。

其中由于每个指针大小为4字节,所以每个指针block能存放的指针数量为BlockSize/4byte。例如blocksize为4KB,那么一个Block可以存放4096/4=1024个指针。

为什么要分间接和直接指针呢?如果一个inode中15个指针全是直接指针,假如每个block的大小为1KB,那么15个指针只能指向15个block即15KB的大小,由于每个文件对应一个inode号,所以就限制了每个文件最大为15*1=15KB,这显然是不合理的。

如果存储大于15KB的文件而又不太大的时候,就占用一级间接指针i_block[12],这时可以存放指针数量为1024/4+12=268,所以能存放268KB的文件。

如果存储大于268K的文件而又不太大的时候,就继续占用二级指针i_block[13],这时可以存放指针数量为[1024/4]^2+1024/4+12=65804,所以能存放65804KB=64M左右的文件。

如果存放的文件大于64M,那么就继续使用三级间接指针i_block[14],存放的指针数量为[1024/4]^3+[1024/4]^2+[1024/4]+12=16843020个指针,所以能存放16843020KB=16GB左右的文件。

如果blocksize=4KB呢?那么最大能存放的文件大小为([4096/4]^3+[4096/4]^2+[4096/4]+12)*4/1024/1024/1024=4T左右。

当然这样计算出来的不一定就是最大能存放的文件大小,它还受到另一个条件的限制。这里的计算只是表明一个大文件是如何寻址和分配的。

其实看到这里的计算数值,就知道ext2和ext3对超大文件的存取效率是低下的,它要核对太多的指针,特别是4KB大小的blocksize时。而ext4针对这一点就进行了优化,ext4使用extent的管理方式取代ext2和ext3的块映射,大大提高了效率也降低了碎片。


标题:inode由浅入深
作者:reyren
地址:https://www.reyren.cn/articles/2021/06/21/1624533490592.html

    评论
    0 评论
avatar

取消