在Linux通用I/O模型中,I/O操作系列函數(系統調用)都是圍繞一個叫做文件描述符的整數展開。這不禁讓人產生疑問:這個整數代表什麼?一個數值代表一個文件嗎?隨便傳一個整數進去調用可以嗎? ...
在Linux通用I/O模型中,I/O
操作系列函數(系統調用)都是圍繞一個叫做文件描述符的整數展開。這不禁讓人產生疑問:這個整數代表什麼?一個數值代表一個文件嗎?隨便傳一個整數進去調用可以嗎?
原文地址:https://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
訂閱更新,獲取更多學習資料,請關註我們的 微信公眾號 :