从对我的质疑说起,谈谈Linux下的文件删除
特特本來就是個剛畢業的小菜,很多知識都是靠著大家的指點才慢慢學會的。之前在一篇"純屬虛構"的文章 (鵝廠后臺開發工程師的工作日常) 提到使用 rm 命令刪除一個近 100 G 的 log 文件。
很榮幸,這篇文章被一個大號轉載了,獲得了很不錯的閱讀量。但是,當我看到有大佬在公眾號留言,"質疑" 我文章內容的正確性,甚至把我個人的水平上升到了鵝廠程序員的水平高低問題時,我真是戰戰兢兢、瑟瑟發抖。
咱也不敢說,咱也不敢問,只能默默地去補習相關的知識,于是有了這篇文章。
一、Linux 刪除文件的原理
通過查閱資料知道,Linux 系統下的文件被分成文件元數據 (metadata)和用戶數據 (user data) 兩部分。
用戶數據,亦即文件數據塊 (data block),是保存文件真實內容的空間;而元數據則是保存如文件大小、創建時間、所有者等文件的附加屬性。
在 Linux 中,文件的元數據保存在一個 inode 結構中,inode 號是文件的唯一標識,而文件名僅是為了方便人們的記憶和使用。當然文件名與 inode 之間會存在映射關系。
而 inode 結構中與本文提到的文件刪除相關的兩個重要參數分別是:i_nlink 和 i_count。從 VFS 的 inode 結構體定義中可以看到其類型如下:
struct inode {...const struct inode_operations *i_op; // 索引節點操作unsigned long i_ino; // 索引節點號atomic_t i_count; // 引用計數器unsigned int i_nlink; // 硬鏈接數目... }當某個進程使用了文件時,該文件的 i_count 值會增加;當創建文件的硬鏈接 (區別于軟鏈接)時,該文件的 i_nlink 值會增加。
當 i_nlink 和 i_count 均為 0 時,文件才會被真正刪除 (這里的刪除是指刪除了文件名到 inode ?之間的鏈接關系,而文件的內容仍然完好無損地保存在磁盤中。如果此時關閉機器,阻止任何新的寫磁盤操作,那么被刪除的文件理論上也是可以恢復的)。
而執行刪除命令 rm 時,本質上只是減少了 (或置0) 該文件的磁盤引用計數 i_nlink。
換言之,如果被執行 rm 命令的文件正在被其他進程所引用,那么此時該文件對應的 i_count 將不為0,即便執行了 rm 操作,該文件并沒有真正被刪除,占用的磁盤空間也未被釋放。
因此,上面截圖中的評論是正確的,僅僅通過 rm 命令確實無法使用被進程打開的文件所占用的磁盤空間。
PS:為啥小特特在工作中可以直接通過 rm 刪除和釋放被進程使用的 log 文件呢?其實是因為通過腳本刪除文件時會觸發進程 reload 的邏輯,進程執行 reload 時會重新打開 log 文件 (當然會先關閉)。
二、Linux 刪除大文件的幾個簡單方法
那么,Linux 下刪除一個大文件的正確方式是什么呢?(假設待刪除的大文件名為 access.log)
個人覺得最簡單的方式是執行命令?> access.log。通過重定向 null 到待刪除的文件可以讓該文件瞬間成為空白,有效地釋放磁盤空間。
另外,cat /dev/null > access.log 命令也是一個不錯的選擇。/dev/null 是一個特殊的文件,它可以清空送到它這里來的所有輸入,而它的輸出則可被視為一個空文件。
echo -n "" > access.log 也是常用的一個命令。需要注意的是,別忘了加上 -n 選項 (否則 access.log 文件中會出現一個空白行)。
當然,還可以通過 truncate 命令將待刪除的文件大小縮小為0,但是我平時用得很少。
三、Linux 進程打開的文件信息
再延伸下:
在 Linux 系統下,當進程打開一個文件后,內核會為該進程在 /proc/ 目錄下建立一個以該進程 pid 為名的文件夾來保存該進程的相關信息。而 /proc/pid/fd/ ?文件夾保存的就是該進程打開的所有文件的文件描述符 (file descriptor, fd)。
除了打開的本地文件,進程建立的 socket 鏈接也算是一個 fd。
一個進程打開的文件數量是有限的,執行 cat /proc/pid/limits | grep "Max open files" 命令可以查看進程號為 pid 的進程允許打開的最大文件個數。
當在 Linux 下調用 open 函數返回了系統錯誤碼 24 時 (對應的系統錯誤描述是:"Too many open files"),則表示當前進程打開的文件數量超過了系統設置的上限。
如果你的程序遇到了這個錯誤,按我的經驗來看,一般是出現句柄泄漏了 (可能是文件未正常關閉、socket 鏈接未正常斷開等),這時候就需要你耐心地 debug 啦。
四、如何高效清空包含大量小文件的目錄
再再延伸下:
Linux 下怎么快速地刪除包含大量小文件的目錄?
直接通過 rm -rf ./* 命令容易導致系統 I/O陡增。我一般使用的方法是利用 rsync 命令同步目錄的機制,通過新建一個空目錄,再將待刪除的目錄與新建的空目錄進行同步,從而達到清空目錄的目的。
總結
以上是生活随笔為你收集整理的从对我的质疑说起,谈谈Linux下的文件删除的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core 3.1 的REST
- 下一篇: 关于 Blazor Server Sid