歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> 每天進步一點點——Linux中的文件描述符與打開文件之間的關系

每天進步一點點——Linux中的文件描述符與打開文件之間的關系

日期:2017/3/3 12:53:54   编辑:Linux技術
轉載請說明出處:/content/6643583.html
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服務器中,通過更改系統默認值文件描述符的最大值來優化服務器是最常見的方式之一,具體優化方式請查看/content/2131443.html。
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()所致),那麼從另一個描述符中也會觀察到變化,無論這兩個文件描述符是否屬於不同進程,還是同一個進程,情況都是如此。
3. 要獲取和修改打開的文件標志(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可執行fcntl()的F_GETFL和F_SETFL操作,其對作用域的約束與上一條頗為類似。
4. 文件描述符標志(即,close-on-exec)為進程和文件描述符所私有。對這一標志的修改將不會影響同一進程或不同進程中的其他文件描述符
參考
[1] http://blog.chinaunix.net/uid-20633888-id-2747146.html
[2] http://www.cppblog.com/guojingjia2006/archive/2012/11/21/195450.html
[3] /content/2131443.html
[4] 《Linux/UNIX系統編程手冊》
Copyright © Linux教程網 All Rights Reserved