在系统调用中使用文件描述符

在系统调用中使用文件描述符

假设我想在系统调用中使用文件描述符(fd 号将通过参数提供)。如果用户空间程序使用此系统调用,会发生什么?操作系统会在哪里寻找这个特定的 fd?在当前进程的文件描述符中还是其他地方?

下面,我试图说明这一点。

+--------------+     +----++--------------+
| Kernel space |     | fd ||  User space  |
|              |     |list||              |
|   handler <---------------- syscall(fd) |
|              |     |    ||              |
+--------------+     +----++--------------+

答案1

文件描述符是一个整数,用于引用给定进程打开的所有文件中的一个文件。通常,这是由内核通过将文件描述符视为表中的索引来实现的。

我的答案的其余部分适用于 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.此外,内核必须执行额外的检查来处理文件描述符上的竞争条件。

相关内容