 Linux文件(jian)描述(shu)符獲取方法及詳細介紹,這里(li)讓(rang)你(ni)快(kuai)速學(xue)習
							時間:2018-06-19      來源:未(wei)知
							Linux文件(jian)描述(shu)符獲取方法及詳細介紹,這里(li)讓(rang)你(ni)快(kuai)速學(xue)習
							時間:2018-06-19      來源:未(wei)知 
							內核(kernel)利用(yong)文(wen)(wen)(wen)件(jian)(jian)描(miao)述符(fu)(fu)(file descriptor)來訪問文(wen)(wen)(wen)件(jian)(jian)。文(wen)(wen)(wen)件(jian)(jian)描(miao)述符(fu)(fu)是非負整數。打開現存文(wen)(wen)(wen)件(jian)(jian)或新建文(wen)(wen)(wen)件(jian)(jian)時(shi),內核會返(fan)回一個文(wen)(wen)(wen)件(jian)(jian)描(miao)述符(fu)(fu)。讀寫文(wen)(wen)(wen)件(jian)(jian)也需(xu)要使用(yong)文(wen)(wen)(wen)件(jian)(jian)描(miao)述符(fu)(fu)來指(zhi)定待(dai)讀寫的文(wen)(wen)(wen)件(jian)(jian)。
在(zai)Linux系統(tong)中一切皆(jie)可(ke)(ke)以看成是文件,文件又可(ke)(ke)分為:普通(tong)(tong)文件、目錄文件、鏈接文件和(he)設備文件。文件描(miao)述(shu)符(fu)(file descriptor)是內核為了高效管理已被打開的文件所創建的索引,其是一個非負整(zheng)數(通(tong)(tong)常是小整(zheng)數),用于指代被打開的文件,所有執行I/O操作(zuo)的系統(tong)調用都通(tong)(tong)過文件描(miao)述(shu)符(fu)。
如何創建文件描述(shu)符
進(jin)程獲(huo)(huo)取(qu)(qu)文(wen)件(jian)描(miao)述(shu)符(fu)最常見的方法是通過(guo)本(ben)機(ji)子(zi)例(li)程open或create獲(huo)(huo)取(qu)(qu)或者通過(guo)從父(fu)進(jin)程繼(ji)承(cheng)。后一種方法允(yun)許子(zi)進(jin)程同(tong)樣(yang)能夠(gou)訪問由父(fu)進(jin)程使用(yong)的文(wen)件(jian)。文(wen)件(jian)描(miao)述(shu)符(fu)對于(yu)每個(ge)進(jin)程一般是唯一的。當用(yong)fork子(zi)例(li)程創建某個(ge)子(zi)進(jin)程時(shi),該(gai)子(zi)進(jin)程會獲(huo)(huo)得其父(fu)進(jin)程所有文(wen)件(jian)描(miao)述(shu)符(fu)的副本(ben),這些(xie)文(wen)件(jian)描(miao)述(shu)符(fu)在執行fork時(shi)打開。在由fcntl、dup和dup2子(zi)例(li)程復(fu)制或拷貝某個(ge)進(jin)程時(shi),會發生同(tong)樣(yang)的復(fu)制過(guo)程。
程(cheng)序(xu)剛剛啟動的時候(hou),0是標準(zhun)輸(shu)入(ru),1是標準(zhun)輸(shu)出,2是標準(zhun)錯誤。如果此時去(qu)打(da)開(kai)一個新的文件(jian),它的文件(jian)描述符會是3。POSIX標準(zhun)要求每次打(da)開(kai)文件(jian)時(含socket)必須使用(yong)(yong)當前進程(cheng)中(zhong)(zhong)最小可用(yong)(yong)的文件(jian)描述符號(hao)碼,因(yin)此,在(zai)網(wang)絡通信過(guo)程(cheng)中(zhong)(zhong)稍不(bu)注意就有可能造成串(chuan)話。標準(zhun)文件(jian)描述符圖如下(xia):
 
文(wen)件描述與打開(kai)的文(wen)件對應(ying)模型(xing)如下圖:
 
