Linux系统调用如何与文件系统交互

3

最近我遇到了这个练习:

给出一个在Linux C语言程序中的系统调用序列:

fd = open("f123", O_RDWRT | O_CREAT, 0777);
lseek(fd, 0x1000000L, 0);
write(fd, &fd, sizeof(int));

请绘制修改这些操作的文件系统数据结构和磁盘块,考虑4 KByte块大小和4字节的索引块指针。关于第一个系统调用(open),我已经了解了它的工作原理并将其示意如下:open system call现在,跳过“绘制”部分(我意识到这会使回答变得困难),我想了解lseek和write在inode和索引块方面的工作原理(无论它们是什么)。我尝试理解lseek如何计算正确的inode(因为已知块大小),但仍然不知道它实际上是如何工作的。

open()函数确定inode节点,其他函数使用该信息。您需要一个打开的文件描述符表和一个打开的文件描述符表(这可能是您所说的“文件表”的意思,但我不确定)。描述符(与描述符相对)保存寻址偏移量(文件中的当前位置)。通常,多个描述符可以引用单个描述符 - 这些描述符不必全部在同一进程中,尽管在您的示例代码中,只涉及一个进程。 - Jonathan Leffler
有道理。所以lseek只是改变描述表中的当前偏移量,然后搜索正确的块(如果有的话)? - Fylax
2
lseek() 不会搜索正确的块;它也不需要。下一个 I/O 操作(与移动操作相对)需要跟踪块。例如,您可以寻找远超当前文件结尾的位置。接下来会发生什么取决于您是读取(EOF)还是写入(新材料被写入,并且任何跳过的块都被视为所有字节为零;包含当前 EOF 的最后一个块的尾部和当前块的开头可能需要强制清零)。您不能从开头使用负偏移量,但除此之外,lseek() 成功(受类型限制)。 - Jonathan Leffler
1个回答

2
在Linux中,这些系统调用与虚拟文件系统(VFS)进行交互。 VFS在真实文件系统上构建了一个抽象层,它定义了一些有用的数据结构来组织文件系统。
以下是从文件 struct中摘录的片段:
  • inode 代表磁盘上的真实文件。通过inode结构可以访问不仅inode块,还可以访问磁盘上的数据块。
  • 目录项 表示路径的一部分。d_entry并不总是指向磁盘上的真实文件。如果它指向磁盘上的目录,则会有一个指向该目录文件的inode结构的指针。
  • 文件 代表进程打开的文件。在结构体中还有指向其d_entry的指针。
struct file {
    // ... other attributes
    struct path     f_path;
    #define f_dentry    f_path.dentry
    #define f_vfsmnt    f_path.mnt
    const struct file_operations    *f_op;
};
struct file_operations {
    // ... other operations
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
}

这些对象都有一个操作列表字段。VFS定义了这些操作,底层文件系统实现这些操作或使用VFS提供的通用实现。
系统调用open()创建文件对象,其他系统调用如lseek()只需获取file对象(通过Fd),并调用操作列表中相应的函数,如write()将调用f->f_op->write(f, ...),然后文件系统可以沿着file -> d_entry -> inode路径访问磁盘上的文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接