Linux 文件刪除 (rm) 並非立即清零磁盤數據,而是通過 **鏈接計數器** 機制延遲真正回收。理解 i_count (內存引用計數) 與 i_nlink (硬鏈接計數) 的交互規則,能解釋「刪除後進程仍可讀寫」、「誤刪後搶救黃金期」等經典現象。
| 計數器 | 全稱 | 層級 | 增條件 | 減條件 | 歸零含義 |
|---|---|---|---|---|---|
i_count | inode 內存引用計數 | VFS 內存 | 進程 open()/mmap()、內核持有 | close()/munmap()、內核釋放 | 無進程/內核引用此 inode |
i_nlink | 硬鏈接計數 (目錄項數) | 磁盤 on-disk inode | link() 創建硬鏈接 | unlink() 刪除目錄項 | 目錄樹中無路徑指向此 inode |
用戶執行 rm file
│
▼
sys_unlink("file") → VFS 找到 inode
│
▼
i_nlink-- (磁盤標記 -1)
│
├─► i_nlink > 0 → 僅刪除該目錄項,文件實體保留 (還有其他硬鏈接)
│
└─► i_nlink == 0
│
▼
檢查 i_count
│
├─► i_count > 0 → 標記 inode "待刪除",文件實體保留,進程可繼續讀寫
│ (此時 ls 不見,但 /proc/PID/fd/ 仍可見)
│
└─► i_count == 0 → 真正回收:
1. 釋放數據塊 (block bitmap)
2. 釋放 inode (inode bitmap)
3. 從目錄項移除 (已由 unlink 完成)
rm 後進程仍正常讀寫原因:進程持有文件描述符 → i_count > 0。unlink 僅將 i_nlink 歸零並移除目錄項,VFS 因 i_count > 0 推遲實體回收。進程通過已打開的 fd 繼續操作數據塊,直到 close() 使 i_count 歸零。
驗證:ls -l /proc/$(pidof myapp)/fd/ 可見 deleted 標記的 fd。
df 不變,du 變小)原因:數據塊未回收 (被刪除文件仍佔用空間) → df 顯示已用;目錄項已刪除 → du 掃描不到 → 顯示減少。必須結束持有進程 才能釋放空間。
i_nlink == 0 但 i_count > 0 (進程仍在運行) → 文件實體完整保留在磁盤。cp /proc/PID/fd/FD_NUM /tmp/recovered_file 直接從內存映射複製出來。i_count == 0 → 內核回收數據塊 → 新寫入可能覆蓋舊塊 → 徹底不可恢復。mount -o remount,ro /partition 或卸載分區,防止數據塊被覆寫。extundelete /dev/sdXN --restore-file /path/to/deleted (ext3/4 專用)testdisk / photorec (通用文件系統,按文件特徵碼雕刻)debugfs 手動查找 del_inode 並 dumpjournal 可能保留刪除前的元數據,ext4magic 可嘗試。int fd = open("tmpfile", O_CREAT|O_EXCL, 0600); unlink("tmpfile"); → 文件創建即刪除,僅進程持有 fd 可訪問,進程結束自動清理,**零殘留、零競爭、安全共享**。mv log.log log.log.1; kill -USR1 $(cat pid) → 信號觸發重新打開 fd,舊 fd 關閉釋放空間,無需重啟服務。shred -n 3 -z /dev/sdXN 或 fallocate -p 打洞 + 覆寫,單純 rm 無法防止數據殘留恢復。Linux 刪除 = 減 i_nlink;真正回收 = i_nlink==0 且 i_count==0;進程持有 fd = 搶救窗口與空間不釋放的根因。
深入掌握 inode 雙計數器機制,讓文件系統操作從「感性認識」轉為「理性可控」。