2. 文(wen)件描(miao)述限(xian)制(zhi)
在編(bian)寫文(wen)(wen)件(jian)操作的(de)(de)(de)(de)或者網(wang)絡通(tong)信的(de)(de)(de)(de)軟件(jian)時,初學者一般(ban)可能會(hui)遇到“Too many open files”的(de)(de)(de)(de)問題。這(zhe)主要是因(yin)為文(wen)(wen)件(jian)描述符是系(xi)統(tong)的(de)(de)(de)(de)一個重(zhong)要資(zi)源,雖然說(shuo)系(xi)統(tong)內(nei)存(cun)有(you)(you)多少就可以(yi)打(da)開多少的(de)(de)(de)(de)文(wen)(wen)件(jian)描述符,但是在實際(ji)實現過程(cheng)中(zhong)內(nei)核(he)是會(hui)做相應的(de)(de)(de)(de)處(chu)理的(de)(de)(de)(de),一般(ban)最(zui)大打(da)開文(wen)(wen)件(jian)數(shu)會(hui)是系(xi)統(tong)內(nei)存(cun)的(de)(de)(de)(de)10%(以(yi)KB來計算(suan))(稱(cheng)之為系(xi)統(tong)級限(xian)制),查(cha)(cha)看(kan)(kan)系(xi)統(tong)級別的(de)(de)(de)(de)最(zui)大打(da)開文(wen)(wen)件(jian)數(shu)可以(yi)使用sysctl -a | grep fs.file-max命(ming)令(ling)查(cha)(cha)看(kan)(kan)。與此同時,內(nei)核(he)為了不讓某(mou)一個進程(cheng)消耗掉所有(you)(you)的(de)(de)(de)(de)文(wen)(wen)件(jian)資(zi)源,其也會(hui)對單個進程(cheng)最(zui)大打(da)開文(wen)(wen)件(jian)數(shu)做默(mo)認(ren)(ren)值處(chu)理(稱(cheng)之為用戶級限(xian)制),默(mo)認(ren)(ren)值一般(ban)是1024,使用ulimit -n命(ming)令(ling)可以(yi)查(cha)(cha)看(kan)(kan)。
3. 文(wen)件描述符合打(da)開文(wen)件之間的關系
每一(yi)(yi)個(ge)(ge)(ge)文(wen)(wen)件(jian)(jian)(jian)(jian)描述符(fu)會與一(yi)(yi)個(ge)(ge)(ge)打(da)開文(wen)(wen)件(jian)(jian)(jian)(jian)相對應,同(tong)時(shi),不同(tong)的(de)文(wen)(wen)件(jian)(jian)(jian)(jian)描述符(fu)也會指(zhi)(zhi)向(xiang)同(tong)一(yi)(yi)個(ge)(ge)(ge)文(wen)(wen)件(jian)(jian)(jian)(jian)。相同(tong)的(de)文(wen)(wen)件(jian)(jian)(jian)(jian)可以被(bei)不同(tong)的(de)進程(cheng)(cheng)打(da)開也可以在同(tong)一(yi)(yi)個(ge)(ge)(ge)進程(cheng)(cheng)中被(bei)多次打(da)開。系統為每一(yi)(yi)個(ge)(ge)(ge)進程(cheng)(cheng)維(wei)護了(le)一(yi)(yi)個(ge)(ge)(ge)文(wen)(wen)件(jian)(jian)(jian)(jian)描述符(fu)表,該表的(de)值(zhi)都是從0開始的(de),所以在不同(tong)的(de)進程(cheng)(cheng)中你會看到相同(tong)的(de)文(wen)(wen)件(jian)(jian)(jian)(jian)描述符(fu),這種情況下(xia)相同(tong)文(wen)(wen)件(jian)(jian)(jian)(jian)描述符(fu)有(you)可能指(zhi)(zhi)向(xiang)同(tong)一(yi)(yi)個(ge)(ge)(ge)文(wen)(wen)件(jian)(jian)(jian)(jian),也有(you)可能指(zhi)(zhi)向(xiang)不同(tong)的(de)文(wen)(wen)件(jian)(jian)(jian)(jian)。具體(ti)(ti)情況要(yao)具體(ti)(ti)分(fen)析,要(yao)理(li)解具體(ti)(ti)其概況如何,需(xu)要(yao)查看由內核維(wei)護的(de)3個(ge)(ge)(ge)數據結(jie)構。
1. 進程級的文(wen)件描述符(fu)表(biao)
2. 系統(tong)級的打(da)開(kai)文件(jian)描述(shu)符(fu)表
3. 文件系統的i-node表
進(jin)程級的(de)描述(shu)符表的(de)每一條目記錄(lu)了單個文件描述(shu)符的(de)相關信息。
1. 控制文件描述(shu)符(fu)操作的一(yi)組標(biao)志。(目前,此類標(biao)志僅定義了(le)一(yi)個,即close-on-exec標(biao)志)
2. 對打開(kai)文(wen)件(jian)句柄的引(yin)用
內核(he)對(dui)所(suo)有(you)(you)打(da)開(kai)(kai)的(de)(de)文件(jian)的(de)(de)文件(jian)維護(hu)有(you)(you)一(yi)(yi)個(ge)系統級的(de)(de)描述符表(biao)格(open file description table)。有(you)(you)時,也稱之(zhi)為(wei)打(da)開(kai)(kai)文件(jian)表(biao)(open file table),并將表(biao)格中(zhong)各條(tiao)目(mu)稱為(wei)打(da)開(kai)(kai)文件(jian)句柄(bing)(open file handle)。一(yi)(yi)個(ge)打(da)開(kai)(kai)文件(jian)句柄(bing)存儲了與一(yi)(yi)個(ge)打(da)開(kai)(kai)文件(jian)相關的(de)(de)全部信(xin)息,如下所(suo)示:
1. 當(dang)前文件(jian)偏移(yi)量(調用read()和write()時更(geng)新,或使用lseek()直(zhi)接(jie)修改)
2. 打開文件時所使用的(de)狀態標識(即,open()的(de)flags參數(shu))
3. 文(wen)件訪問模(mo)(mo)式(如調用open()時所設置的只讀模(mo)(mo)式、只寫模(mo)(mo)式或讀寫模(mo)(mo)式)
4. 與信號驅動(dong)相關的設置
5. 對該文件i-node對象的引用
6. 文件(jian)類型(例如:常(chang)規文件(jian)、套(tao)接字或FIFO)和訪問(wen)權限
7. 一個指針,指向該文件所(suo)持(chi)有的鎖(suo)列表(biao)
8. 文件(jian)的各(ge)種屬性,包括(kuo)文件(jian)大小以(yi)及與不同類型操作相關(guan)的時間戳(chuo)
 
