Syscalls

在系統呼叫中使用文件描述符

  • November 26, 2018

假設我想在系統呼叫中使用文件描述符(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;

指針可能在函式返回之前被消除(例如,如果程序的一個執行緒同時對同一個文件描述符執行 aread和另一個 a )。close核心負責這個。

如果struct file返回的指針fdget_posNULL,則表示傳遞給系統呼叫的文件描述符無效。在這種情況下,系統呼叫會返回錯誤程式碼EBADF(“錯誤文件描述符”)。

總而言之,文件描述符只是文件描述符的每個程序表中的索引。但是,僅僅取消引用它們是不夠的,因為文件表中的條目可能是NULL. 此外,核心必須進行額外的檢查以處理文件描述符上的競爭條件。

引用自:https://unix.stackexchange.com/questions/354373