轉載html
1 目錄模型
2 VFS的概念
VFS是Linux中的一個虛擬文件文件系統,也稱爲虛擬文件系統交換層(Virtual Filesystem Switch)。
它爲應用程序員提供一層抽象,屏蔽底層各類文件系統的差別.
以下圖所示:
node
3 VFS的構建
所謂VFS的構建就是加載實際文件系統的過程,也就是mount被調用的過程。
以下圖所示,以mount一個ext2的文件系統爲例。
這是一個通過簡化的Ext2磁盤結構,只是用於說明用它構建VFS的基本過程。
mount命令的通常形式爲:linux
mount /dev/sdb1 /mnt/mysdb1 /* /dev/sdb1是設備名,/mnt/mysdb1是掛載點 */
VFS文件系統的基本結構是dentry結構體與inode結構體。
Dentry表明一個文件目錄中的一個點,能夠是目錄也能夠是文件。
Inode表明一個在磁盤上的文件,它與磁盤文件一一對應。
Inode與dentry不必定一一對應,一個inode可能會對應多個dentry項。(hard link)程序員
mount時,linux先找到磁盤分區的super block,而後經過解析磁盤的inode table與file data,構建出本身的dentry列表與inode列表。
須要注意的是,VFS其實是按照Ext的方式進行構建的,因此二者很是類似(畢竟Ext是Linux的原生文件系統)。
好比inode節點,Ext與VFS中都把文件管理結構稱爲inode,但實際上它們是不同的。
Ext的inode節點在磁盤上;VFS的inode節點在內存裏。Ext-inode中的一些成員變量實際上是沒有用的,如引用計數等。保留它們的目的是爲了與vfs-node保持一致。這樣在用ext-inode節點構造vfs-inode節點時,就不須要一個一個賦值,只需一次內存拷貝便可。
若是是非EXT格式的磁盤,就沒有這麼幸運了,因此mount非EXT磁盤會慢一些。數組
4 VFS的結構
構建出VFS文件系統後,下一步是把第一節中提到的目錄模型映射到VFS結構體系中。
上文提到了VFS主要由denty與inode構成。
Dentry用於維護VFS的目錄結構,每一個dentry項就表明着咱們用ls時看的的一項(每一個目錄和每一個文件都對應着一個dentry項)。Inode爲文件節點,它與文件一一對應。Linux中,目錄也是一種文件,因此dentry也會對應一個inode節點。函數
下圖是上圖中的目錄模型在VFS中的結構。ui
5 Dentry cache
每一個文件都要對應一個inode節點與至少一個dentry項。
假設咱們有一個100G的硬盤,上面寫滿了空文件,那個須要多少內存才能重建VFS呢?
文件最少要佔用1個block(通常是4K)。假一個dentry與一個inode須要100byte,則dentry與inode須要佔用1/40的空間。1G硬盤則須要2.5G空間。最近都開始換裝1T硬盤了,須要 25G的內存才能放下inode與dentry,相信沒有幾臺電腦能夠承受。spa
爲了不資源浪費,VFS採用了dentry cache的設計。.net
當有用戶用ls命令查看某一個目錄或用open命令打開一個文件時,VFS會爲這裏用的每一個目錄項與文件創建dentry項與inode,即「按需建立」。而後維護一個LRU(Least Recently Used)列表,當Linux認爲VFS佔用太多資源時,VFS會釋放掉長時間沒有被使用的dentry項與inode項。
須要注意的是:這裏的創建於釋放是從內存佔用的角度看。從Linux角度看,dentry與inode是VFS中固有的東西。所不一樣的只是VFS是否把dentry與inode讀到了內存中。對於Ext2/3文件系統,構建dentry與inode的過程很是簡單,但對於其餘文件系統,則會慢得多。設計
6 無dentry時定位文件
由於上面提到的Dentry Cache,VFS並不能保證隨時都有dentry項與inode項可用。
下面是無dentry項與inode項時的定位方式。
爲了簡化問題,這裏假設已經找到了dir的dentry項(找到dentry的過程會在後面講解)。
首先,經過dir對應的dentry0找到inode0節點,有了inode節點就能夠讀取目錄中的信息。其中包含了該目錄包含的下一級目錄與文件文件列表,包括name與inode號。實際上用ls命令查看的就是這些信息。「ls -i」會顯示出文件的inode號。
> ls -i 975248 subdir0 975247 subdir1 975251 file0
而後,根據經過根據subdir0對應的inode號重建inode2,並經過文件數據(目錄也是文件)與inode2重建subdir0的dentry節點:dentry1。
> ls -i 975311 file1 975312 file2
接着,根據file1對應的inode號重建inode4,並經過文件數據與inode4重建file1的dentry節點。
最後,就能夠經過inode4節點訪問文件了。
注意:文件對應的inode號是肯定的,只是inode結構體須要從新構造。
7 有dentry時定位文件
8 Symbolic link
9 hard link
10 進程對文件的管理
進程控制塊task_struct中有兩個變量與文件有關:fs與files。
files中存儲着root與pwd兩個指向dentry項的指針。用戶定路徑時,絕對路徑會經過root進行定位;相對路徑會經過pwd進行定位。
(進程的root不必定是文件系統的根目錄。如ftp進程的根目錄不是文件系統的根目錄,這樣才能保證用戶只能訪問ftp目錄下的內容)
fs是一個file object列表,其中每個節點對應着一個被打開了的文件。
當進程定位到文件時,會構造一個file object,並經過f_inode 關聯到inode節點。
文件關閉時(close),進程會釋放對應對應file object
。
file object中的f_mode是打開時選擇的權限,f_pos爲讀寫位置。
當打開同一個文件屢次時,每次都會構造一個新的file object。每一個file object中有獨立的f_mode與f_pos。
11 open的過程
首先創建一個文件管理結構,以下圖所示,該進程已經打開了兩個文件,接下來咱們再打開一個新文件。
第一步:找到文件;
從上文中能定位到咱們文件的inode節點,找到了inode節點也就找到了文件。
第二步:創建file object;
創建一個新的file object對象,放入file object對象列表,並把它指向inode節點。
第三步:創建file descriptor
file descriptor就是進程控制塊task_struct中files中維護的fd_array。
由於是數組,因此file descriptor實際上已經預先分配好空間了,這裏這是須要把某個空閒的file descriptor與file object關聯起來。
這個file descriptor在數組中的索引號就是open文件時獲得的文件fd。
12 open與dup
同一個文件是能夠open屢次的,結構以下圖所示。每次open都會創建一個新的file descriptor與file object。而後指向同一個文件的inode節點。下圖中,假設open的文件與fd1指向的是同一個文件,則新建立的file object 2與fd1的file object 2會指向同一個inode2節點。
Linux還提供了dup功能,用於複製file descriptor。使用dup不會創建新的非file object,因此新創建的file descriptor會與原filedescriptor同時指向同一個file object。下圖中,咱們經過dup(fd1)獲得了fd2,則fd2與fd1指向同一個file object2。
13 fork對打開文件的影響
Dup的操做與fork一個子進程時的操做相似.
下圖是已有父進程的文件結構:
使用fork後的結構以下。一樣是沒有建立新的file object,所以當對parent process中的fd1進行文件指針的移動時(如讀寫),child process中的fd1也會受影響。也便是說opened files list不是進程的一部分,所以不會被複制。Opened files list應該是一個全局性的資源鏈表,進程維護的是一個指針列表fd table,因此被複制的只是指針列表fd table,而不是opened files list。
14 文件操做函數解析