當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > Linux文件描述符獲取方法及詳細(xì)介紹,這里讓你快速學(xué)習(xí)
內(nèi)核(kernel)利用文件描述符(file descriptor)來(lái)訪問(wèn)文件。文件描述符是非負(fù)整數(shù)。打開(kāi)現(xiàn)存文件或新建文件時(shí),內(nèi)核會(huì)返回一個(gè)文件描述符。讀寫(xiě)文件也需要使用文件描述符來(lái)指定待讀寫(xiě)的文件。
在Linux系統(tǒng)中一切皆可以看成是文件,文件又可分為:普通文件、目錄文件、鏈接文件和設(shè)備文件。文件描述符(file descriptor)是內(nèi)核為了高效管理已被打開(kāi)的文件所創(chuàng)建的索引,其是一個(gè)非負(fù)整數(shù)(通常是小整數(shù)),用于指代被打開(kāi)的文件,所有執(zhí)行I/O操作的系統(tǒng)調(diào)用都通過(guò)文件描述符。
如何創(chuàng)建文件描述符
進(jìn)程獲取文件描述符最常見(jiàn)的方法是通過(guò)本機(jī)子例程open或create獲取或者通過(guò)從父進(jìn)程繼承。后一種方法允許子進(jìn)程同樣能夠訪問(wèn)由父進(jìn)程使用的文件。文件描述符對(duì)于每個(gè)進(jìn)程一般是唯一的。當(dāng)用fork子例程創(chuàng)建某個(gè)子進(jìn)程時(shí),該子進(jìn)程會(huì)獲得其父進(jìn)程所有文件描述符的副本,這些文件描述符在執(zhí)行fork時(shí)打開(kāi)。在由fcntl、dup和dup2子例程復(fù)制或拷貝某個(gè)進(jìn)程時(shí),會(huì)發(fā)生同樣的復(fù)制過(guò)程。
程序剛剛啟動(dòng)的時(shí)候,0是標(biāo)準(zhǔn)輸入,1是標(biāo)準(zhǔn)輸出,2是標(biāo)準(zhǔn)錯(cuò)誤。如果此時(shí)去打開(kāi)一個(gè)新的文件,它的文件描述符會(huì)是3。POSIX標(biāo)準(zhǔn)要求每次打開(kāi)文件時(shí)(含socket)必須使用當(dāng)前進(jìn)程中最小可用的文件描述符號(hào)碼,因此,在網(wǎng)絡(luò)通信過(guò)程中稍不注意就有可能造成串話。標(biāo)準(zhǔn)文件描述符圖如下:
文件描述與打開(kāi)的文件對(duì)應(yīng)模型如下圖:
2. 文件描述限制
在編寫(xiě)文件操作的或者網(wǎng)絡(luò)通信的軟件時(shí),初學(xué)者一般可能會(huì)遇到“Too many open files”的問(wèn)題。這主要是因?yàn)槲募枋龇窍到y(tǒng)的一個(gè)重要資源,雖然說(shuō)系統(tǒng)內(nèi)存有多少就可以打開(kāi)多少的文件描述符,但是在實(shí)際實(shí)現(xiàn)過(guò)程中內(nèi)核是會(huì)做相應(yīng)的處理的,一般最大打開(kāi)文件數(shù)會(huì)是系統(tǒng)內(nèi)存的10%(以KB來(lái)計(jì)算)(稱之為系統(tǒng)級(jí)限制),查看系統(tǒng)級(jí)別的最大打開(kāi)文件數(shù)可以使用sysctl -a | grep fs.file-max命令查看。與此同時(shí),內(nèi)核為了不讓某一個(gè)進(jìn)程消耗掉所有的文件資源,其也會(huì)對(duì)單個(gè)進(jìn)程最大打開(kāi)文件數(shù)做默認(rèn)值處理(稱之為用戶級(jí)限制),默認(rèn)值一般是1024,使用ulimit -n命令可以查看。
3. 文件描述符合打開(kāi)文件之間的關(guān)系
每一個(gè)文件描述符會(huì)與一個(gè)打開(kāi)文件相對(duì)應(yīng),同時(shí),不同的文件描述符也會(huì)指向同一個(gè)文件。相同的文件可以被不同的進(jìn)程打開(kāi)也可以在同一個(gè)進(jìn)程中被多次打開(kāi)。系統(tǒng)為每一個(gè)進(jìn)程維護(hù)了一個(gè)文件描述符表,該表的值都是從0開(kāi)始的,所以在不同的進(jìn)程中你會(huì)看到相同的文件描述符,這種情況下相同文件描述符有可能指向同一個(gè)文件,也有可能指向不同的文件。具體情況要具體分析,要理解具體其概況如何,需要查看由內(nèi)核維護(hù)的3個(gè)數(shù)據(jù)結(jié)構(gòu)。
1. 進(jìn)程級(jí)的文件描述符表
2. 系統(tǒng)級(jí)的打開(kāi)文件描述符表
3. 文件系統(tǒng)的i-node表
進(jìn)程級(jí)的描述符表的每一條目記錄了單個(gè)文件描述符的相關(guān)信息。
1. 控制文件描述符操作的一組標(biāo)志。(目前,此類標(biāo)志僅定義了一個(gè),即close-on-exec標(biāo)志)
2. 對(duì)打開(kāi)文件句柄的引用
內(nèi)核對(duì)所有打開(kāi)的文件的文件維護(hù)有一個(gè)系統(tǒng)級(jí)的描述符表格(open file description table)。有時(shí),也稱之為打開(kāi)文件表(open file table),并將表格中各條目稱為打開(kāi)文件句柄(open file handle)。一個(gè)打開(kāi)文件句柄存儲(chǔ)了與一個(gè)打開(kāi)文件相關(guān)的全部信息,如下所示:
1. 當(dāng)前文件偏移量(調(diào)用read()和write()時(shí)更新,或使用lseek()直接修改)
2. 打開(kāi)文件時(shí)所使用的狀態(tài)標(biāo)識(shí)(即,open()的flags參數(shù))
3. 文件訪問(wèn)模式(如調(diào)用open()時(shí)所設(shè)置的只讀模式、只寫(xiě)模式或讀寫(xiě)模式)
4. 與信號(hào)驅(qū)動(dòng)相關(guān)的設(shè)置
5. 對(duì)該文件i-node對(duì)象的引用
6. 文件類型(例如:常規(guī)文件、套接字或FIFO)和訪問(wèn)權(quán)限
7. 一個(gè)指針,指向該文件所持有的鎖列表
8. 文件的各種屬性,包括文件大小以及與不同類型操作相關(guān)的時(shí)間戳
下圖展示了文件描述符、打開(kāi)的文件句柄以及i-node之間的關(guān)系,圖中,兩個(gè)進(jìn)程擁有諸多打開(kāi)的文件描述符。 在進(jìn)程A中,文件描述符1和30都指向了同一個(gè)打開(kāi)的文件句柄(標(biāo)號(hào)23)。這可能是通過(guò)調(diào)用dup()、dup2()、fcntl()或者對(duì)同一個(gè)文件多次調(diào)用了open()函數(shù)而形成的。
進(jìn)程A的文件描述符2和進(jìn)程B的文件描述符2都指向了同一個(gè)打開(kāi)的文件句柄(標(biāo)號(hào)73)。這種情形可能是在調(diào)用fork()后出現(xiàn)的(即,進(jìn)程A、B是父子進(jìn)程關(guān)系),或者當(dāng)某進(jìn)程通過(guò)UNIX域套接字將一個(gè)打開(kāi)的文件描述符傳遞給另一個(gè)進(jìn)程時(shí),也會(huì)發(fā)生。再者是不同的進(jìn)程獨(dú)自去調(diào)用open函數(shù)打開(kāi)了同一個(gè)文件,此時(shí)進(jìn)程內(nèi)部的描述符正好分配到與其他進(jìn)程打開(kāi)該文件的描述符一樣。
此外,進(jìn)程A的描述符0和進(jìn)程B的描述符3分別指向不同的打開(kāi)文件句柄,但這些句柄均指向i-node表的相同條目(1976),換言之,指向同一個(gè)文件。發(fā)生這種情況是因?yàn)槊總(gè)進(jìn)程各自對(duì)同一個(gè)文件發(fā)起了open()調(diào)用。同一個(gè)進(jìn)程兩次打開(kāi)同一個(gè)文件,也會(huì)發(fā)生類似情況。
優(yōu)點(diǎn):
文件描述符的好處主要有兩個(gè):
基于文件描述符的I/O操作兼容POSIX標(biāo)準(zhǔn)。
在UNIX、Linux的系統(tǒng)調(diào)用中,大量的系統(tǒng)調(diào)用都是依賴于文件描述符。
例如,下面的代碼就示范了如何基于文件描述符來(lái)讀取當(dāng)前目錄下的一個(gè)指定文件,并把文件內(nèi)容打印至Console中。
此外,在Linux系列的操作系統(tǒng)上,由于Linux的設(shè)計(jì)思想便是把一切設(shè)備都視作文件。因此,文件描述符為在該系列平臺(tái)上進(jìn)行設(shè)備相關(guān)的編程實(shí)際上提供了一個(gè)統(tǒng)一的方法。
總結(jié):
1. 由于進(jìn)程級(jí)文件描述符表的存在,不同的進(jìn)程中會(huì)出現(xiàn)相同的文件描述符,它們可能指向同一個(gè)文件,也可能指向不同的文件
2. 兩個(gè)不同的文件描述符,若指向同一個(gè)打開(kāi)文件句柄,將共享同一文件偏移量。因此,如果通過(guò)其中一個(gè)文件描述符來(lái)修改文件偏移量(由調(diào)用read()、write()或lseek()所致),那么從另一個(gè)描述符中也會(huì)觀察到變化,無(wú)論這兩個(gè)文件描述符是否屬于不同進(jìn)程,還是同一個(gè)進(jìn)程,情況都是如此。
3. 要獲取和修改打開(kāi)的文件標(biāo)志(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可執(zhí)行fcntl()的F_GETFL和F_SETFL操作,其對(duì)作用域的約束與上一條頗為類似。
4. 文件描述符標(biāo)志(即,close-on-exec)為進(jìn)程和文件描述符所私有。對(duì)這一標(biāo)志的修改將不會(huì)影響同一進(jìn)程或不同進(jìn)程中的其他文件描述符。