刪除文件為啥磁盤依然爆滿
01
問題發(fā)現(xiàn)
最近在測試遇到一個問題,容器日志過大導(dǎo)致系統(tǒng)磁盤爆滿,造成的影響就是該服務(wù)器的一些服務(wù)掛掉了。日志過大,解決方法就是刪啊,直接定位到容器日志位置發(fā)現(xiàn)高達5G,三下五除二就rm了,但是仍然顯示磁盤空間已滿,但是du -sh命令也顯示文件夾下為空。經(jīng)過同事百度得知,容器日志不能直接rm,要通過cat /dev/null > {log文件}方式將日志刪除。因為該文件被進程所引用,直接刪除并不能擦除磁盤上的文件block信息,解決方式就是停止進程。今天就來復(fù)現(xiàn)一下并探究一下底層原理。
02
設(shè)計復(fù)現(xiàn)
由于容器也屬于一種進程,引用文件的方式并沒有不同于其他普通進程,所以就使用一個簡單的demo復(fù)現(xiàn)。
首先進入一個目錄,以/tmp/testfile目錄為例,可以看到我的服務(wù)器還有2G的剩余空間
[centos@guozhao testfile]$ cd /tmp/testfile
[centos@guozhao testfile]$ df -h .
文件系統(tǒng) 容量 已用 可用 已用% 掛載點
/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字節(jié)(1.0 GB)已復(fù)制,8.31491 秒,126 MB/秒
[centos@guozhao testfile]$
[centos@guozhao testfile]$ df -h .
文件系統(tǒng) 容量 已用 可用 已用% 掛載點
/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 .
文件系統(tǒng) 容量 已用 可用 已用% 掛載點
/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停止后再次查看磁盤空間,發(fā)現(xiàn)磁盤已經(jīng)釋放。
[centos@guozhao testfile]$ df -h .
文件系統(tǒng) 容量 已用 可用 已用% 掛載點
/dev/vda1 50G 49G 2.0G 97% /重做一次上面的步驟,這次我們使用正確的方式釋放磁盤的空間,可以看到磁盤空間騰出來了。
[centos@guozhao testfile]$ cat /dev/null > /tmp/testfile/delfiletest
[centos@guozhao testfile]$
[centos@guozhao testfile]$ df -h .
文件系統(tǒng) 容量 已用 可用 已用% 掛載點
/dev/vda1 50G 49G 2.0G 97% /03
原理考究
繼續(xù)深挖一下造成這種結(jié)果的原因。
在Linux上,每個文件都有一個自己對應(yīng)的索引節(jié)點即inode,在這個inode里記錄了文件在磁盤的塊信息,以及鏈接數(shù)量等信息,一個文件在是否要被真正刪除釋放空間,取決于兩個值,一個是 i_count ,代表引用計數(shù);一個是 i_nlink ,代表硬鏈接數(shù)量,只有當(dāng)兩個都為0,文件才會真正釋放。
struct inode{
atomic_t i_count;
unsignet int i_nlink;
......
};當(dāng)有進程使用該文件時候, i_count 就會加1,當(dāng)進程不在引用或進程結(jié)束,就會減一。
硬鏈接也是如此,當(dāng)為文件創(chuàng)建一個硬鏈接時, i_nlink 就會加一,刪除就會減一,當(dāng)減少為0時候,就會刪除文件,釋放空間。
在Linux中,硬鏈接指的是文件名與inode的鏈接,通常創(chuàng)建一個文件對應(yīng)一個硬鏈接,我們可以手動通過ln命令或者程序觸發(fā)link系統(tǒng)調(diào)用為一個文件創(chuàng)建一個硬鏈接,相當(dāng)于兩個文件名對應(yīng)了同一個磁盤文件,兩個都刪除才會刪除磁盤文件(沒有進程引用的前提下)。而Linux的rm命令相當(dāng)于執(zhí)行了unlink系統(tǒng)調(diào)用,會使得i_nlink數(shù)量減1。
在上述示例中,由于文件并沒有被真正刪除,所以該文件是可以恢復(fù)的,只需要找到進程的pid,并進入 /proc/{pid}/fd 中,找到對應(yīng)的文件描述符,執(zhí)行 cp 命令復(fù)制即可找回文件。
推薦閱讀
