1. 程式人生 > 其它 >檔案描述符與socket連線

檔案描述符與socket連線

目錄

一.簡介

每個程序開啟一個soeket連線,都會佔用一個檔案描述符。

檔案描述符的唯一性是程序+檔案描述符ID確定的。

在Linux系統中一切皆可以看成是檔案,檔案又可分為:普通檔案、目錄檔案、連結檔案和裝置檔案。

檔案描述符(file descriptor)是核心為了高效管理已被開啟的檔案所建立的索引,其是一個非負整數(通常是小整數),用於指代被開啟的檔案,所有執行I/O操作(包括網路socket操作)的系統呼叫都通過檔案描述符。

程式剛剛啟動的時候,0是標準輸入,1是標準輸出,2是標準錯誤。如果此時去開啟一個新的檔案,它的檔案描述符會是3。POSIX標準要求每次開啟檔案時(含socket)必須使用當前程序中最小可用的檔案描述符號碼,因此,在網路通訊過程中稍不注意就有可能造成串話。標準檔案描述符圖如下:

檔案描述與開啟的檔案對應模型如下圖:

二.檔案描述限制

在編寫檔案操作的或者網路通訊的軟體時,初學者一般可能會遇到“Too many open files”的問題。這主要是因為檔案描述符是系統的一個重要資源,雖然說系統記憶體有多少就可以開啟多少的檔案描述符。

但是在實際實現過程中核心是會做相應的處理的,一般最大開啟檔案數會是系統記憶體的10%(以KB來計算)(稱之為系統級限制),檢視系統級別的最大開啟檔案數可以使用sysctl -a | grep fs.file-max命令檢視。

與此同時,核心為了不讓某一個程序消耗掉所有的檔案資源,其也會對單個程序最大開啟檔案數做預設值處理(稱之為使用者級限制),預設值一般是1024,使用ulimit -n命令可以檢視使用者級檔案描述符。在Web伺服器中,通過更改系統預設值檔案描述符的最大值來優化伺服器是最常見的方式之一。

三.核心維護的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()呼叫。同一個程序兩次開啟同一個檔案,也會發生類似情況。

四.總結

  1. 由於程序級檔案描述符表的存在,不同的程序中會出現相同的檔案描述符,它們可能指向同一個檔案,也可能指向不同的檔案
  2. 兩個不同的檔案描述符,若指向同一個開啟檔案控制代碼,將共享同一檔案偏移量。因此,如果通過其中一個檔案描述符來修改檔案偏移量(由呼叫read()、write()或lseek()所致),那麼從另一個描述符中也會觀察到變化,無論這兩個檔案描述符是否屬於不同程序,還是同一個程序,情況都是如此。
  3. 要獲取和修改開啟的檔案標誌(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可執行fcntl()的F_GETFL和F_SETFL操作,其對作用域的約束與上一條頗為類似。
  4. 檔案描述符標誌(即,close-on-exec)為程序和檔案描述符所私有。對這一標誌的修改將不會影響同一程序或不同程序中的其他檔案描述符
本文版權歸作者所有,歡迎轉載,請務必新增原文連結。