1. 概述
在linux系統中一切皆可以看成是檔案,檔案又可分為:普通檔案、目錄檔案、鏈結檔案和裝置檔案。檔案描述符(file descriptor)是核心為了高效管理已被開啟的檔案所建立的索引,其是乙個非負整數(通常是小整數),用於指代被開啟的檔案,所有執行i/o操作的系統呼叫都通過檔案描述符。程式剛剛啟動的時候,0是標準輸入,1是標準輸出,2是標準錯誤。如果此時去開啟乙個新的檔案,它的檔案描述符會是3。posix標準要求每次開啟檔案時(含socket)必須使用當前程序中最小可用的檔案描述符號碼,因此,在網路通訊過程中稍不注意就有可能造成串話。標準檔案描述符圖如下:
檔案描述與開啟的檔案對應模型如下圖:
2. 檔案描述限制在編寫檔案操作的或者網路通訊的軟體時,初學者一般可能會遇到「too many open files」的問題。這主要是因為檔案描述符是系統的乙個重要資源,雖然說系統記憶體有多少就可以開啟多少的檔案描述符,但是在實際實現過程中核心是會做相應的處理的,一般最大開啟檔案數會是系統記憶體的10%(以kb來計算)(稱之為系統級限制),檢視系統級別的最大開啟檔案數可以使用sysctl -a | grep fs.file-max命令檢視。與此同時,核心為了不讓某乙個程序消耗掉所有的檔案資源,其也會對單個程序最大開啟檔案數做預設值處理(稱之為使用者級限制),預設值一般是1024,使用ulimit -n命令可以檢視。在web伺服器中,通過更改系統預設值檔案描述符的最大值來優化伺服器是最常見的方式之一,具體優化方式請檢視
3. 檔案描述符合開啟檔案之間的關係
每乙個檔案描述符會與乙個開啟檔案相對應,同時,不同的檔案描述符也會指向同乙個檔案。相同的檔案可以被不同的程序開啟也可以在同乙個程序中被多次開啟。系統為每乙個程序維護了乙個檔案描述符表,該錶的值都是從0開始的,所以在不同的程序中你會看到相同的檔案描述符,這種情況下相同檔案描述符有可能指向同乙個檔案,也有可能指向不同的檔案。具體情況要具體分析,要理解具體其概況如何,需要檢視由核心維護的3個資料結構。
1. 程序級的檔案描述符表
2. 系統級的開啟檔案描述符表
3. 檔案系統的i-node表
程序級的描述符表的每一條目記錄了單個檔案描述符的相關資訊。
1. 控制檔案描述符操作的一組標誌。(目前,此類標誌僅定義了乙個,即close-on-exec標誌)
2. 對開啟檔案控制代碼的引用
核心對所有開啟的檔案的檔案維護有乙個系統級的描述符**(open file description table)。有時,也稱之為開啟檔案表(open file table),並將**中各條目稱為開啟檔案控制代碼(open file handle)。乙個開啟檔案控制代碼儲存了與乙個開啟檔案相關的全部資訊,如下所示:
1. 當前檔案偏移量(呼叫read()和write()時更新,或使用lseek()直接修改)
2. 開啟檔案時所使用的狀態標識(即,open()的flags引數)
3. 檔案訪問模式(如呼叫open()時所設定的唯讀模式、只寫模式或讀寫模式)
4. 與訊號驅動相關的設定
5. 對該檔案i-node物件的引用
6. 檔案型別(例如:常規檔案、套接字或fifo)和訪問許可權
7. 乙個指標,指向該檔案所持有的鎖列表
8. 檔案的各種屬性,包括檔案大小以及與不同型別操作相關的時間戳
下圖展示了檔案描述符、開啟的檔案控制代碼以及i-node之間的關係,圖中,兩個程序擁有諸多開啟的檔案描述符。
在程序a中,檔案描述符1和30都指向了同乙個開啟的檔案控制代碼(標號23)。這可能是通過呼叫dup()、dup2()、fcntl()或者對同乙個檔案多次呼叫了open()函式而形成的。
程序a的檔案描述符2和程序b的檔案描述符2都指向了同乙個開啟的檔案控制代碼(標號73)。這種情形可能是在呼叫fork()後出現的(即,程序a、b是父子程序關係),或者當某程序通過unix域套接字將乙個開啟的檔案描述符傳遞給另乙個程序時,也會發生。再者是不同的程序獨自去呼叫open函式開啟了同乙個檔案,此時程序內部的描述符正好分配到與其他程序開啟該檔案的描述符一樣。
此外,程序a的描述符0和程序b的描述符3分別指向不同的開啟檔案控制代碼,但這些控制代碼均指向i-node表的相同條目(1976),換言之,指向同乙個檔案。發生這種情況是因為每個程序各自對同乙個檔案發起了open()呼叫。同乙個程序兩次開啟同乙個檔案,也會發生類似情況。
4. 總結
1. 由於程序級檔案描述符表的存在,不同的程序中會出現相同的檔案描述符,它們可能指向同乙個檔案,也可能指向不同的檔案
2. 兩個不同的檔案描述符,若指向同乙個開啟檔案控制代碼,將共享同一檔案偏移量。因此,如果通過其中乙個檔案描述符來修改檔案偏移量(由呼叫read()、write()或lseek()所致),那麼從另乙個描述符中也會觀察到變化,無論這兩個檔案描述符是否屬於不同程序,還是同乙個程序,情況都是如此。
4. 檔案描述符標誌(即,close-on-exec)為程序和檔案描述符所私有。對這一標誌的修改將不會影響同一程序或不同程序中的其他檔案描述符 參考
[1]
[2]
[3]
[4] 《linux/unix系統程式設計手冊》
linux檔案描述符與開啟檔案的關係
1.概述 在linux系統中一切皆可以看成是檔案,檔案又可分為 普通檔案 目錄檔案 鏈結檔案和裝置檔案。檔案描述符 file descriptor 是核心為了高效管理已被開啟的檔案所建立的索引,其是乙個非負整數 通常是小整數 用於指代被開啟的檔案,所有執行i o操作的系統呼叫都通過檔案描述符。程式剛...
Linux中的檔案描述符與開啟檔案之間的關係
1.概述 在linux系統中一切皆可以看成是檔案,檔案又可分為 普通檔案 目錄檔案 鏈結檔案和裝置檔案。檔案描述符 file descriptor 是核心為了高效管理已被開啟的檔案所建立的索引,其是乙個非負整數 通常是小整數 用於指代被開啟的檔案,所有執行i o操作的系統呼叫都通過檔案描述符。程式剛...
Linux中的檔案描述符與開啟檔案之間的關係
核心 kernel 利用檔案描述符 file descriptor 來訪問檔案。檔案描述符是非負整數。開啟現存盤案或新建檔案時,核心會返回乙個檔案描述符。讀寫檔案也需要使用檔案描述符來指定待讀寫的檔案。1.概述 在linux系統中一切皆可以看成是檔案,檔案又可分為 普通檔案 目錄檔案 鏈結檔案和裝置...