和塊位圖類似,本身占一個塊,其中每個bit表示一個inode是否空閑可用。
我們知道,一個文件除了數據需要存儲之外,一些描述信息也需要存儲,例如文件類型(常規、目錄、符號鏈接等),權限,文件大小,創建/修改/訪問時間等,也就是ls -l
命令看到的那些信息,這些信息存在inode中而不是數據塊中。每個文件都有一個inode,一個塊組中的所有inode組成了inode表。
inode表占多少個塊在格式化時就要決定並寫入塊組描述符中,mke2fs
格式化工具的默認策略是一個塊組有多少個8KB就分配多少個inode。由於數據塊占了整個塊組的絕大部分,也可以近似認為數據塊有多少個8KB就分配多少個inode,換句話說,如果平均每個文件的大小是8KB,當分區存滿的時候inode表會得到比較充分的利用,數據塊也不浪費。如果這個分區存的都是很大的文件(比如電影),則數據塊用完的時候inode會有一些浪費,如果這個分區存的都是很小的文件(比如源代碼),則有可能數據塊還沒用完inode就已經用完了,數據塊可能有很大的浪費。如果用戶在格式化時能夠對這個分區以后要存儲的文件大小做一個預測,也可以用mke2fs
的-i
參數手動指定每多少個字節分配一個inode。
根據不同的文件類型有以下幾種情況
-
對於常規文件,文件的數據存儲在數據塊中。
-
對於目錄,該目錄下的所有文件名和目錄名存儲在數據塊中,注意文件名保存在它所在目錄的數據塊中,除文件名之外,
ls -l
命令看到的其它信息都保存在該文件的inode中。注意這個概念:目錄也是一種文件,是一種特殊類型的文件。 -
對於符號鏈接,如果目標路徑名較短則直接保存在inode中以便更快地查找,如果目標路徑名較長則分配一個數據塊來保存。
-
設備文件、FIFO和socket等特殊文件沒有數據塊,設備文件的主設備號和次設備號保存在inode中。
內核數據結構
Linux內核的VFS子系統可以圖示如下:
圖 29.8. VFS
在第 28 章 文件與I/O中講過,每個進程在PCB(Process Control Block)中都保存着一份文件描述符表,文件描述符就是這個表的索引,每個表項都有一個指向已打開文件的指針,現在我們明確一下:已打開的文件在內核中用file
結構體表示,文件描述符表中的指針指向file
結構體。
在file
結構體中維護File Status Flag(file
結構體的成員f_flags
)和當前讀寫位置(file
結構體的成員f_pos
)。在上圖中,進程1和進程2都打開同一文件,但是對應不同的file
結構體,因此可以有不同的File Status Flag和讀寫位置。file
結構體中比較重要的成員還有f_count
,表示引用計數(Reference Count),后面我們會講到,dup
、fork
等系統調用會導致多個文件描述符指向同一個file
結構體,例如有fd1
和fd2
都引用同一個file
結構體,那么它的引用計數就是2,當close(fd1)
時並不會釋放file
結構體,而只是把引用計數減到1,如果再close(fd2)
,引用計數就會減到0同時釋放file
結構體,這才真的關閉了文件。
每個file
結構體都指向一個file_operations
結構體,這個結構體的成員都是函數指針,指向實現各種文件操作的內核函數。比如在用戶程序中read
一個文件描述符,read
通過系統調用進入內核,然后找到這個文件描述符所指向的file
結構體,找到file
結構體所指向的file_operations
結構體,調用它的read
成員所指向的內核函數以完成用戶請求。在用戶程序中調用lseek
、read
、write
、ioctl
、open
等函數,最終都由內核調用file_operations
的各成員所指向的內核函數完成用戶請求。file_operations
結構體中的release
成員用於完成用戶程序的close
請求,之所以叫release
而不叫close
是因為它不一定真的關閉文件,而是減少引用計數,只有引用計數減到0才關閉文件。對於同一個文件系統上打開的常規文件來說,read
、write
等文件操作的步驟和方法應該是一樣的,調用的函數應該是相同的,所以圖中的三個打開文件的file
結構體指向同一個file_operations
結構體。如果打開一個字符設備文件,那么它的read
、write
操作肯定和常規文件不一樣,不是讀寫磁盤的數據塊而是讀寫硬件設備,所以file
結構體應該指向不同的file_operations
結構體,其中的各種文件操作函數由該設備的驅動程序實現。
每個file
結構體都有一個指向dentry
結構體的指針,“dentry”是directory entry(目錄項)的縮寫。我們傳給open
、stat
等函數的參數的是一個路徑,例如/home/akaedu/a
,需要根據路徑找到文件的inode。為了減少讀盤次數,內核緩存了目錄的樹狀結構,稱為dentry cache,其中每個節點是一個dentry
結構體,只要沿着路徑各部分的dentry搜索即可,從根目錄/
找到home
目錄,然后找到akaedu
目錄,然后找到文件a
。dentry cache只保存最近訪問過的目錄項,如果要找的目錄項在cache中沒有,就要從磁盤讀到內存中。
每個dentry
結構體都有一個指針指向inode
結構體。inode
結構體保存着從磁盤inode讀上來的信息。在上圖的例子中,有兩個dentry,分別表示/home/akaedu/a
和/home/akaedu/b
,它們都指向同一個inode,說明這兩個文件互為硬鏈接。inode
結構體中保存着從磁盤分區的inode讀上來信息,例如所有者、文件大小、文件類型和權限位等。每個inode
結構體都有一個指向inode_operations
結構體的指針,后者也是一組函數指針指向一些完成文件目錄操作的內核函數。和file_operations
不同,inode_operations
所指向的不是針對某一個文件進行操作的函數,而是影響文件和目錄布局的函數,例如添加刪除文件和目錄、跟蹤符號鏈接等等,屬於同一文件系統的各inode
結構體可以指向同一個inode_operations
結構體。
inode
結構體有一個指向super_block
結構體的指針。super_block
結構體保存着從磁盤分區的超級塊讀上來的信息,例如文件系統類型、塊大小等。super_block
結構體的s_root
成員是一個指向dentry
的指針,表示這個文件系統的根目錄被mount
到哪里,在上圖的例子中這個分區被mount
到/home
目錄下。
file
、dentry
、inode
、super_block
這幾個結構體組成了VFS的核心概念。對於ext2文件系統來說,在磁盤存儲布局上也有inode和超級塊的概念,所以很容易和VFS中的概念建立對應關系。而另外一些文件系統格式來自非UNIX系統(例如Windows的FAT32、NTFS),可能沒有inode或超級塊這樣的概念,但為了能mount
到Linux系統,也只好在驅動程序中硬湊一下,在Linux下看FAT32和NTFS分區會發現權限位是錯的,所有文件都是rwxrwxrwx
,因為它們本來就沒有inode和權限位的概念,這是硬湊出來的。
參考: Linux C編程一站式學習