乐趣区

关于linux运维:删除文件了为啥磁盘还是爆满

问题发现

最近在测试遇到一个问题,容器日志过大导致系统磁盘爆满,造成的影响就是该服务器的一些服务挂掉了。日志过大,解决办法就是删啊,间接定位到容器日志地位发现高达 5G,三下五除二就 rm 了,然而依然显示磁盘空间已满,然而 du -sh 命令也显示文件夹下为空。通过共事百度得悉,容器日志不能间接 rm,要通过 cat /dev/null > {log 文件}形式将日志删除。因为该文件被过程所援用,间接删除并不能擦除磁盘上的文件 block 信息,解决形式就是进行过程。明天就来复现一下并探索一下底层原理。

复现形式

因为容器也属于一种过程,援用文件的形式并没有不同于其余一般过程,所以就应用一个简略的 demo 复现。

首先进入一个目录,以 /tmp/testfile 目录为例, 能够看到我的服务器还有 2G 的残余空间

[centos@guozhao testfile]$ cd /tmp/testfile
[centos@guozhao testfile]$ df -h .
文件系统        容量  已用  可用 已用 % 挂载点
/dev/vda1        50G   49G  2.0G   97% /

而后生成一个随机文件,再来查看残余空间,看到还残余 1014M 空间

[centos@guozhao testfile]$ dd if=/dev/urandom of=/tmp/testfile/delfiletest bs=1M count=1000
记录了 1000+0 的读入
记录了 1000+0 的写出
1048576000 字节 (1.0 GB) 已复制,8.31491 秒,126 MB/ 秒
[centos@guozhao testfile]$ 
[centos@guozhao testfile]$ df -h .
文件系统        容量  已用  可用 已用 % 挂载点
/dev/vda1        50G   49G 1014M   99% /

启动一个程序,援用该文件,不退出过程

func main() {file, err := os.Open("/tmp/testfile/delfiletest")
    defer file.Close()
    if err != nil{fmt.Println("open err :",err.Error())
        return
    }
    time.Sleep(100*time.Minute)
} 

接下来删除该文件文件,查看磁盘占用状况,看到尽管文件删除,然而磁盘空间并没有开释。

[centos@guozhao testfile]$ rm -rf delfiletest
[centos@guozhao testfile]$ df -h .
文件系统        容量  已用  可用 已用 % 挂载点
/dev/vda1        50G   49G 1015M   99% /

解决办法,找到援用该文件的过程,并进行该过程,能够 Ctrl+ C 进行,也能够 kill 命令进行。

[centos@guozhao testfile]$ lsof|grep deleted|grep delfile
lsof: WARNING: can't stat() proc file system /run/docker/netns/default
      Output information may be incomplete.
......
testdelet 29604 29608  centos    3r      REG  253,1 1048576000 58925156 /tmp/testfile/delfiletest (deleted)
......
[centos@guozhao testfile]$ kill 29604

进行后再次查看磁盘空间,发现磁盘曾经开释。

[centos@guozhao-170 testfile]$ df -h .
文件系统        容量  已用  可用 已用 % 挂载点
/dev/vda1        50G   49G  2.0G   97% /

重做一次下面的步骤,这次咱们应用正确的形式开释磁盘的空间,能够看到磁盘空间腾出来了。

[centos@guozhao-170 testfile]$ cat /dev/null > /tmp/testfile/delfiletest 
[centos@guozhao-170 testfile]$ 
[centos@guozhao-170 testfile]$ df -h .
文件系统        容量  已用  可用 已用 % 挂载点
/dev/vda1        50G   49G  2.0G   97% /

起因探索

持续深挖一下造成这种后果的起因。

在 Linux 上,每个文件都有一个本人对应的索引节点即 inode,在这个inode 里记录了文件在磁盘的块信息,以及链接数量等信息,一个文件在是否要被真正删除开释空间,取决于两个值,一个是i_count,代表援用计数;一个是i_nlink,代表硬链接数量,只有当两个都为 0,文件才会真正开释。

struct inode{
    atomic_t i_count;
    unsignet int i_nlink;
    ......
};

当有过程应用该文件时候,i_count就会加 1,当过程不在援用或过程完结,就会减一。

硬链接也是如此,当为文件创建一个硬链接时,i_nlink就会加一,删除就会减一,当缩小为 0 时候,就会删除文件,开释空间。

在 Linux 中,硬链接指的是文件名与 inode 的链接,通常创立一个文件对应一个硬链接,咱们能够手动通过 ln 命令或者程序触发 link 零碎调用为一个文件创建一个硬链接,相当于两个文件名对应了同一个磁盘文件,两个都删除才会删除磁盘文件(没有过程援用的前提下)。而 Linux 的 rm 命令相当于执行了 unlink 零碎调用,会使得 i_nlink 数量减一。

当然,因为文件并没有被真正删除,所以该文件是能够复原的,只须要找到过程的 pid,并进入 /proc/{pid}/fd 中,找到对应的文件描述符,执行 cp 命令复制即可找回文件。

退出移动版