天天看點

Linux中的檔案描述符與打開檔案之間的關系

1. 概述

    在Linux系統中一切皆可以看成是檔案,檔案又可分為:普通檔案、目錄檔案、連結檔案和裝置檔案。檔案描述符(file descriptor)是核心為了高效管理已被打開的檔案所建立的索引,其是一個非負整數(通常是小整數),用于指代被打開的檔案,所有執行I/O操作的系統調用都通過檔案描述符。程式剛剛啟動的時候,0是标準輸入,1是标準輸出,2是标準錯誤。如果此時去打開一個新的檔案,它的檔案描述符會是3。POSIX标準要求每次打開檔案時(含socket)必須使用目前程序中最小可用的檔案描述符号碼,是以,在網絡通信過程中稍不注意就有可能造成串話。标準檔案描述符圖如下:

Linux中的檔案描述符與打開檔案之間的關系

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

Linux中的檔案描述符與打開檔案之間的關系

2. 檔案描述限制

    在編寫檔案操作的或者網絡通信的軟體時,初學者一般可能會遇到“Too many open files”的問題。這主要是因為檔案描述符是系統的一個重要資源,雖然說系統記憶體有多少就可以打開多少的檔案描述符,但是在實際實作過程中核心是會做相應的處理的,一般最大打開檔案數會是系統記憶體的10%(以KB來計算)(稱之為系統級限制),檢視系統級别的最大打開檔案數可以使用sysctl -a | grep fs.file-max指令檢視。與此同時,核心為了不讓某一個程序消耗掉所有的檔案資源,其也會對單個程序最大打開檔案數做預設值處理(稱之為使用者級限制),預設值一般是1024,使用ulimit -n指令可以檢視。在Web伺服器中,通過更改系統預設值檔案描述符的最大值來優化伺服器是最常見的方式之一,如CentOS6.6系統下的檔案描述符優化:

## 檢視預設檔案描述符的大小
[root@poe ~]# ulimit -n
1024
      

臨時修改檔案描述符的大小

[root@Gin scripts]# ulimit -SHn 65535
[root@Gin scripts]# ulimit -n
65535
      

永久修改檔案描述符的大小:

[root@Gin ~]# echo '*               -       nofile          65535' >>/etc/security/limits.conf 
[root@Gin ~]# tail -n1 /etc/security/limits.conf
*               -       nofile          65535
      

更多具體優化方式請檢視http://blog.csdn.net/kumu_linux/article/details/7877770。

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之間的關系,圖中,兩個程序擁有諸多打開的檔案描述符。

Linux中的檔案描述符與打開檔案之間的關系

在程序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)為程序和檔案描述符所私有。對這一标志的修改将不會影響同一程序或不同程序中的其他檔案描述符

本文轉自:http://blog.csdn.net/cywosp/article/details/38965239