在系統呼叫中使用文件描述符
假設我想在系統呼叫中使用文件描述符(fd 編號將通過參數提供)。如果使用者空間程序使用這個系統呼叫會發生什麼?作業系統會在哪裡尋找這個特定的 fd?在目前程序的文件描述符中還是其他地方?
下面,我試圖說明這一點。
+--------------+ +----++--------------+ | Kernel space | | fd || User space | | | |list|| | | handler <---------------- syscall(fd) | | | | || | +--------------+ +----++--------------+
文件描述符是用於在給定程序打開的所有文件中引用文件的整數。通常,這是由核心通過將文件描述符視為表中的索引來實現的。
我的其餘答案適用於 Linux。
在 Linux 中,每個有效的文件描述符都與一個
struct file
. 該結構包含指向 inode(文件的數據和元數據)的指針、程序在文件中的目前位置、操作列表,這些操作實際上是指向文件所在文件系統實現的函式的指針等。為了
file
從文件描述符中獲取結構,Linux 核心按如下方式進行。我這里以read
系統呼叫為例。SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) { struct fd f = fdget_pos(fd); ssize_t ret = -EBADF; if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_read(f.file, buf, count, &pos); if (ret >= 0) file_pos_write(f.file, pos); fdput_pos(f); } return ret; }
第一個操作是
fdget_pos
。它將來自使用者空間中呼叫者的文件描述符作為參數,並獲取相應的file
. 它返回一個struct fd
定義如下:struct fd { struct file *file; unsigned int flags; };
這基本上是一個
struct file
,帶有幾個標誌來記住在放回結構時需要哪些操作。現在,如何
fdget_pos
工作。它實際上以奇怪的方式錯綜複雜,但歸結為兩個基本操作(為了簡單起見,我沒有在這裡顯示更多檢查):第一個包括獲取程序的文件表。該表可從呼叫者程序結構中的指針獲得(可通過 訪問
current
):struct files_struct *files = current->files;
下一個操作包括驗證文件描述符的有效性:
if (fd < files->fdt->max_fds) // first of all, if the file descriptor is too big, then it cannot be valid return files->fdt->fd[fd]; // otherwise, we return the pointer stored in the table of file descriptors (may be NULL) return NULL;
指針可能在函式返回之前被消除(例如,如果程序的一個執行緒同時對同一個文件描述符執行 a
read
和另一個 a )。close
核心負責這個。如果
struct file
返回的指針fdget_pos
是NULL
,則表示傳遞給系統呼叫的文件描述符無效。在這種情況下,系統呼叫會返回錯誤程式碼EBADF
(“錯誤文件描述符”)。總而言之,文件描述符只是文件描述符的每個程序表中的索引。但是,僅僅取消引用它們是不夠的,因為文件表中的條目可能是
NULL
. 此外,核心必須進行額外的檢查以處理文件描述符上的競爭條件。