下圖展示了(le)文(wen)件(jian)描述符、打(da)開的(de)(de)文(wen)件(jian)句(ju)柄以(yi)及i-node之間的(de)(de)關系,圖中,兩個進程擁有諸多(duo)打(da)開的(de)(de)文(wen)件(jian)描述符。 在進程A中,文(wen)件(jian)描述符1和30都指向了(le)同(tong)一(yi)個打(da)開的(de)(de)文(wen)件(jian)句(ju)柄(標號23)。這可能是通過(guo)調用(yong)dup()、dup2()、fcntl()或者(zhe)對(dui)同(tong)一(yi)個文(wen)件(jian)多(duo)次調用(yong)了(le)open()函數而(er)形成的(de)(de)。
進(jin)程(cheng)(cheng)A的(de)文(wen)件(jian)描(miao)述符(fu)(fu)2和(he)進(jin)程(cheng)(cheng)B的(de)文(wen)件(jian)描(miao)述符(fu)(fu)2都指向了同(tong)一(yi)個打(da)開的(de)文(wen)件(jian)句柄(標號73)。這(zhe)種情(qing)形(xing)可能是(shi)在調(diao)用fork()后出現的(de)(即,進(jin)程(cheng)(cheng)A、B是(shi)父子進(jin)程(cheng)(cheng)關系),或者當某進(jin)程(cheng)(cheng)通過UNIX域套接字將(jiang)一(yi)個打(da)開的(de)文(wen)件(jian)描(miao)述符(fu)(fu)傳遞給另一(yi)個進(jin)程(cheng)(cheng)時,也會發生(sheng)。再者是(shi)不(bu)同(tong)的(de)進(jin)程(cheng)(cheng)獨自去調(diao)用open函數打(da)開了同(tong)一(yi)個文(wen)件(jian),此時進(jin)程(cheng)(cheng)內部(bu)的(de)描(miao)述符(fu)(fu)正好分(fen)配到與其他(ta)進(jin)程(cheng)(cheng)打(da)開該文(wen)件(jian)的(de)描(miao)述符(fu)(fu)一(yi)樣。
此(ci)外,進(jin)程(cheng)A的(de)描述符0和進(jin)程(cheng)B的(de)描述符3分別指(zhi)向不同的(de)打開文件(jian)(jian)句(ju)柄(bing),但(dan)這些句(ju)柄(bing)均指(zhi)向i-node表的(de)相同條(tiao)目(1976),換言之(zhi),指(zhi)向同一(yi)個(ge)文件(jian)(jian)。發生這種情況(kuang)是因(yin)為每個(ge)進(jin)程(cheng)各(ge)自對同一(yi)個(ge)文件(jian)(jian)發起了(le)open()調用。同一(yi)個(ge)進(jin)程(cheng)兩次打開同一(yi)個(ge)文件(jian)(jian),也會發生類似情況(kuang)。
優點:
文件(jian)描(miao)述符的好處主(zhu)要(yao)有兩個(ge):
基(ji)于文件描(miao)述符的I/O操作兼(jian)容POSIX標準。
在UNIX、Linux的系統調用中,大量的系統調用都是依賴于文件描述符。
例(li)如,下面的代(dai)碼就示范了如何基于文(wen)件(jian)描述符(fu)來讀取當(dang)前目(mu)錄下的一個指定文(wen)件(jian),并把文(wen)件(jian)內(nei)容打(da)印至Console中(zhong)。
此外,在(zai)Linux系列的(de)操(cao)作系統(tong)上,由(you)于Linux的(de)設(she)計(ji)思想便(bian)是把(ba)一(yi)(yi)切設(she)備(bei)(bei)都視作文件。因(yin)此,文件描述符為在(zai)該系列平(ping)臺上進行設(she)備(bei)(bei)相關(guan)的(de)編程實際上提供了一(yi)(yi)個(ge)統(tong)一(yi)(yi)的(de)方法(fa)。
總結:
1. 由于進程級(ji)文件描述符表的(de)存(cun)在,不同(tong)的(de)進程中會出(chu)現(xian)相同(tong)的(de)文件描述符,它們可(ke)(ke)能指向(xiang)同(tong)一個文件,也可(ke)(ke)能指向(xiang)不同(tong)的(de)文件
2. 兩個不同(tong)的文件(jian)描(miao)述符(fu)(fu),若指向同(tong)一(yi)個打開文件(jian)句柄,將共享同(tong)一(yi)文件(jian)偏(pian)移量(liang)。因此,如果通(tong)過(guo)其中一(yi)個文件(jian)描(miao)述符(fu)(fu)來修改文件(jian)偏(pian)移量(liang)(由調(diao)用(yong)read()、write()或lseek()所致),那么(me)從(cong)另一(yi)個描(miao)述符(fu)(fu)中也會(hui)觀察到變(bian)化,無論這兩個文件(jian)描(miao)述符(fu)(fu)是否屬于不同(tong)進(jin)程,還(huan)是同(tong)一(yi)個進(jin)程,情況都是如此。
3. 要獲取和(he)修改(gai)打開的(de)文件(jian)標志(例如:O_APPEND、O_NONBLOCK和(he)O_ASYNC),可執行fcntl()的(de)F_GETFL和(he)F_SETFL操作(zuo),其對作(zuo)用域的(de)約束與上(shang)一條(tiao)頗為類似。
4. 文件描述符標志(即,close-on-exec)為進程和文件描述符所私有(you)。對這一(yi)標志的修改將不會影(ying)響同一(yi)進程或不同進程中(zhong)的其(qi)他(ta)文件描述符。