從 lsof 開始,深入理解 Linux 虛擬文件系統(tǒng)


背景
df命令查看磁盤使用情況,發(fā)現(xiàn)磁盤已經(jīng)滿了。-bash-4.2$ df -ThFilesystem Type Size Used Avail Use% Mounted on/dev/vda1 ext4 30G 30G 0 100% /devtmpfs devtmpfs 489M 0 489M 0% /devtmpfs tmpfs 497M 0 497M 0% /dev/shmtmpfs tmpfs 497M 50M 447M 11% /runtmpfs tmpfs 497M 0 497M 0% /sys/fs/cgroup
2.執(zhí)行 du 命令查看各個目錄的磁盤占用情況,把各個目錄文件的大小相加,發(fā)現(xiàn)并沒有占滿磁盤,有10多G空間莫名失蹤。
-bash-4.2$ du -h --max-depth=1 /home16M /home/logs11G /home/serverdog11G /home
lsof?命令顯示打開已刪除的文件。將有問題的進(jìn)程重啟(或,清空),磁盤空間就會得到釋放。-bash-4.2# lsof | grep deletemysqld 2470 mysql 4u REG 253,1 0 523577 /var/tmp/ibfTeQFn (deleted)mysqld 2470 mysql 5u REG 253,1 0 523579 /var/tmp/ibaHcIdW (deleted)mysqld 2470 mysql 6u REG 253,1 0 523581 /var/tmp/ibLjiALu (deleted)mysqld 2470 mysql 7u REG 253,1 0 523585 /var/tmp/ibCFnzTB (deleted)mysqld 2470 mysql 11u REG 253,1 0 523587 /var/tmp/ibCjuqva (deleted)
什么是虛擬文件系統(tǒng)(VFS:virtual filesystem)?
什么是通用文件模型?
超級塊對象(superblock object)
索引節(jié)點(diǎn)對象(inode object)
文件對象(file object)
目錄項(xiàng)對象(dentry object)
文件的概念
文件的表達(dá)
內(nèi)存表達(dá)
磁盤表達(dá)
目錄樹的構(gòu)建
軟鏈接 vs 硬鏈接
文件 & 磁盤管理
索引節(jié)點(diǎn)狀態(tài)
文件 & 進(jìn)程管理
操作:
打開&刪除
虛擬文件系統(tǒng)(virtual filesystem)

通用文件模型
超級塊對象(superblock object)
內(nèi)存:文件系統(tǒng)安裝時創(chuàng)建,存放文件系統(tǒng)的有關(guān)信息
磁盤:對應(yīng)于存放在磁盤上的文件系統(tǒng)控制塊(filesystem control block)
索引節(jié)點(diǎn)對象(inode object)
內(nèi)存:訪問時創(chuàng)建,存放關(guān)于具體文件的一般信息(
inode 結(jié)構(gòu))
磁盤:對應(yīng)于存放在磁盤上的文件控制塊(file control block)
每個索引節(jié)點(diǎn)對象都有一個索引節(jié)點(diǎn)號,唯一地標(biāo)識文件系統(tǒng)的文件
文件對象(file object)
內(nèi)存:打開文件時創(chuàng)建,存放 打開文件 與進(jìn)程之間進(jìn)行交互的有關(guān)信息(
file 結(jié)構(gòu))
打開文件信息,僅當(dāng)進(jìn)程訪問文件期間存在于內(nèi)核內(nèi)存中。
目錄項(xiàng)對象(dentry object)
內(nèi)存:目錄項(xiàng)一旦被讀入內(nèi)存,VFS就會將其轉(zhuǎn)換成
dentry 結(jié)構(gòu)的目錄項(xiàng)對象
磁盤:特定文件系統(tǒng)以特定的方式存儲在磁盤上
存放目錄項(xiàng)(即,文件名稱)與對應(yīng)文件進(jìn)行鏈接的有關(guān)信息
目錄樹
綜合來說,Linux 的 根文件系統(tǒng)(system’s root filessystem) 是內(nèi)核啟動mount的第一個文件系統(tǒng)。內(nèi)核代碼映像文件保存在根文件系統(tǒng)中,而系統(tǒng)引導(dǎo)啟動程序會在根文件系統(tǒng)掛載之后,從中把一些基本的初始化腳本和服務(wù)等加載到內(nèi)存中去運(yùn)行(文件系統(tǒng)和內(nèi)核是完全獨(dú)立的兩個部分)。其他文件系統(tǒng),則后續(xù)通過腳本或命令作為子文件系統(tǒng)安裝在已安裝文件系統(tǒng)的目錄上,最終形成整個目錄樹。
start_kernelvfs_caches_initmnt_initinit_rootfs // 注冊rootfs文件系統(tǒng)init_mount_tree // 掛載rootfs文件系統(tǒng)…rest_initkernel_thread(kernel_init, NULL, CLONE_FS);
軟鏈接 vs 硬鏈接
i_nlink字段為零時,說明沒有硬鏈接指向該文件。文件 & 進(jìn)程管理

