在Linux通用I/O模型中,I/O
操作系列函式(系統呼叫)都是圍繞一個叫做檔案描述符的整數展開。這不禁讓人產生疑問:這個整數代表什麼?一個數值代表一個檔案嗎?隨便傳一個整數進去呼叫可以嗎?
原文地址:learn-linux.readthedocs.io 。
玩轉Linux舊群已滿,請加新群:278378501 。
歡迎關注我們的公眾號:小菜學程式設計 (coding-fan)
解答以上疑問,需要更深入學習——檔案描述符(File Descriptor)。
圖解
理解具體情況,需要了解由核心維護的3個資料結構:
- 程式級檔案描述符表(file descriptor table)
- 系統級開啟檔案表(open file table)
- 檔案系統i-node表(i-node table)
這3個資料結構之間的關係如下圖所示:
檔案描述符表
核心為每個程式維護一個檔案描述符表,該表每一條目都記錄了單個檔案描述符的相關資訊,包括:
- 控制標誌(flags),目前核心僅定義了一個,即
close-on-exec
- 開啟檔案描述體指標
開啟檔案表
核心對所有開啟的檔案維護一個系統級別的開啟檔案描述表(open file description table),簡稱開啟檔案表。表中條目稱為開啟檔案描述體(open file description),儲存了與一個開啟檔案相關的全部資訊,包括:
- 檔案偏移量(file offset),呼叫
read()
和write()
更新,呼叫lseek()
直接修改 - 訪問模式,由
open()
呼叫設定,例如:只讀、只寫或讀寫等 i-node
物件指標
i-node表
每個檔案系統會為儲存於其上的所有檔案(包括目錄)維護一個i-node
表,單個i-node
包含以下資訊:
- 檔案型別(file type),可以是常規檔案、目錄、套接字或
FIFO
- 訪問許可權
- 檔案鎖列表(file locks)
- 檔案大小
- 等等
i-node
儲存在磁碟裝置上,核心在記憶體中維護了一個副本,這裡的**i-node
表**為後者。副本除了原有資訊,還包括:引用計數(從開啟檔案描述體)、所在裝置號以及一些臨時屬性,例如檔案鎖。
場景解析
上圖中,詳細描述了兩個程式諸多檔案描述符,以及相互關係。
檔案描述符複製
在程式A
中,檔案描述符1和檔案描述符20都指向同一個開啟檔案描述體(標號23)。這很可能是通過呼叫dup()
系列函式形成的。
檔案描述符複製,在某些場景下非常有用,比如:標準輸入/輸出重定向。在shell
下,完成這個操作非常簡單,大部分人都會,但是極少人思考過背後的原理。
大概描述一下需要的幾個步驟,以標準輸出(檔案描述符為1)重定向為例:
- 開啟目標檔案,返回檔案描述符n;
- 關閉檔案描述符1;
- 呼叫
dup
將檔案描述符n複製到1; - 關閉檔案描述符n;
子程式繼承檔案描述符
程式A
的檔案描述符2和程式B
的檔案描述符2都指向同一個開啟檔案描述體(標號73)。這種情形很可能發生在呼叫fork()
派生子程式之後,比如A
呼叫fork()
派生出B
。這時,B
作為子程式,從父程式A
繼承了檔案描述符表,其中包括圖中標明的檔案描述符2。這就是子程式繼承父程式開啟的檔案
這句話的由來。
當然了,程式A
通過Unix
套接字將一個檔案描述符傳遞給B
也會出現類似的情形,但一般檔案描述符數值是不一樣的。同時為2要非常湊巧才發生。
下一步
更多文章請訪問:學習Linux 。
訂閱更新,獲取更多學習資料,請關注我們的 微信公眾號 :