最近由于機緣巧合,結合最近工作中遇到的一些問題,深入了解了檔案描述符(File Descriptor,簡稱FD,以下使用 FD 稱謂)。預計會有兩到三篇關于 FD 的文章陸續出來。首篇也就是這篇,作為基礎篇,介紹一些關于通用 FD 的内容知識。
概念定義
- 檔案描述符 是 用來通路資源(檔案,輸入輸出裝置等)的一種抽象訓示符。
- 檔案描述符 是POSIX(Portable Operating System Interface)規範的組成部分
- 檔案描述符 通常是非負整數,C 語言中使用int類型。
FD 具體可以指向什麼
- 檔案/目錄 files/directories
- 輸入輸出源 input/output
- 管道 pipes
- 套接字 sockets
- 其他 Unix 檔案類型 other Unix files
系統預設的FDs
每一個 Unix 程序中,通常會有三個預制的 FD。它們分别是
- 标準輸入 Standard input
- 标準輸出 Standard output
- 标準錯誤(輸出) Standard error
其對應的行為是
- 标準輸入 用于程式接受資料
- 标準輸出 用于程式輸出資料
- 标準錯誤 用于程式輸出錯誤或者診斷資訊
内部機制
三張表
如上圖從左至右有三張表
- file descriptor table 歸屬于單個程序
- global file table(又稱open file table) 歸屬于系統全局
- inode table 歸屬于系統全局
從一次檔案打開說起
當我們嘗試打開檔案
/path/myfile.txt
1.從inode table 中查找到對應的檔案節點
2.根據使用者代碼
open
的一些參數(比如讀寫權限等)在open file table 中建立open file 節點
3.将上一步的open file節點資訊儲存,在file descriptor table中建立 file descriptor
4.傳回上一步的file descriptor的索引位置,供應用讀寫等使用。
備注:上述圖檔來自https://www.computerhope.com/jargon/f/file-descriptor.htm
FD 數量限制
出于穩定系統性能和避免因為過多打開檔案導緻CPU和RAM占用居高的考慮,系統都會設定了一個最大可用的 FD 數量。
FD上限值通常不小,一般應用很難達到。
限制類型
- hard limit 由系統管理權限人員設定,是soft limit 可以設定的上限
- soft limit 目前使用者設定,用來限定程序,通常小于(但不能超過)hard limit值。
1 2 3 4 5 6 7 8 | #檢視soft limit 設定 ➜ /tmp ulimit -nS 4864 #檢視 hard limit 設定 ➜ /tmp ulimit -nH unlimited |
---|
Questions
程序退出與 FD 關系
因為file descriptor table 存在于 PCB (程序控制塊,Process Control Block) 中,程序退出後所有的 FD都需要關閉處理掉。
如下為POSIX文檔
All of the file descriptors, directory streams, conversion descriptors, and message catalog descriptors open in the calling process shall be closed.
同一路徑 與 FD 關系
- 同一檔案,多次打開,FD值不同
- 同一檔案,讀寫模式不同打開,FD值也不同
打開檔案過多會怎樣
-
傳回值會出現open
-1
- 通常會導緻程序無法進行,甚至是崩潰
示例驗證代碼
如下代碼可以驗證上述問題中的結論
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> void printStandardFD() { //input/output/error stream printf("%d\t\t\t%p\t\t\t Terminal's input device\n", STDIN_FILENO, stdin); printf("%d\t\t\t%p\t\t\t Terminal's output device\n", STDOUT_FILENO,stdout); printf("%d\t\t\t%p\t\t\t Terminal's error device\n", STDERR_FILENO, stderr); } int printInputFD() { int afd = open("/tmp/a.txt", O_RDONLY); if (afd == -1 ) { printf("error occurs %s\n", strerror(errno)); } printf("%d\t\t\t %p\t\t\t File /tmp/a.txt\n", afd, fdopen(afd, "r")); return afd; } void printWriteFD() { int fd = open("/tmp/b.txt", O_WRONLY); printf("%d\t\t\t %p\t\t\t File /tmp/b.txt\n", fd, fdopen(fd, "w")); } void testSamePathDifferentMode() { int readFd = open("/tmp/c.txt", O_RDONLY); printf("%d\t\t\t %p\t\t\t File /tmp/c.txt read \n", readFd, fdopen(readFd, "r")); int writeFd = open("/tmp/c.txt", O_WRONLY); printf("%d\t\t\t %p\t\t\t File /tmp/c.txt write \n", writeFd, fdopen(writeFd, "w")); } void printPipeFD() { int pipeFds[2]; pipe(pipeFds); printf("%d\t\t\t %p\t\t\t Pipe's read end\n", pipeFds[0], fdopen(pipeFds[0], "r")); printf("%d\t\t\t %p\t\t\t Pipe's write end\n", pipeFds[1], fdopen(pipeFds[1], "w")); } void tryToReachMaxFDs() { while(1 == 1) { if(-1 == printInputFD()) { break; } } } void scanChars() { char chr; printf("Enter a character: "); scanf("%c",&chr); } int main(){ printf("Process File Descriptor table\n"); printf("-----------------------------------------------------\n"); printf("Descriptor\t\t Pointer\t\t Description\n"); printStandardFD(); printInputFD(); printWriteFD(); //printPipeFD(); //tryToReachMaxFDs(); //testSamePathDifferentMode(); scanChars(); } |
---|
P.S.很多年不寫C代碼了。
References
- https://unix.stackexchange.com/questions/430365/what-happens-to-file-descriptors-when-the-process-is-killed
- https://apple.lib.utah.edu/open-file-limits-on-os-x-what-they-are-why-increase-them-and-how-to-increase-them/
- http://geekswing.com/geek/quickie-tutorial-ulimit-soft-limits-hard-limits-soft-stack-hard-stack/
- https://cseweb.ucsd.edu/classes/sp16/cse120-a/applications/ln/lecture15.html
- http://pubs.opengroup.org/onlinepubs/9699919799/functions/_Exit.html#tag_16_01_03_01
- File Descriptors Explained