* "in_use" - valid inode, i_count > 0, i_nlink > 0* "dirty" - as "in_use" but also dirty* "unused" - valid inode, i_count = 0
open()?和?close()?操作建立和銷毀文件對象,文件對象通過索引節(jié)點(diǎn)提供的?iget?和?iput??更新索引節(jié)點(diǎn)的i_count字段,以完成使用計數(shù)。open 操作使得 i_count 加一, close 操作使得 i_count 減一。在 close 操作時判斷索引節(jié)點(diǎn)是否釋放,如果 i_count = 0,則意味著不再有進(jìn)程引用,將會從內(nèi)存釋放。文件 & 磁盤管理
文件與磁盤管理聯(lián)系最緊密的操作,莫過于touch和rm操作,而尤以后者最為關(guān)鍵。通過strace(或 dtruss),查看 rm 的實(shí)際的系統(tǒng)調(diào)用
# dtruss rm tmp...geteuid(0x0, 0x0, 0x0) = 0 0ioctl(0x0, 0x4004667A, 0x7FFEE06F09C4) = 0 0lstat64("tmp\0", 0x7FFEE06F0968, 0x0) = 0 0access("tmp\0", 0x2, 0x0) = 0 0unlink("tmp\0", 0x0, 0x0) = 0 0
可以發(fā)現(xiàn) rm 實(shí)際是通過 unlink 完成的。unlink代表刪除目錄項(xiàng),以及減少其索引節(jié)點(diǎn)的計數(shù)。由通用文件模型可知,父目錄本身同樣是一個文件,也就意味著目錄項(xiàng)是其文件數(shù)據(jù)的一部分。刪除目錄項(xiàng)等價于從父目錄的文件中刪除數(shù)據(jù),也就意味著首先要打開父目錄的文件。那么,刪除操作即可理解為:
刪除命令(一個進(jìn)程)使用 open 操作獲得父目錄文件對象
通過?
iget?增加 目錄文件的索引節(jié)點(diǎn)對象計數(shù)讀取目錄文件數(shù)據(jù)
將目錄文件數(shù)據(jù)轉(zhuǎn)化為目錄項(xiàng)對象
由于目錄項(xiàng)包含文件的索引節(jié)點(diǎn),類似的,需要通過 iget 增加文件的索引節(jié)點(diǎn)對象計數(shù)
刪除目錄的目錄項(xiàng)
減少文件索引節(jié)點(diǎn)對象的硬鏈接計數(shù)i_nlink
通過?
iput?結(jié)束對文件索引節(jié)點(diǎn)對象的操作,使用計數(shù) i_count 減一判斷i_count是否為零,如果為零,則釋放內(nèi)存
然后,判斷i_nlink是否為零,如果為零,則釋放磁盤空間
通過 iput 結(jié)束對目錄索引節(jié)點(diǎn)對象的操作。
總結(jié)
回頭來看遇到的問題,其實(shí)可以從兩個角度來理解:
索引與數(shù)據(jù)

緩存策略
由于操作系統(tǒng)使用 Write back 的策略,意味著只有先釋放內(nèi)存,才有可能釋放磁盤。
Why lsof ?
作者:cyningsun
鏈接:https://juejin.im/post/6875110082724659213
文章轉(zhuǎn)載:高效運(yùn)維
(版權(quán)歸原作者所有,侵刪)

