在Unix和類Unix的操作系統中,檔案描述符是進程唯一的標識符,專門用來處理檔案或其他輸入/輸出資源,如管道或網絡套接字。這些檔案描述符通常為非負整數值,負值則用來表示「無值」或錯誤條件。這項技術已成為POSIX API的一部分,對於每個Unix進程來說,除了某些守護進程之外,都應擁有三個標準POSIX檔案描述符,這三個檔案描述符分別對應於標準輸入、標準輸出和標準錯誤流。
在傳統的Unix實現中,檔案描述符索引到由內核維護的每個進程的檔案描述符表,該表進而索引到系統範圍內的由所有進程打開的檔案的表,稱為檔案表。
檔案表記錄了檔案或其他資源的打開模式,包括讀取、寫入、附加等模式。此外,它還索引到一個稱為inode表的第三個表,該表描述了實際的底層檔案。要執行輸入或輸出,進程通過系統調用將檔案描述符傳遞給內核,內核將代進程存取檔案。需要注意的是,進程無法直接存取檔案或inode表。在Linux中,可以在路徑/proc/PID/fd/下訪問一個進程所打開的檔案描述符,其中PID是進程識別碼。
/proc/PID/fd/0是標準輸入,/proc/PID/fd/1是標準輸出,/proc/PID/fd/2是標準錯誤輸出。
在顯示的目錄下,任何運行中的進程也可以通過/proc/self/fd和/dev/fd文件夾訪問自己的檔案描述符。Unix類系統中的檔案描述符可以指向檔案系統中任何命名的Unix檔案類型,這不僅包括普通檔案,還涉及目錄、區塊設備和字符設備(也稱為特殊檔案)、Unix域套接字和命名管道。
檔案描述符還可以指向一些並非通常存在於檔案系統中的對象,例如匿名管道和網絡套接字。在Unix類系統中,C標準I/O庫中的FILE數據結構通常包含一個低級檔案描述符,該數據結構提供了額外的抽象,並被稱為檔案句柄。
在現代Unix類系統上,對檔案描述符的操作可分為幾類。大多數這些功能在<unistd.h>
標頭中聲明,但某些則在<fcntl.h>
標頭中定義。
創建檔案描述符的常用函數包括open()
、socket()
、pipe()
等。這些函數使得用戶可以根據需求創建新的檔案或資源以進行操作。
常見的單個檔案描述符操作包括read()
、write()
、lseek()
等。這些操作允許用戶讀取或寫入檔案數據,或者修改檔案的讀寫位置。
在需要處理多個檔案描述符的場合,可以使用select()
、poll()
等函數進行監聽,這在網絡通訊和多進程應用中特別重要。
Unix檔案描述符在許多方面表現出能力的特性。它們可以通過Unix域套接字在進程之間傳遞,使用sendmsg()
系統調用。然而,真正傳遞的是對「打開檔案描述」的引用,這有狀態(如檔案偏移量和檔案狀態標誌),這使得安全地使用這些檔案描述符變得複雜。
Unix進程的檔案描述符表是能力列表(C-list)的例子。
總體來說,檔案描述符不僅是系統對資源管理的一種重要手段,同時也展現了它們在進程間協作中的核心作用。
當然,這一切激發了一個重要的問題:在未來的技術發展下,檔案描述符的角色是否會隨著捕獲新型態的資料流和更複雜的系統架構而演變呢?