Cet article traite principalement des systèmes de fichiers virtuels. L'architecture du système de fichiers Linux comprend la couche d'abstraction entre des systèmes de fichiers spécifiques (tels que Ext2, Ext3, XFS, etc.) et des applications, à savoir le système de fichiers virtuel (VFS). VFS permet aux applications de communiquer avec différents types de systèmes de fichiers sans connaître les détails du système de fichiers sous-jacent. Avec VFS, les implémentations de systèmes de fichiers peuvent être isolées et découplées des applications, améliorant ainsi la flexibilité et la maintenabilité du système. VFS permet également au noyau Linux de prendre en charge plusieurs types de systèmes de fichiers et fournit une interface unifiée permettant aux applications d'accéder au système de fichiers. Dans le cadre de VFS, différents systèmes de fichiers peuvent communiquer avec le noyau en implémentant des interfaces d'exploitation de système de fichiers standard
Photos
La figure ci-dessus montre que le centre de cette architecture est le système de fichiers virtuel VFS. VFS fournit un cadre de système de fichiers et des systèmes de fichiers locaux peuvent être implémentés sur la base de VFS. Il accomplit principalement les tâches suivantes :
1) En tant que couche d'abstraction, VFS fournit une interface unifiée (lecture, écriture, chmod, etc.) pour la couche application.
2) Certaines fonctions courantes sont implémentées dans VFS, telles que le cache d'inodes et le cache de pages.
3) Standardise les interfaces que les systèmes de fichiers spécifiques doivent implémenter.
Sur la base des paramètres ci-dessus, d'autres systèmes de fichiers spécifiques doivent simplement suivre les conventions de VFS, implémenter les interfaces et la logique interne correspondantes et les enregistrer dans le système. Une fois que l'utilisateur a formaté et monté le système de fichiers, il peut utiliser les ressources du disque dur pour effectuer des opérations basées sur le système de fichiers.
Dans le système d'exploitation Linux, après avoir formaté le disque, vous devez utiliser la commande mount pour le monter dans un répertoire de l'arborescence du système. Ce répertoire est appelé point de montage. Une fois le montage terminé, nous pouvons utiliser l'espace disque dur formaté sur la base de ce système de fichiers. Dans le système d'exploitation Linux, le point de montage peut être presque n'importe quel répertoire, mais pour des raisons de standardisation, le point de montage est généralement un sous-répertoire du répertoire mnt.
Ce qui suit montre une structure de répertoires relativement complexe. Dans cette structure de répertoires, le répertoire racine est situé sur le disque dur sda, et il y a trois sous-répertoires sous le répertoire mnt, à savoir ext4, xfs et nfs, qui montent respectivement le système de fichiers Ext4 (construit sur le disque dur sdb) et le Système de fichiers XFS (construit sur le disque dur sdc) et système de fichiers NFS (monté sur le réseau).
Photos
La relation entre plusieurs systèmes de fichiers dans l'arborescence des répertoires est représentée par certaines structures de données dans le noyau. Lors du montage d'un système de fichiers, la relation entre les systèmes de fichiers sera établie et l'API du système de fichiers spécifique sera enregistrée. Lorsque le mode utilisateur appelle l'API pour ouvrir un fichier, il trouvera l'API du système de fichiers correspondante et l'associera à la structure liée au fichier (telle que le fichier et l'inode, etc.).
La description ci-dessus est relativement schématique, et vous pourriez encore vous sentir un peu confus. Mais ne vous inquiétez pas, nous présenterons plus en détail VFS et comment prendre en charge plusieurs systèmes de fichiers en fonction du code.
VFS sous Linux n'existait pas depuis le début. La première version de Linux publiée n'avait pas VFS. De plus, VFS n'a pas été inventé sous Linux. Il a été développé pour la première fois par Sun en 1985 dans son SunOS2.0. L'objectif principal du développement de VFS est d'adapter son système de fichiers local et son système de fichiers NFS.
VFS implémente l'abstraction de systèmes de fichiers spécifiques via un ensemble d'API publiques et de structures de données. Lorsque l'utilisateur appelle l'API du système de fichiers fournie par le système d'exploitation, la fonction implémentée par le noyau VFS est appelée via une interruption logicielle. Le tableau suivant montre la correspondance entre certaines API de fichiers et les fonctions VFS du noyau.
API du mode utilisateur |
Fonction noyau |
Instructions |
ouvert |
do_sys_open |
Ouvrir le fichier |
fermer |
ksys_close |
Fermer le dossier |
lire |
ksys_read/vfs_read |
Lire les données |
écrire |
ksys_write/vfs_write |
Écrire des données |
monter |
do_mount |
Monter le système de fichiers |
Le tableau ci-dessus montre que chaque API en mode utilisateur a une fonction en mode noyau correspondante. Lorsqu'une application appelle l'API du système de fichiers, la fonction correspondante dans l'état du noyau est déclenchée. Ce qui est répertorié ici ne représente qu'un sous-ensemble relativement petit de l'API du système de fichiers, et le but est d'illustrer la relation entre l'API et VFS. Si vous souhaitez en savoir plus sur d'autres API, veuillez lire vous-même le code source du noyau, et je n'entrerai pas dans les détails dans cet article.
Afin de donner à chacun une compréhension perceptive de la relation entre VFS et des systèmes de fichiers spécifiques, cette section prend l'API d'écriture Ext2 comme exemple pour montrer la relation d'appel de l'API aux fonctions VFS puis aux fonctions du système de fichiers Ext2. Comme le montre la figure ci-dessous, la fonction API write déclenche la fonction ksys_write du noyau via une interruption logicielle. Après un certain traitement, cette fonction appellera finalement la fonction ext2_file_write_iter du système de fichiers Ext2 via le pointeur de fonction (file->f_op). ->wirte_iter).
Photos
Dans la figure ci-dessus, l'entrée du processus du noyau est la fonction ksys_write. Comme le montre le code d'implémentation, l'objectif principal ici est d'obtenir un fd, puis d'appeler vfs_write avec le fichier membre dans le fd comme paramètre. . Parmi eux, fd est une structure, et son format est comme indiqué dans la figure ci-dessous. Le membre de fichier est une structure de données relativement centrale. Comme le montre la figure ci-dessus, c'est à travers le contenu de ce membre que les fonctions du système de fichiers Ext2 sont transférées.
Photos
Cela semble très simple, VFS n'a besoin que d'appeler le pointeur de fonction enregistré par le système de fichiers spécifique. Mais il y a ici un problème non résolu. Quand les pointeurs de fonction dans VFS ont-ils été enregistrés ?
Le pointeur de fonction d'Ext2 est initialisé lorsque le fichier est ouvert (pour plus de détails, veuillez vous référer à la section 3.1.2.2 de « Insider of File System Technology »). Comme nous le savons tous, un programme en mode utilisateur renvoie un descripteur de fichier lorsqu'il ouvre un fichier, mais le fichier de structure représentant le fichier dans le noyau lui correspond. Les membres les plus importants de cette structure incluent f_inode, f_ops et f_mapping, comme le montre la figure ci-dessous.
Photos
Dans la figure ci-dessus, f_inode est le nœud inode correspondant au fichier. f_ops est une collection de pointeurs de fonction pour les opérations sur les fichiers dans un système de fichiers spécifique (tel que Ext2). Il est initialisé lorsque le fichier est ouvert. C'est grâce à cette collection de pointeurs de fonction que VFS implémente l'accès à des systèmes de fichiers spécifiques.
Ce qui précède implique un autre concept de VFS, l'inode. Sous Linux, inode est l'abréviation de nœud d'index, qui représente un objet spécifique (tel qu'un fichier ou un répertoire) dans le système de fichiers. Il existe une structure de données nommée inode dans VFS, qui est une abstraction de l'inode spécifique du système de fichiers. Par exemple, dans le système de fichiers Ext2, il est spécifiquement défini comme ext2_inode_info, mais dans XFS, il est représenté par la structure de données xfs_inode. De plus, la structure des données inode du système de fichiers spécifique est intrinsèquement liée à l'inode de VFS. Vous pouvez lire le code par vous-même.
Dans le diagramme d'architecture, nous voyons qu'il existe plusieurs implémentations de cache dans VFS, notamment le cache de pages, le cache d'inodes, le cache dentry, etc. Le cache inode et le cache dentry sont implémentés de la même manière et sont relativement simples. Par conséquent, cet article présente d’abord ces deux caches.
En fait, ces deux caches sont implémentés via des tables de hachage. Tout le monde connaît le concept des tables de hachage, je n'entrerai donc pas dans les détails dans cet article. Prenons l'exemple du cache d'inodes. La figure suivante montre son processus d'initialisation. Grâce au paramètre ihash_entries, vous pouvez voir que sa taille est dynamique (sa taille est liée à la mémoire système. Plus le cache d'inodes est grand lorsque la mémoire système est grande. lire).
Photos
Étant donné que l'inode et le dentry sont fréquemment consultés lors de l'accès aux fichiers, leur mise en cache peut éviter la perte de performances causée par la lecture des données du disque dur.
Le cache de pages VFS (Cache) est principalement utilisé pour améliorer les performances du système de fichiers. La technologie de mise en cache fait référence à une technologie qui stocke une partie des données et métadonnées du système de fichiers en mémoire pour améliorer les performances du système de fichiers. Étant donné que le délai d'accès à la mémoire est le cent millième du délai d'accès d'un disque dur mécanique (comme le montre la figure ci-dessous, avec le registre comme unité de base 1s), l'utilisation de la technologie de mise en cache peut grandement améliorer les performances du fichier. système.
Photos
Cache améliore les performances du système de fichiers grâce à trois aspects de l'optimisation des E/S, à savoir les données chaudes, la pré-lecture et la fusion des E/S. De nombreuses applications contiennent des données sensibles. Par exemple, lorsqu'un auteur modifie un document, le bloc de données actuel et les blocs de données voisins sont des données sensibles. Ou lorsqu'un article populaire apparaît, le contenu de cet article est une donnée chaude. Les périphériques de stockage sous-jacents ont tendance à avoir de meilleures performances pour les lectures et écritures de blocs volumineux. La lecture anticipée signifie lire à l'avance de gros blocs de données du périphérique sous-jacent et les mettre en cache, afin que les demandes des applications puissent être répondues via le cache. La fusion des E/S est destinée aux demandes d'écriture. Les demandes d'écriture ne sont pas immédiatement conservées sur le périphérique principal, mais sont mises en cache et divisées en gros blocs d'E/S avant l'écriture.
Étant donné que la capacité de la mémoire est bien inférieure à la capacité du disque dur, le cache de pages ne peut pas mettre en cache toutes les données du disque dur. De cette manière, seul un sous-ensemble des données du système de fichiers peut être stocké dans le cache. Lorsque les utilisateurs continuent d'écrire des données, ils seront confrontés à une situation où le cache est plein. À ce stade, cela implique le problème de savoir comment vider les données du cache sur le disque, puis stocker de nouvelles données.
Le processus de vidage du cache sur le disque et de stockage de nouvelles données est appelé remplacement du cache. Il existe de nombreux algorithmes de remplacement du cache, chacun étant utilisé pour résoudre des problèmes différents. Nous introduisons ensuite plusieurs algorithmes courants de remplacement du cache.
Algorithme LRU, le nom complet de LRU est Least Récemment Utilisé, ce qui signifie le moins récemment utilisé. Cet algorithme est basé sur le principe de localité temporelle, c'est-à-dire que si une donnée a été utilisée récemment, il y a une forte probabilité qu'elle soit réutilisée dans le futur. Par conséquent, l’algorithme libérera les caches qui n’ont pas été utilisés récemment.
L'algorithme LRU est généralement implémenté à l'aide d'une liste chaînée. Le cache qui vient d'être utilisé sera inséré en tête de la table, tandis que les données qui n'ont pas été souvent utilisées seront lentement comprimées jusqu'à la fin de la liste chaînée. Afin de mieux comprendre le principe du LRU, nous allons l’illustrer avec la figure suivante.
Photos
Dans cet exemple, nous prenons comme exemple les hits complets. Supposons qu'il y ait 6 blocs de données dans le cache, comme indiqué sur la première ligne de la figure. Le nombre dans la case représente le numéro du bloc de données. Supposons que le premier accès (peut être en lecture ou en écriture) est le bloc de données n° 3. Une fois l'accès effectué, il est déplacé en tête de la liste chaînée.
Le deuxième accès se fait au bloc de données n°4. Selon le même principe, ce bloc de données est également déplacé en tête de la liste chaînée. Les détails sont présentés à la ligne 2 de la figure ci-dessus.
Par analogie, après 4 tours d'accès, les données accédées ont été avancées, tandis que les blocs de données non consultés (tels que 1 et 2) sont lentement relégués à la fin de la liste chaînée. Cela indique dans une certaine mesure que la possibilité d'accéder ultérieurement à ces deux blocs de données est relativement faible.
S'il s'agit d'un succès complet, il n'y aura pas de remplacement du cache. La situation réelle est que le cache sera souvent insuffisant et que les données qu'il contient doivent être libérées (selon la situation, si elles doivent être vidées sur le disque) pour stocker de nouvelles données. À l'heure actuelle, l'algorithme LRU est utile. Cet algorithme utilise le bloc de données de queue pour stocker de nouvelles données, puis le place en tête de la liste chaînée, comme le montre la figure ci-dessous. S'il y a des données sales dans ce bloc de données, elles doivent être vidées sur le disque, sinon elles peuvent être libérées directement.
Photos
Le principe et la mise en œuvre de l'algorithme LRU sont relativement simples, mais il a un large éventail d'utilisations. Cependant, l'algorithme LRU présente un inconvénient : lorsqu'une grande quantité de données continues est soudainement écrite, tous les blocs de cache seront remplacés, ce qui rendra invalides toutes les statistiques d'utilisation du cache précédentes. Ce phénomène est appelé pollution du cache. Afin de résoudre le problème de pollution du cache, il existe de nombreux algorithmes LRU améliorés, parmi lesquels les plus courants sont LRU-K, 2Q et LIRS.
Algorithme LFU, le nom complet de LFU est Le moins fréquemment utilisé, ce qui signifie qu'il est le moins fréquemment utilisé récemment. Cet algorithme décide quel bloc de cache libérer en fonction de la fréquence d'accès aux données. Le bloc de cache avec la fréquence d'accès la plus basse sera libéré en premier.
Comme indiqué ci-dessous, vous trouverez un diagramme schématique de l'algorithme LFU. La ligne 1 correspond à l'état d'origine et le nombre dans la case représente le nombre de fois où le bloc de cache a été accédé. L'ajout de nouvelles données et la suppression des blocs de cache s'effectuent depuis la queue. Supposons qu'une certaine donnée (case en pointillés) ait été consultée 4 fois et que le nombre d'accès soit passé de 12 à 16, elle doit donc être déplacée vers un nouvel emplacement, ce à quoi ressemble la ligne 2 de la figure. .
Photos
Ce livre utilise une liste chaînée comme exemple pour illustrer le principe de LFU pour faciliter la compréhension, mais il ne sera jamais implémenté à l'aide d'une liste chaînée pendant la mise en œuvre du projet. Étant donné qu'un nouvel emplacement doit être trouvé lorsque le nombre d'accès à un bloc de données change, l'opération de recherche par liste chaînée prend beaucoup de temps. Afin d'obtenir une recherche rapide, un arbre de recherche est généralement utilisé.
LFU a aussi ses inconvénients. Si un bloc de données a été consulté fréquemment au cours d'une certaine période de temps et n'est plus consulté à l'avenir, les données resteront dans le cache. Cependant, comme les données ne seront plus accessibles, la capacité effective du cache est réduite. En d’autres termes, l’algorithme LFU ne prend pas en compte la situation récente.
Cet article présente principalement deux algorithmes de remplacement très basiques, LRU et LFU. En plus des algorithmes ci-dessus, il existe de nombreux algorithmes de remplacement, dont la plupart sont basés sur la théorie LRU et LFU, tels que 2Q, MQ, LRFU, TinyLFU et ARC, etc. En raison du manque de place, ce livre n'entrera pas dans les détails. Vous pouvez lire les articles pertinents par vous-même.
La pré-lecture des données dispose également d'un certain algorithme. L'algorithme de pré-lecture lit à l'avance les données du disque vers le cache en identifiant les modèles d'E/S. De cette manière, lorsque l'application lit des données, elle peut lire les données directement à partir du cache, améliorant ainsi considérablement les performances de lecture des données.
La chose la plus importante dans l'algorithme de pré-lecture est la condition de déclenchement, c'est-à-dire dans quelles circonstances l'opération de pré-lecture est lancée. Il existe généralement deux situations qui déclenchent une lecture anticipée : l'une est lorsqu'il y a des demandes de lecture consécutives provenant de plusieurs adresses, ce qui déclenche une opération de lecture anticipée ; l'autre est lorsque l'application accède à un cache avec une marque de lecture anticipée ; Ici, le cache de la marque de lecture anticipée est une marque faite sur la page du cache lorsque l'opération de lecture anticipée est terminée. Lorsque l'application lit le cache avec cette marque, la prochaine lecture anticipée sera déclenchée, omettant ainsi l'identification. du mode IO.
Photos
Afin d'expliquer plus clairement la logique de la pré-lecture, nous présentons l'ensemble du processus à travers l'image ci-dessus. Lorsque le système de fichiers reconnaît que le mode IO nécessite une pré-lecture, il lira une partie supplémentaire du contenu (appelée pré-lecture synchrone), comme indiqué au temps 1 (première ligne). Dans le même temps, pour les données à lecture anticipée synchrone, le système de fichiers marquera l'un des blocs. Le but de cette marque est de déclencher la prochaine pré-lecture le plus tôt possible avant la fin du cache.
Au deuxième instant, lorsque l'application continue de lire les données, parce que le bloc de cache marqué est lu, la pré-lecture suivante est déclenchée en même temps. À ce stade, les données seront lues à partir du disque en une seule étape et vous pouvez voir sur la figure que le cache augmente.
Aux prochains points 3 et 4, l'application peut lire les données directement depuis le cache. Puisqu’aucun bloc de cache marqué n’a été lu, la prochaine lecture anticipée ne sera pas déclenchée. Au moment 5, parce qu'il y a une marque de pré-lecture, le processus de pré-lecture sera à nouveau déclenché.
L'analyse ci-dessus montre qu'en raison de la fonction de lecture anticipée, les données sont lues dans le cache à l'avance. Les applications peuvent lire les données directement à partir du cache sans accéder au disque, ce qui améliore considérablement les performances globales d'accès.
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!