一言以概之:Linux上一切皆文件,inode则是文件的元信息。Linux分配硬盘空间会包含两部分,一部分是inode区,一部分是data区。
文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。
文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。
可以用stat命令,查看某个文件的inode信息:
root@changcheng:/home/docker# stat docker-compose.yaml
File: docker-compose.yaml
Size: 1874 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 1573431 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-08-01 05:32:29.455411249 +0000
Modify: 2023-08-01 05:32:12.000000000 +0000
Change: 2023-08-01 05:32:14.870967822 +0000
Birth: 2023-07-28 05:26:52.057246701 +0000
root@changcheng:/home/docker# file docker-compose.yaml
docker-compose.yaml: Unicode text, UTF-8 text, with CRLF line terminators
inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。
查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。
⚡ root@changcheng ~ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
tmpfs 1002117 1106 1001011 1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv 6553600 237044 6316556 4% /
tmpfs 1002117 1 1002116 1% /dev/shm
tmpfs 1002117 3 1002114 1% /run/lock
/dev/nvme0n1p2 131072 310 130762 1% /boot
/dev/nvme0n1p1 0 0 0 - /boot/efi
tmpfs 1002117 9 1002108 1% /var/snap/microk8s/common/var/lib/kubelet/pods/cb70e392-313f-4285-b5fb-30107df84b5c/volumes/kubernetes.io~projected/kube-api-access-ll6x4
tmpfs 1002117 9 1002108 1% /var/snap/microk8s/common/var/lib/kubelet/pods/a91a008a-101a-4448-8dc1-98dddaa9b47d/volumes/kubernetes.io~projected/kube-api-access-v6qzc
shm 1002117 1 1002116 1% /var/snap/microk8s/common/run/containerd/io.containerd.grpc.v1.cri/sandboxes/a273f929f95b54f78a00c2f1899ca78dc452b56aa0899d8d51101777329325c5/shm
overlay 6553600 237044 6316556 4% /var/snap/microk8s/common/run/containerd/io.containerd.runtime.v2.task/k8s.io/a273f929f95b54f78a00c2f1899ca78dc452b56aa0899d8d51101777329325c5/rootfs
shm 1002117 1 1002116 1% /var/snap/microk8s/common/run/containerd/io.containerd.grpc.v1.cri/sandboxes/f7c37d0116b7f6290a1b8763f2c7bb2d94b8e9d9492dcbb079135cd6dc4004dc/shm
overlay 6553600 237044 6316556 4% /var/snap/microk8s/common/run/containerd/io.containerd.runtime.v2.task/k8s.io/f7c37d0116b7f6290a1b8763f2c7bb2d94b8e9d9492dcbb079135cd6dc4004dc/rootfs
overlay 6553600 237044 6316556 4% /var/snap/microk8s/common/run/containerd/io.containerd.runtime.v2.task/k8s.io/420b53f8583d0c5b43000267616e31e3eefbc278032caebf4e55ab5418d9a016/rootfs
overlay 6553600 237044 6316556 4% /var/snap/microk8s/common/run/containerd/io.containerd.runtime.v2.task/k8s.io/de287c4bc83f77bf2d41b867ee605f0643f41b79a3b893034a14308174b9ba05/rootfs
overlay 6553600 237044 6316556 4% /var/lib/docker/overlay2/58b0100ef141d94b0ade95e1b712374aa2ff439bf55292404c697ba1839103fe/merged
overlay 6553600 237044 6316556 4% /var/lib/docker/overlay2/454c8ca75abff57d639ac491294abb332c406662bfe98c851ddc04c7027e8733/merged
overlay 6553600 237044 6316556 4% /var/lib/docker/overlay2/739a34b4240bfe40b96c27b7ddc15ce5f81c5eb53e1dc4dcacc59b09bda23886/merged
tmpfs 200423 26 200397 1%
由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。
每个inode都有一个号码,操作系统用inode号码来识别不同的文件。
这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。
表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。
使用ls -i
命令,可以看到文件名对应的inode号码:
⚡ root@changcheng /home/docker ls -i docker-compose.yaml
1573431 docker-compose.yaml
Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。
目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。
ls命令只列出目录文件中的所有文件名:
⚡ root@changcheng /home/docker ls -i /etc
1835665 adduser.conf 1835040 dhcp 1835690 issue.net 1835011 mtab 1835075 python3.10 1835727 sudo_logsrvd.conf
1835020 alternatives 1966124 docker 1835050 kernel 1966117 multipath 1835077 rc0.d 1835729 sysctl.conf
1835022 apparmor 1835041 dpkg 2097156 kubernetes 1835707 multipath.conf 1835078 rc1.d 1835093 sysctl.d
1835021 apparmor.d 1835675 e2scrub.conf 1835051 landscape 1835708 nanorc 1835079 rc2.d 1835094 systemd
1835023 apport 1835676 environment 1835053 ldap 1835062 needrestart 1835080 rc3.d 1835095 terminfo
1966081 apt 1835677 ethertypes 1836050 ld.so.cache 1835709 netconfig 1835081 rc4.d 1836503 thermald
1835666 bash.bashrc 1966140 fonts 1835692 ld.so.conf 1835063 netplan 1835082 rc5.d 1835730 timezone
1835667 bash_completion 1836550 fstab 1835052 ld.so.conf.d 1835064 network 1835083 rc6.d 1835096 tmpfiles.d
1835025 bash_completion.d 1835678 fuse.conf 1835693 legal 1835065 networkd-dispatcher 1835084 rcS.d 1835097 ubuntu-advantage
1835668 bindresvport.blacklist 1835042 fwupd 1835694 libaudit.conf 1835017 NetworkManager 1835013 resolv.conf 1835731 ucf.conf
1835026 binfmt.d 1835679 gai.conf 1835054 libblockdev 1835710 networks 1836310 rmt 1835098 udev
1835027 byobu 1835043 groff 1835055 libnl-3 1835066 newt 1835718 rpc 1835099 udisks2
1835028 ca-certificates 1836542 group 1835695 locale.alias 1835711 nftables.conf 1835719 rsyslog.conf 1835100 ufw
1836307 ca-certificates.conf 1836522 group- 1836038 locale.gen 1835712 nsswitch.conf 1835085 rsyslog.d 1835101 update-manager
1835669 ca-certificates.conf.dpkg-old 1835044 grub.d 1835744 localtime 1835067 opt 1835720 screenrc 1835102 update-motd.d
1835029 cloud 1836533 gshadow 1835056 logcheck 1835012 os-release 1835086 security 1835103 update-notifier
1835030 console-setup 1835680 gshadow- 1835697 login.defs 1835713 overlayroot.conf 1835087 selinux 1836505 UPower
1966122 containerd 1835045 gss 1835698 logrotate.conf 1835018 PackageKit 1835721 services 1835732 usb_modeswitch.conf
1835031 cron.d 1835682 hdparm.conf 1835057 logrotate.d 1835714 pam.conf 1835818 shadow 1835104 usb_modeswitch.d
1835032 cron.daily 1835683 host.conf 1835699 lsb-release 1835068 pam.d 1835715 shadow- 1835105 vim
1835033 cron.hourly 1836515 hostname 1835058 lvm 1836611 passwd 1835723 shells 1835106 vmware-tools
1835034 cron.monthly 1835684 hosts 1835700 machine-id 1836511 passwd- 1835088 skel 1835015 vtrgb
1835670 crontab 1835686 hosts.allow 1835701 magic 1835069 perl 1835089 sos 1835733 wgetrc
1835035 cron.weekly 1835687 hosts.deny 1835702 magic.mime 1835070 pki 1835090 ssh 1966086 wpa_supplicant
1835036 cryptsetup-initramfs 1966082 ifplugd 1835703 manpath.config 1835071 pm 1835091 ssl 1835019 X11
1835671 crypttab 1966125 init 1835059 mdadm 1835072 polkit-1 1836041 subgid 1835734 xattr.conf
1835037 dbus-1 1835046 init.d 1835704 mime.types 1835073 pollinate 1835725 subgid- 1835107 xdg
1835672 debconf.conf 1835047 initramfs-tools 1835705 mke2fs.conf 1835716 profile 1836040 subuid 1966130 zsh
1835673 debian_version 1835688 inputrc 1835016 ModemManager 1835074 profile.d 1836039 subuid- 1835735 zsh_command_not_found
1835038 default 1835048 iproute2 1835060 modprobe.d 1966127 prometheus 1835726 sudo.conf
1835674 deluser.conf 1835049 iscsi 1835706 modules 1835717 protocols 1835728 sudoers
1835039 depmod.d 1835689 issue 1835061 modules-load.d 1835076 python3 1835092 sudoers.d
一般情况下,文件名和inode号码是”一一对应”关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。
这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为”硬链接”(hard link)。
ln命令可以创建硬链接:
ln 源文件 目标文件
例如在对docker-compose
创建了硬链接docker-compose-ylj
之后,他们两个指向了同一个inode号2753369
。删除任何一个都不会影响inode本身及数据本身的变化。
⚡ root@changcheng /usr/local/bin ln docker-compose docker-compose-ylj
⚡ root@changcheng /usr/local/bin ls -alFi
total 114324
2764966 drwxr-xr-x 2 root root 4096 Aug 1 13:06 ./
2752522 drwxr-xr-x 4 root root 4096 Feb 17 17:19 ../
2753369 -rwxr-xr-x 2 root root 54537935 Jul 28 05:21 docker-compose*
2753369 -rwxr-xr-x 2 root root 54537935 Jul 28 05:21 docker-compose-ylj*
⚡ root@changcheng /usr/local/bin stat docker-compose-ylj
File: docker-compose-ylj
Size: 54537935 Blocks: 106528 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 2753369 Links: 2
Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-08-01 01:45:30.714098917 +0000
Modify: 2023-07-28 05:21:03.000000000 +0000
Change: 2023-08-01 13:06:57.927156833 +0000
Birth: 2023-07-28 05:22:03.657782955 +0000
⚡ root@changcheng /usr/local/bin stat docker-compose
File: docker-compose
Size: 54537935 Blocks: 106528 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 2753369 Links: 2
Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-08-01 01:45:30.714098917 +0000
Modify: 2023-07-28 05:21:03.000000000 +0000
Change: 2023-08-01 13:06:57.927156833 +0000
Birth: 2023-07-28 05:22:03.657782955 +0000
软链接(也称符号链接)可以看成是一个普通的文件,**只不过这个文件中的数据块存放的是源文件的索引节点号。**可以通过为ln
指令添加-s
(s: soft 的意思)选项创建软链接:
我们在同一目录下创建了docker-compose的软链接和硬链接。
可以看到软链接docker-compose-rlj文件本身只有14个字节,并且指向一个新的inode。硬链接和本身都是53M的大小,但指向同一个inode。
⚡ root@changcheng /usr/local/bin ls -alFih
total 112M
2764966 drwxr-xr-x 2 root root 4.0K Aug 1 13:15 ./
2752522 drwxr-xr-x 10 root root 4.0K Feb 17 17:19 ../
2753369 -rwxr-xr-x 2 root root 53M Jul 28 05:21 docker-compose*
2752681 lrwxrwxrwx 1 root root 14 Aug 1 13:15 docker-compose-rlj -> docker-compose*
2753369 -rwxr-xr-x 2 root root 53M Jul 28 05:21 docker-compose-ylj*
2790458 -rwxr-xr-x 1 root root 1.2M Jul 25 05:43 redis-benchmark*
2790461 lrwxrwxrwx 1 root root 12 Jul 25 05:43 redis-check-aof -> redis-server*
2790460 lrwxrwxrwx 1 root root 12 Jul 25 05:43 redis-check-rdb -> redis-server*
2790459 -rwxr-xr-x 1 root root 1.1M Jul 25 05:43 redis-cli*
2790462 lrwxrwxrwx 1 root root 12 Jul 25 05:43 redis-sentinel -> redis-server*
2790457 -rwxr-xr-x 1 root root 5.5M Jul 25 05:43 redis-server*
显然软链接和文件本身家一个文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWkzLSzz-1690992559057)(assets/image-20230801212613-6s6zd6d.png)]
软链接文件的数据块中仅仅存放着源文件的索引节点号,这也是为什么源文件为 36 个字节,而软链接文件却有 9 个字节的原因。由于软链接只不过是数据块中存放了源文件的索引节点号,因此删除软链接文件并不会影响源文件。但是如果删除源文件,由于软链接文件中指向的索引节点号对应的文件没有了,所以会导致软链接文件失效。
软链接不同于硬链接,在软链接中删除源文件会影响到软链接的使用,因此在 Linux 中会有很多地方标识文件是否为软链接:
ls -l
指令输出的文件,其中十个字符中的第一个字符代表文件类型,如果文件为软链接文件则为l
;注意:如果软链接文件和源文件不在同一个目录,源文件要使用绝对路径,不能使用相对路径。