整理自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html
檔案描述符的操作(如: open)傳回的是一個檔案描述符,核心會在每個程序空間中維護一個檔案描述符表, 所有打開的檔案都将通過此表中的檔案描述符來引用;
而流(如: fopen)傳回的是一個FILE結構指針, FILE結構是包含有檔案描述符的,FILE結構函數可以看作是對fd直接操作的系統調用的封裝, 它的優點是帶有I/O緩存
下面舉一個實際的例子,在Linux中,值為0、1、2的fd分别代表标準輸入、标準輸出和标準錯誤輸出。在程式中打開檔案得到的fd從3開始增長。 fd具體是什麼呢?在核心中,每一個程序都有一個私有的“打開檔案表”,這個表是一個指針數組,每一個元素都指向一個核心的打開檔案對象。而fd,就是這 個表的下标。當使用者打開一個檔案時,核心會在内部生成一個打開檔案對象,并在這個表裡找到一個空項,讓這一項指向生成的打開檔案對象,并傳回這一項的下标 作為fd。由于這個表處于核心,并且使用者無法通路到,是以使用者即使擁有fd,也無法得到打開檔案對象的位址,隻能夠通過系統提供的函數來操作。
Linux支援各種各樣的檔案系統格式,如ext2、ext3、reiserfs、FAT、NTFS、iso9660等等,不同的磁盤分區、CD光牒或其它儲存設備都有不同的檔案系統格式,然而這些檔案系統都可以<code>mount</code>到某個目錄下,使我們看到一個統一的目錄樹,各種檔案系統上的目錄和檔案我們用<code>ls</code>指令看起來是一樣的,讀寫操作用起來也都是一樣的,這是怎麼做到的呢?Linux核心在各種不同的檔案系統格式之上做了一個抽象層,使得檔案、目錄、讀寫通路等概念成為抽象層的概念,是以各種檔案系統看起來用起來都一樣,這個抽象層稱為虛拟檔案系統(VFS,Virtual Filesystem)。上一節我們介紹了一種典型的檔案系統在磁盤上的存儲布局,這一節我們介紹運作時檔案系統在核心中的表示。
Linux核心的VFS子系統可以圖示如下:

