Linux 提示磁盘满 No space left on device 的问题分析

问题

在目录创建文件的时候 提示 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

更多内容请参考下面链接。

http://www.ruanyifeng.com/blog/2011/12/inode.html