延伸閱讀: 「一切皆為檔案」的理念與解讀
檔案描述符(file descriptor)就是一個整數,它對每個程序(process)都是私有的,在 UNIX 系統中用來存取檔案。因此,一旦檔案被開啟,只要你有權限,你就可以透過這個檔案描述符去讀取或寫入檔案。 從這個角度來看,file descriptor 是一種「能力」(capability) —— 它是一個不透明的操作控制代碼,可以賦予你執行某些特定操作的能力。
也可以把 file descriptor 想像成一個指向 file 類型物件的指標,一旦你持有這個物件,你就能使用像 read() 和 write() 這些「方法」來存取檔案。在 UNIX 系統中,每個 process 的 proc 結構中都會維護一個簡單的資料結構(例如陣列)透過 file descriptor 作為索引,來追蹤目前有哪些檔案是被這個程序開啟的。這個陣列的每一個元素其實就是一個指向 struct file 的指標,用來儲存目前正在讀寫的檔案的詳細資訊
可以用 strace 來追蹤程式呼叫了哪些系統呼叫(system call)、傳遞了什麼參數、回傳了什麼值
範例
接著執行
open() 被底層實作成 openat(),所以 trace 參數要寫 openat。
int fd = open("test.txt", O_RDONLY);
=> openat(AT_FDCWD, "test.txt", O_RDONLY) = 3
: = 3 表示這次開啟成功,系統回傳 file descriptor 3read(fd, buffer, sizeof(buffer) - 1);
=> read(3, "Hello from file descriptor!\n", 127) = 28
: fd = 3,成功從 test.txt 讀了 28 byte; 存到 buffer[] 裡write(STDOUT_FILENO, buffer, bytes_read);
=> write(1, "Hello from file descriptor!\n", 28) = 28
: STDOUT_FILENO 是 1;把剛剛讀到的 28 個字元寫到標準輸出(螢幕);成功寫了 28 bytes。close(fd);
=> close(3) = 0
: 關閉 file descriptor 3;回傳 0 表示關閉成功。為了要觀察更詳細資訊,在 close(fd)
之前加一行 sleep(120)
可看到最後一行 fd = 3,即是我在程式中 open() 的檔案
範例:
cat
程式第一步會開啟檔案來進行讀取。我們可以發現: 檔案是以唯讀(read-only)方式開啟的,這由 O_RDONLY 旗標所示;open() 成功並回傳了一個檔案描述符(fd)3。
當 open() 一個新的檔案時(像 cat 這樣),它幾乎一定會得到 fd 3,這是因為每個正在執行的 process 都有三個預設已開啟的檔案: stdin(fd 0); stdout(fd 1); stderr(fd 2)
file: https://github.com/torvalds/linux/blob/master/include/linux/fdtable.h
file: https://github.com/torvalds/linux/blob/master/include/linux/fdtable.h
file: https://github.com/torvalds/linux/blob/master/include/linux/fs.h
file:https://github.com/torvalds/linux/blob/master/include/linux/sched.h