Très souvent, les débutants entendent une phrase "Tout est un fichier sous Linux / Unix". Mais quels sont alors les répertoires? En quoi sont-ils différents des fichiers?
Remarque: à l'origine, cela a été écrit pour prendre en charge ma réponse pour Pourquoi le répertoire actuel dans la ls
commande est-il identifié comme lié à lui-même? mais j'ai senti que c'était un sujet qui méritait de se suffire à lui-même, et donc ce Q&R .
Essentiellement, un répertoire est juste un fichier spécial, qui contient la liste des entrées et leur ID.
Avant de commencer la discussion, il est important de faire une distinction entre quelques termes et de comprendre ce que les répertoires et les fichiers représentent réellement. Vous avez peut-être entendu l'expression "Tout est un fichier" pour Unix / Linux. Eh bien, ce que les utilisateurs comprennent souvent comme fichier, c'est ceci: /etc/passwd
- Un objet avec un chemin et un nom. En réalité, un nom (que ce soit un répertoire ou un fichier, ou quoi que ce soit d'autre) n'est qu'une chaîne de texte - une propriété de l'objet réel. Cet objet est appelé inode ou I-number, et stocké sur disque dans la table d'inode. Les programmes ouverts ont également des tables d'inode, mais ce n'est pas notre préoccupation pour l'instant.
La notion d'Unix d'un répertoire est comme Ken Thompson l'a dit dans une interview de 1989 :
... Et puis certains de ces fichiers étaient des répertoires qui contenaient simplement le nom et le numéro I.
Une observation intéressante peut être tirée de la conférence de Dennis Ritchie en 1972 :
"... le répertoire n'est en fait qu'un fichier, mais son contenu est contrôlé par le système, et le contenu est le nom d'autres fichiers. (Un répertoire est parfois appelé catalogue dans d'autres systèmes.)"
... mais il n'y a aucune mention d'inodes dans le discours. Cependant, le manuel de 1971 sur les format of directories
états:
Le fait qu'un fichier soit un répertoire est indiqué par un bit dans le mot indicateur de son entrée i-node.
Les entrées du répertoire font 10 octets. Le premier mot est le i-nœud du fichier représenté par l'entrée, s'il est différent de zéro; si zéro, l'entrée est vide.
Elle existe donc depuis le début.
Le couplage de répertoires et d'inodes est également expliqué dans Comment les structures de répertoires sont-elles stockées dans le système de fichiers UNIX? . un répertoire lui-même est une structure de données, plus précisément: une liste d'objets (fichiers et numéros d'inode) pointant vers des listes concernant ces objets (autorisations, type, propriétaire, taille, etc.). Ainsi, chaque répertoire contient son propre numéro d'inode, puis les noms de fichier et leurs numéros d'inode. Le plus célèbre est l' inode # 2 qui est le /
répertoire . (Note, bien que /dev
et /run
sont systèmes de fichiers virtuels, donc , car ils sont les dossiers racine pour leur système de fichiers, ils ont aussi inode 2; c'est-à-dire qu'un inode est unique sur son propre système de fichiers, mais avec plusieurs systèmes de fichiers connectés, vous avez des inodes non uniques). le diagramme emprunté à la question liée l'explique probablement plus succinctement:
Toutes ces informations stockées dans l'inode sont accessibles via les stat()
appels système, sous Linux man 7 inode
:
Chaque fichier a un inode contenant des métadonnées sur le fichier. Une application peut récupérer ces métadonnées à l'aide de stat (2) (ou des appels associés), qui renvoie une structure stat, ou statx (2), qui renvoie une structure statx.
Est - il possible d'accéder à un fichier ne connaissant que son numéro d'inode ( ref1 , ref2 )? Sur certaines implémentations Unix, c'est possible mais cela contourne les vérifications d'autorisation et d'accès, donc sous Linux il n'est pas implémenté, et vous devez parcourir l'arborescence du système de fichiers (via find <DIR> -inum 1234
par exemple) pour obtenir un nom de fichier et son inode correspondant.
Au niveau du code source, il est défini dans la source du noyau Linux et est également adopté par de nombreux systèmes de fichiers qui fonctionnent sur les systèmes d'exploitation Unix / Linux, y compris les systèmes de fichiers ext3 et ext4 (Ubuntu par défaut). Chose intéressante: avec les données qui ne sont que des blocs d'informations, Linux a en fait la fonction inode_init_always qui peut déterminer si un inode est un pipe ( inode->i_pipe
). Oui, les sockets et les pipes sont techniquement aussi des fichiers - des fichiers anonymes, qui peuvent ne pas avoir de nom de fichier sur le disque. Les FIFO et les sockets de domaine Unix ont des noms de fichiers sur le système de fichiers.
Les données elles-mêmes peuvent être uniques, mais les numéros d'inode ne sont pas uniques. Si nous avons un lien dur vers foo appelé foobar, cela indiquera également l'inode 123. Cet inode lui-même contient des informations sur les blocs d'espace disque réels occupés par cet inode. Et c'est techniquement comment vous pouvez .
être lié au nom de fichier du répertoire. Eh bien, presque: vous ne pouvez pas créer des répertoires sur les liens physiques pour Linux vous - même , mais les systèmes de fichiers peut permettre des liens durs vers les répertoires d'une manière très disciplinée, ce qui en fait une contrainte d'avoir seulement .
et ..
que les liens durs.
Les systèmes de fichiers implémentent une arborescence de répertoires comme l'une des infrastructures de données de l'arborescence. En particulier,
Le point clé ici est que les répertoires eux-mêmes sont des nœuds dans une arborescence et les sous-répertoires sont des nœuds enfants, chaque enfant ayant un lien vers le nœud parent. Ainsi, pour un lien de répertoire, le nombre d'inodes est au minimum de 2 pour un répertoire nu (lien vers le nom du répertoire /home/example/
et lien vers soi /home/example/.
), et chaque sous-répertoire supplémentaire est un lien / nœud supplémentaire:
# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24
Le diagramme trouvé sur la page du cours d'Ian D. Allen montre un diagramme simplifié très clair:
WRONG - names on things RIGHT - names above things
======================= ==========================
R O O T ---> [etc,bin,home] <-- ROOT directory
/ | \ / | \
etc bin home ---> [passwd] [ls,rm] [abcd0001]
| / \ \ | / \ |
| ls rm abcd0001 ---> | <data> <data> [.bashrc]
| | | |
passwd .bashrc ---> <data> <data>
La seule chose incorrecte dans le diagramme DROIT est que les fichiers ne sont pas techniquement considérés comme étant dans l'arborescence de répertoires elle-même: l'ajout d'un fichier n'a aucun effet sur le nombre de liens:
$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4
Pour citer Linus Torvalds :
Le point avec "tout est un fichier" n'est pas que vous avez un nom de fichier aléatoire (en effet, les sockets et les tuyaux montrent que "fichier" et "nom de fichier" n'ont rien à voir l'un avec l'autre), mais le fait que vous pouvez utiliser des outils pour opérer sur différentes choses.
Étant donné qu'un répertoire n'est qu'un cas particulier d'un fichier, il doit naturellement y avoir des API qui nous permettent de les ouvrir / lire / écrire / fermer de la même manière que les fichiers normaux.
C'est là que la dirent.h
bibliothèque C entre en place, qui définit la dirent
structure, que vous pouvez trouver dans man 3 readdir :
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
Ainsi, dans votre code C, vous devez définir struct dirent *entry_p
, et lorsque nous ouvrons un répertoire avec opendir()
et commençons à le lire readdir()
, nous stockons chaque élément dans cette entry_p
structure. Bien sûr, chaque élément contiendra les champs définis dans le modèle dirent
ci-dessus.
L'exemple pratique de la façon dont cela fonctionne peut être trouvé dans ma réponse sur Comment lister les fichiers et leurs numéros d'inode dans le répertoire de travail actuel .
Notez que le manuel POSIX sur fdopen indique que "[les] entrées de répertoire pour les points et les points sont facultatifs" et que les états manuels readdir struct dirent
ne doivent avoir d_name
que les d_ino
champs et .
Remarque sur "l'écriture" dans les répertoires: écrire dans un répertoire modifie sa "liste" d'entrées. Par conséquent, la création ou la suppression d'un fichier est directement associée aux autorisations d'écriture de répertoire , et l'ajout / suppression de fichiers est l'opération d'écriture sur ledit répertoire.
open()
et lesread()
sockets ontconnect()
etread()
aussi. Ce qui serait plus précis, c'est que le "fichier" est vraiment des "données" organisées stockées sur le disque ou la mémoire, et certains fichiers sont anonymes - ils n'ont pas de nom de fichier. Habituellement, les utilisateurs pensent aux fichiers en fonction de cette icône sur le bureau, mais ce n'est pas la seule chose qui existe. Voir aussi unix.stackexchange.com/a/116616/85039