Dans le monde Unix, il existe un dicton classique : tout est un fichier. Cela signifie que dans le système d'exploitation Unix, tous les objets peuvent être traités comme des fichiers et peuvent être manipulés à l'aide de l'interface d'exploitation des fichiers. En tant que système d'exploitation de type Unix, Linux s'efforce également d'atteindre cet objectif.
Pour réaliser一切皆文件
这个目标,Linux 内核提供了一个中间层:虚拟文件系统(Virtual File System)
.
Si vous avez déjà utilisé des langages de programmation orientés objet (tels que C++/Java, etc.), alors vous devriez être familier avec ce concept接口
. Le système de fichiers virtuel est similaire à l'interface de la programmation orientée objet, qui définit un ensemble d'interfaces standard. Les développeurs n'ont qu'à implémenter cet ensemble d'interfaces, puis ils peuvent utiliser l'interface pour exploiter des fichiers afin de faire fonctionner des objets. Comme le montre l'image ci-dessous :
La partie bleue dans l'image ci-dessus est l'emplacement du système de fichiers virtuel.
Comme le montre la figure ci-dessus, le système de fichiers virtuel fournit une interface unifiée pour les applications de couche supérieure. Si un système de fichiers implémente l'interface d'un système de fichiers virtuel, les applications de couche supérieure peuvent utiliser des fonctions telles queopen()
、read()
和write()
pour les exploiter.
Aujourd'hui, nous présenterons le principe et la mise en œuvre du système de fichiers virtuel.
Avant d'expliquer le principe du système de fichiers virtuel, introduisons d'abord un exemple Java. Grâce à cet exemple Java, nous pouvons plus facilement comprendre les principes des systèmes de fichiers virtuels.
Si vous avez utilisé Java pour écrire des programmes, il sera facile de comprendre le système de fichiers virtuel. Nous utilisons l'interface de Java pour simuler la définition d'un système de fichiers virtuel :
public interface VFSFile { int open(String file, int mode); int read(int fd, byte[] buffer, int size); int write(int fd, byte[] buffer, int size); ... }
Ce qui précède définit une police nomméeVFSFile
, qui définit certaines méthodes, commeVFSFile
的接口,接口中定义了一些方法,如open()
、read()
和write()
等。现在我们来定义一个名为Ext3File
, etc. Définissons maintenant une police appelée : objet #10f5d6c">Ext3Filepour implémenter cette interface :
public class Ext3File implements VFSFile { @Override public int open(String file, int mode) { ... } @Override public int read(int fd, byte[] buffer, int size) { ... } @Override public int write(int fd, byte[] buffer, int size) { ... } ... }
Nous pouvons maintenant utiliser l'objetVFSFile
接口来操作Ext3File
, comme indiqué ci-dessous :
public class Main() { public static void main(String[] args) { VFSFile file = new Ext3File(); int fd = file.open("/tmp/file.txt", 0); ... } }
从上面的例子可以看出,底层对象只需要实现VFSFile
接口,就可以使用VFSFile
接口相关的方法来操作对象,用户完全不需要了解底层对象的实现过程。
上面的 Java 例子已经大概说明虚拟文件系统的原理,但由于 Linux 是使用 C 语言来编写的,而 C 语言并没有接口这个概念。所以,Linux 内核使用了一些技巧来模拟接口这个概念。
下面来介绍一下 Linux 内核是如何实现的。
为了模拟接口,Linux 内核定义了一个名为file
的结构体,其定义如下:
struct file { ... const struct file_operations *f_op; ... };
在 file 结构中,最为重要的一个字段就是f_op
,其类型为file_operations
结构。而file_operations
结构是由一组函数指针组成,其定义如下:
struct file_operations { ... 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 *); ... int (*open) (struct inode *, struct file *); ... };
从file_operations
结构的定义可以隐约看到接口的影子,所以可以猜想出,如果实现了file_operations
结构中的方法,应该就能接入到虚拟文件系统中。
在 Linux 内核中,file
结构代表着一个被打开的文件。所以,只需要将file
结构的f_op
字段设置成不同文件系统实现好的方法集,那么就能够使用不同文件系统的功能。
这个过程在__dentry_open()
函数中实现,如下所示:
static struct file * __dentry_open(struct dentry *dentry, struct vfsmount *mnt, truct file *f, int (*open)(struct inode *, struct file *), const struct cred *cred) { ... inode = dentry->d_inode; ... // 设置file结构的f_op字段为底层文件系统实现的方法集 f->f_op = fops_get(inode->i_fop); ... return f; }
设置好file
结构的f_op
字段后,虚拟文件系统就能够使用通用的接口来操作此文件了。调用过程如下:
底层文件系统需要实现虚拟文件系统的接口,才能被虚拟文件系统使用。也就是说,底层文件系统需要实现file_operations
结构中的方法集。
一般底层文件系统会在其内部定义好file_operations
结构,并且填充好其方法集中的函数指针。如minix文件系统
就定义了一个名为minix_file_operations
的file_operations
结构。其定义如下:
// 文件:fs/minix/file.c const struct file_operations minix_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .aio_read = generic_file_aio_read, .write = do_sync_write, .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, .fsync = generic_file_fsync, .splice_read = generic_file_splice_read, };
也就是说,如果当前使用的是 minix 文件系统,当使用read()
函数读取其文件的内容时,那么最终将会调用do_sync_read()
函数来读取文件的内容。
到这里,虚拟文件系统的原理基本分析完毕,但还有两个非常重要的结构要介绍一下的:dentry
和inode
。
dentry
结构表示一个打开的目录项,当我们打开文件/usr/local/lib/libc.so
文件时,内核会为文件路径中的每个目录创建一个dentry
结构。如下图所示:
由于/usr/local/lib/libc.so
和/tmp/libc.so
指向同一个文件,所以它们都使用同一个inode
对象。
inode 结构保存了文件的所有属性值,如文件的创建时间、文件所属用户和文件的大小等。其定义如下所示:
struct inode { ... uid_t i_uid; // 文件所属用户 gid_t i_gid; // 文件所属组 ... struct timespec i_atime; // 最后访问时间 struct timespec i_mtime; // 最后修改时间 struct timespec i_ctime; // 文件创建时间 ... unsigned short i_bytes; // 文件大小 ... const struct file_operations *i_fop; // 文件操作方法集(用于设置file结构) ... };
我们注意到 inode 结构有个类型为file_operations
结构的字段i_fop
,这个字段保存了文件的操作方法集。当用户调用open()
系统调用打开文件时,内核将会使用inode
结构的i_fop
字段赋值给file
结构的f_op
字段。我们再来重温下赋值过程:
static struct file * __dentry_open(struct dentry *dentry, struct vfsmount *mnt, truct file *f, int (*open)(struct inode *, struct file *), const struct cred *cred) { ... // 文件对应的inode对象 inode = dentry->d_inode; ... // 使用inode结构的i_fop字段赋值给file结构的f_op字段 f->f_op = fops_get(inode->i_fop); ... return f; }
本文主要介绍了虚拟文件系统
的基本原理,从分析中可以发现,虚拟文件系统使用了类似于面向对象编程语言中的接口概念。正是有了虚拟文件系统
,Linux 才能支持各种各样的文件系统。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!