現在我們明确一下:已打開的檔案在核心中用<code>file</code>結構體表示,檔案描述符表中的指針指向<code>file</code>結構體。
在<code>file</code>結構體中維護File Status Flag(<code>file</code>結構體的成員<code>f_flags</code>)和目前讀寫位置(<code>file</code>結構體的成員<code>f_pos</code>)。在上圖中,程序1和程序2都打開同一檔案,但是對應不同的<code>file</code>結構體,是以可以有不同的File Status Flag和讀寫位置。<code>file</code>結構體中比較重要的成員還有<code>f_count</code>,表示引用計數(Reference Count),後面我們會講到,<code>dup</code>、<code>fork</code>等系統調用會導緻多個檔案描述符指向同一個<code>file</code>結構體,例如有<code>fd1</code>和<code>fd2</code>都引用同一個<code>file</code>結構體,那麼它的引用計數就是2,當<code>close(fd1)</code>時并不會釋放<code>file</code>結構體,而隻是把引用計數減到1,如果再<code>close(fd2)</code>,引用計數就會減到0同時釋放<code>file</code>結構體,這才真的關閉了檔案。
每個<code>file</code>結構體都指向一個<code>file_operations</code>結構體,這個結構體的成員都是函數指針,指向實作各種檔案操作的核心函數。比如在使用者程式中<code>read</code>一個檔案描述符,<code>read</code>通過系統調用進入核心,然後找到這個檔案描述符所指向的<code>file</code>結構體,找到<code>file</code>結構體所指向的<code>file_operations</code>結構體,調用它的<code>read</code>成員所指向的核心函數以完成使用者請求。在使用者程式中調用<code>lseek</code>、<code>read</code>、<code>write</code>、<code>ioctl</code>、<code>open</code>等函數,最終都由核心調用<code>file_operations</code>的各成員所指向的核心函數完成使用者請求。<code>file_operations</code>結構體中的<code>release</code>成員用于完成使用者程式的<code>close</code>請求,之是以叫<code>release</code>而不叫<code>close</code>是因為它不一定真的關閉檔案,而是減少引用計數,隻有引用計數減到0才關閉檔案。對于同一個檔案系統上打開的正常檔案來說,<code>read</code>、<code>write</code>等檔案操作的步驟和方法應該是一樣的,調用的函數應該是相同的,是以圖中的三個打開檔案的<code>file</code>結構體指向同一個<code>file_operations</code>結構體。如果打開一個字元裝置檔案,那麼它的<code>read</code>、<code>write</code>操作肯定和正常檔案不一樣,不是讀寫磁盤的資料塊而是讀寫硬體裝置,是以<code>file</code>結構體應該指向不同的<code>file_operations</code>結構體,其中的各種檔案操作函數由該裝置的驅動程式實作。
每個<code>file</code>結構體都有一個指向<code>dentry</code>結構體的指針,“dentry”是directory entry(目錄項)的縮寫。我們傳給<code>open</code>、<code>stat</code>等函數的參數的是一個路徑,例如<code>/home/akaedu/a</code>,需要根據路徑找到檔案的inode。為了減少讀盤次數,核心緩存了目錄的樹狀結構,稱為dentry cache,其中每個節點是一個<code>dentry</code>結構體,隻要沿着路徑各部分的dentry搜尋即可,從根目錄<code>/</code>找到<code>home</code>目錄,然後找到<code>akaedu</code>目錄,然後找到檔案<code>a</code>。dentry cache隻儲存最近通路過的目錄項,如果要找的目錄項在cache中沒有,就要從磁盤讀到記憶體中。
每個<code>dentry</code>結構體都有一個指針指向<code>inode</code>結構體。<code>inode</code>結構體儲存着從磁盤inode讀上來的資訊。在上圖的例子中,有兩個dentry,分别表示<code>/home/akaedu/a</code>和<code>/home/akaedu/b</code>,它們都指向同一個inode,說明這兩個檔案互為硬連結。<code>inode</code>結構體中儲存着從磁盤分區的inode讀上來資訊,例如所有者、檔案大小、檔案類型和權限位等。每個<code>inode</code>結構體都有一個指向<code>inode_operations</code>結構體的指針,後者也是一組函數指針指向一些完成檔案目錄操作的核心函數。和<code>file_operations</code>不同,<code>inode_operations</code>所指向的不是針對某一個檔案進行操作的函數,而是影響檔案和目錄布局的函數,例如添加删除檔案和目錄、跟蹤符号連結等等,屬于同一檔案系統的各<code>inode</code>結構體可以指向同一個<code>inode_operations</code>結構體。
<code>inode</code>結構體有一個指向<code>super_block</code>結構體的指針。<code>super_block</code>結構體儲存着從磁盤分區的超級塊讀上來的資訊,例如檔案系統類型、塊大小等。<code>super_block</code>結構體的<code>s_root</code>成員是一個指向<code>dentry</code>的指針,表示這個檔案系統的根目錄被<code>mount</code>到哪裡,在上圖的例子中這個分區被<code>mount</code>到<code>/home</code>目錄下。
<code>file</code>、<code>dentry</code>、<code>inode</code>、<code>super_block</code>這幾個結構體組成了VFS的核心概念。對于ext2檔案系統來說,在磁盤存儲布局上也有inode和超級塊的概念,是以很容易和VFS中的概念建立對應關系。而另外一些檔案系統格式來自非UNIX系統(例如Windows的FAT32、NTFS),可能沒有inode或超級塊這樣的概念,但為了能<code>mount</code>到Linux系統,也隻好在驅動程式中硬湊一下,在Linux下看FAT32和NTFS檔案系統會發現權限位是錯的,所有檔案都是<code>rwxrwxrwx</code>,因為它們本來就沒有inode和權限位的概念,這是硬湊出來的。