问题
在目录创建文件的时候 提示 No space left on device
[root@centos7 mnt]# mkdir test
mkdir: cannot create directory ‘test’: No space left on device
[root@centos7 mnt]#
排查
遇到这个问题,一般人首先会想到使用 df -h 去查看空间使用情况。
[root@centos7 mnt]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 20G 2.1G 17G 11% /
devtmpfs 874M 0 874M 0% /dev
tmpfs 884M 0 884M 0% /dev/shm
tmpfs 884M 25M 860M 3% /run
tmpfs 884M 0 884M 0% /sys/fs/cgroup
tmpfs 177M 0 177M 0% /run/user/0
/dev/vdc 9.8G 52M 9.2G 1% /mnt
检查发现 mnt
对应的磁盘 vdc
使用率才 1%,然而却无法写入文件了。这是为什么?很多人在排查到这一步的时候就疑惑不解了。明明磁盘还没满呀!
如果你也是这么想的,说明你的基础打的不扎实。
我们继续通过下面的命令 df -i 查看inode信息,发现vdc 这块盘的inode使用率已经达到了 100%
[root@centos7 mnt]# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 1310720 39389 1271331 4% /
devtmpfs 223643 353 223290 1% /dev
tmpfs 226153 1 226152 1% /dev/shm
tmpfs 226153 416 225737 1% /run
tmpfs 226153 16 226137 1% /sys/fs/cgroup
tmpfs 226153 1 226152 1% /run/user/0
/dev/vdc 655360 655360 0 100% /mnt
所以,该问题的根源是该磁盘的inode 被写满了导致的无法写入新文件。
那么,什么是inode呢?
理解 inode
维基百科对于inode的解释: inode是指在许多“类Unix文件系统”中的一种数据结构。 每个inode保存了文件系统中的一个文件系统对象(包括文件、目录、设备文件、socket、管道, 等等)的元信息数据,但不包括数据内容或者文件名。
什么是inode
文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。
文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。
inode 的内容
inode包含文件的元信息,具体来说有以下内容:
- 文件的字节数
- 文件拥有者的User ID
- 文件的Group ID
- 文件的读、写、执行权限
- 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
- 链接数,即有多少文件名指向这个inode
- 文件数据block的位置
使用stat命令查看文件inode 信息
[root@centos7 mnt]# stat /etc/ssh/sshd_config
File: ‘/etc/ssh/sshd_config’
Size: 4357 Blocks: 16 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 14100 Links: 1
Access: (0600/-rw-------) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-10-17 18:54:25.539000000 +0800
Modify: 2017-04-12 14:17:31.081000000 +0800
Change: 2017-04-12 14:18:31.684000030 +0800
Birth: -
重点来了!
inode会消耗硬盘空间,所以通常硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。所以在排查此类问题的时要不仅仅是排查数据区是否被写满,还要查看inode 是否被写满。
每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。
查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。
查看磁盘的 inode 大小,使用 dumpe2fs 查看 Inode size
的 value。
[root@centos7 mnt]# dumpe2fs -h /dev/vdc
dumpe2fs 1.42.9 (28-Dec-2013)
Filesystem volume name: <none>
Last mounted on: /mnt
Filesystem UUID: 315dfc9d-2326-4c2a-80b6-265ceca95ed3
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit 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: 655360
Block count: 2621440
Reserved block count: 131072
Free blocks: 2538303
Free inodes: 655349
First block: 0
Block size: 4096
Fragment size: 4096
Group descriptor size: 64
Reserved GDT blocks: 1024
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8192
Inode blocks per group: 512
Flex block group size: 16
Filesystem created: Wed Oct 18 13:33:19 2017
Last mount time: Wed Oct 18 13:33:29 2017
Last write time: Wed Oct 18 13:33:29 2017
Mount count: 1
Maximum mount count: -1
Last checked: Wed Oct 18 13:33:19 2017
Check interval: 0 (<none>)
Lifetime writes: 132 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
Default directory hash: half_md4
Directory Hash Seed: c533050d-0c29-482c-bce4-c1dd7d5a5cf8
Journal backup: inode blocks
Journal features: journal_64bit
Journal size: 128M
Journal length: 32768
Journal sequence: 0x00000010
Journal start: 28918
inode号码
每个inode都有一个号码,操作系统用inode号码来识别不同的文件。
这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。
表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。
使用ls -i命令,可以看到文件名对应的inode号码:
[root@centos7 mnt]# ls -i /etc/ssh/sshd_config
14100 /etc/ssh/sshd_config
更多内容请参考下面链接。