在 UNIX 和 UNIX-like 作業系統中,檔案描述符 (File Descriptor, FD) 是一個用於標識檔案或其他輸入/輸出資源(如管道或網路套接字)的過程唯一標識符。這些檔案描述符的值通常為非負整數,負值用來指示“沒有值”或錯誤條件。檔案描述符是 POSIX API 的一部分,每個 UNIX 過程(除了一些守護進程)應有三個標準的 POSIX 檔案描述符,對應三個標準流:標準輸入、標準輸出和標準錯誤輸出。
在傳統的 UNIX 實現中,檔案描述符索引到由內核維護的每個過程的檔案描述符表,該表又索引到所有進程打開的檔案的系統範圍表,即檔案表。
檔案表記錄檔案的打開模式,如讀取、寫入、附加等。它還索引進入第三個表即 inode 表,該表描述了實際的底層檔案。在進行輸入或輸出時,進程會通過系統呼叫將檔案描述符傳遞給內核,內核則會代表該過程訪問檔案。進程無法直接訪問檔案或 inode 表。
在 Linux 中,可以通過路徑 /proc/PID/fd/ 訪問進程中打開的檔案描述符,這裡的 PID 是進程標識符。
UNIX-like 系統中的檔案描述符可以引用任何在檔案系統中命名的 UNIX 檔案類型,除了常規檔案外,這還包括目錄、區塊和字符設備(也稱為“特殊檔案”)、UNIX 域套接字以及命名管道。檔案描述符還可以引用其他在檔案系統中不常存在的對象,例如匿名管道和網路套接字。在 C 標準 I/O 庫中的 FILE 資料結構通常包括一個底層檔案描述符,這為所指對象提供了額外的抽象,因而稱之為檔案句柄。
現代 UNIX-like 系統中對檔案描述符的典型操作包括創建檔案描述符、單個檔案描述符的操作以及多個檔案描述符的操作等等。大多數這些函數在 <unistd.h>
標頭中聲明,但有些則在 <fcntl.h>
標頭中聲明。
常見的創建檔案描述符的方法包括 open()
、socket()
和 epoll_create()
等。如果您需要處理多個檔案描述符,這些函數使您能夠以高效的方式進行管理。
對單個檔案描述符進行的操作包括 read()
、write()
、lseek()
等。這些操作提供了對檔案的基本讀寫能力,從而使開發者能夠進行正常的資料處理。
在需要同時監控和處理多個檔案描述符的情況下,select()
、poll()
、epoll_wait()
和 kqueue()
等函數尤為重要。這些函數能讓開發者在高效的方式下處理大量的連接請求,尤其在網路伺服器中顯得尤為重要。
fcntl()
函數可以用於對檔案描述符執行各種操作。其命令參數可用來獲取和設置與檔案描述符相關的屬性,包括 F_GETFD
和 F_SETFD
等。
在處理大量檔案描述符時,epoll 和 kqueue 是兩種非常有效的系統調用。epoll
是 Linux 專有的解決方案,而 kqueue
則出現在 BSD 系統中。這兩者都能在龐大的檔案描述符集合中高效地尋找可用的檔案描述符,從而實現資料的讀寫。
epoll 的優勢在於其支持事件驅動的 I/O 模式,您可以在應用程序中註冊各種事件來監控不同的檔案描述符。
利用 epoll_ctl()
,用戶可以方便地添加、刪除和修改其註冊的檔案描述符,這改進了處理大量同時連接的效率。另一方面,kqueue
則通過事件通知機制,使得它在需要精細控制響應時間的應用中非常有效。
隨著應用需求的增加,系統設計者必須越來越多地考慮如何有效地管理檔案描述符,進而影響應用性能。如何選擇最適合自己應用的技術方案,已成為現代開發中的重要挑戰,您是否能夠在您的工作中應用這些技術來提升性能呢?