Alors que la question Stack Overflow semblait être suffisante au début, je comprends, d'après vos commentaires, pourquoi vous avez peut-être encore un doute à ce sujet. Pour moi, c'est exactement le genre de situation critique impliquée lorsque les deux sous-systèmes UNIX (processus et fichiers) communiquent.
Comme vous le savez peut-être, les systèmes UNIX sont généralement divisés en deux sous-systèmes: le sous-système de fichiers et le sous-système de processus. Maintenant, sauf indication contraire via un appel système, le noyau ne devrait pas avoir ces deux sous-systèmes interagissent l'un avec l'autre. Il existe cependant une exception: le chargement d'un fichier exécutable dans les régions de texte d' un processus . Bien sûr, on peut faire valoir que cette opération est également déclenchée par un appel système ( execve
), mais il est généralement connu que c'est le seul cas où le sous-système de processus fait une demande implicite au sous-système de fichiers.
Parce que le sous-système de processus n'a naturellement aucun moyen de gérer les fichiers (sinon il ne servirait à rien de diviser le tout en deux), il doit utiliser tout ce que le sous-système de fichiers fournit pour accéder aux fichiers. Cela signifie également que le sous-système de processus est soumis à toute mesure prise par le sous-système de fichiers concernant l'édition / la suppression de fichiers. Sur ce point, je recommanderais de lire la réponse de Gilles à cette question U&L . Le reste de ma réponse est basé sur celle plus générale de Gilles.
La première chose à noter est qu'en interne, les fichiers ne sont accessibles que via des inodes . Si le noyau reçoit un chemin, sa première étape sera de le traduire en un inode à utiliser pour toutes les autres opérations. Lorsqu'un processus charge un exécutable en mémoire, il le fait via son inode, qui a été fourni par le sous-système de fichiers après la traduction d'un chemin. Les inodes peuvent être associés à plusieurs chemins (liens) et les programmes ne peuvent supprimer que des liens. Afin de supprimer un fichier et son inode, l'espace utilisateur doit supprimer tous les liens existants vers cet inode et s'assurer qu'il est complètement inutilisé. Lorsque ces conditions sont remplies, le noyau supprimera automatiquement le fichier du disque.
Si vous regardez la partie remplaçable des exécutables de la réponse de Gilles, vous verrez que selon la façon dont vous éditez / supprimez le fichier, le noyau réagira / s'adaptera différemment, toujours via un mécanisme implémenté dans le sous-système de fichiers.
- Si vous essayez la première stratégie ( ouvrir / tronquer à zéro / écrire ou ouvrir / écrire / tronquer à une nouvelle taille ), vous verrez que le noyau ne prendra pas la peine de traiter votre demande. Vous obtiendrez une erreur 26: fichier texte occupé (
ETXTBSY
). Aucune conséquence que ce soit.
- Si vous essayez la stratégie deux, la première étape consiste à supprimer votre exécutable. Cependant, étant donné qu'il est utilisé par un processus, le sous-système de fichiers démarrera et empêchera le fichier (et son inode) d'être réellement supprimé du disque. À partir de là, le seul moyen d'accéder au contenu de l'ancien fichier est de le faire via son inode, ce que fait le sous-système de processus chaque fois qu'il a besoin de charger de nouvelles données dans des sections de texte (en interne, il est inutile d'utiliser des chemins, sauf lors de leur traduction en inodes). Même si vous avez dissociéle fichier (supprimé tous ses chemins), le processus peut toujours l'utiliser comme si vous n'aviez rien fait. La création d'un nouveau fichier avec l'ancien chemin ne change rien: le nouveau fichier recevra un inode complètement nouveau, dont le processus en cours n'a aucune connaissance.
Les stratégies 2 et 3 sont également sans danger pour les exécutables: bien que l'exécution d'exécutables (et de bibliothèques chargées dynamiquement) ne soit pas des fichiers ouverts dans le sens d'avoir un descripteur de fichier, ils se comportent de manière très similaire. Tant qu'un programme exécute le code, le fichier reste sur le disque même sans entrée de répertoire.
- La troisième stratégie est assez similaire car l'
mv
opération est atomique. Cela nécessitera probablement l'utilisation de l' rename
appel système, et comme les processus ne peuvent pas être interrompus en mode noyau, rien ne peut interférer avec cette opération tant qu'elle n'est pas terminée (avec succès ou non). Encore une fois, il n'y a pas d'altération de l'inode de l'ancien fichier: un nouveau est créé et les processus déjà en cours n'en auront aucune connaissance, même s'il est associé à l'un des liens de l'ancien inode.
Avec la stratégie 3, l'étape de déplacement du nouveau fichier vers le nom existant supprime l'entrée de répertoire menant à l'ancien contenu et crée une entrée de répertoire menant au nouveau contenu. Cela se fait en une seule opération atomique, donc cette stratégie a un avantage majeur: si un processus ouvre le fichier à tout moment, il verra l'ancien contenu ou le nouveau contenu - il n'y a aucun risque d'obtenir du contenu mixte ou du fichier non existant.
Recompilation d'un fichier : lorsque vous utilisez gcc
(et le comportement est probablement similaire pour de nombreux autres compilateurs), vous utilisez la stratégie 2. Vous pouvez le voir en exécutant l'un strace
des processus de votre compilateur:
stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
- Le compilateur détecte que le fichier existe déjà via les appels système
stat
et lstat
.
- Le fichier n'est pas lié . Ici, bien qu'il ne soit plus accessible via le nom
a.out
, son inode et son contenu restent sur le disque, tant qu'ils sont utilisés par des processus déjà en cours d'exécution.
- Un nouveau fichier est créé et rendu exécutable sous le nom
a.out
. Il s'agit d'un tout nouvel inode et de nouveaux contenus dont les processus déjà en cours ne se soucient pas.
Maintenant, en ce qui concerne les bibliothèques partagées, le même comportement s'applique. Tant qu'un objet de bibliothèque est utilisé par un processus, il ne sera pas supprimé du disque, quelle que soit la façon dont vous modifiez ses liens. Chaque fois que quelque chose doit être chargé en mémoire, le noyau le fera via l'inode du fichier, et ignorera donc les modifications que vous avez apportées à ses liens (comme les associer à de nouveaux fichiers).