Je viens de me rendre compte que je suis capable de déplacer un programme actif en cours d'exécution vers un autre répertoire. D'après mon expérience, cela n'était pas possible sous MacO ou Windows. Comment ça marche dans Ubuntu?
Edit: Je pensais que ce n’était pas possible sur Mac mais apparemment, c’est possible en vérifiant les commentaires. Il n’est peut-être pas possible sous Windows. Merci pour toutes les réponses.
Laisse-moi décomposer.
Lorsque vous exécutez un exécutable, une séquence d'appels système est exécutée, notamment fork()
et execve()
:
fork()
crée un processus enfant du processus appelant, qui est (généralement) une copie exacte du parent, les deux exécutant toujours le même exécutable (en utilisant des pages de mémoire de copie sur écriture, donc efficace). Il retourne deux fois: dans le parent, il retourne le PID enfant. Dans l'enfant, il renvoie 0. Normalement, les appels de processus enfants s'exécutent immédiatement:
execve()
prend un chemin complet vers l'exécutable en tant qu'argument et remplace le processus appelant par l'exécutable. À ce stade, le processus nouvellement créé obtient son propre espace d'adressage virtuel, à savoir la mémoire virtuelle, et l'exécution commence à son point d'entrée (dans un état spécifié par les règles de la ABI de la plate-forme pour les nouveaux processus).
À ce stade, le chargeur ELF du noyau a mappé le segments de texte et de données de l'exécutable en mémoire, comme s'il avait utilisé l'appel système mmap()
(avec lecture seule partagée). et mappages privés en lecture-écriture). Le BSS est également mappé comme avec MAP_ANONYMOUS. (En passant, j'ignore les liens dynamiques ici pour des raisons de simplicité: l'éditeur de liens dynamique open()
s et mmap()
s de toutes les bibliothèques dynamiques avant de passer au point d'entrée de l'exécutable principal.)
En réalité, seules quelques pages sont chargées dans la mémoire à partir du disque avant qu'un nouvel exec () ed commence à exécuter son propre code. Les autres pages sont demande paginée selon les besoins, si/lorsque le processus touche ces parties de son espace d'adressage virtuel. (Le préchargement de pages de code ou de données avant de commencer à exécuter du code d'espace utilisateur est simplement une optimisation des performances.)
Le fichier exécutable est identifié par l'inode au niveau inférieur. Une fois le fichier commencé à être exécuté, le noyau conserve le contenu du fichier intact par la référence inode, et non par son nom, comme pour les descripteurs de fichier ouverts ou les mappages de mémoire sauvegardée. Ainsi, vous pouvez facilement déplacer l'exécutable vers un autre emplacement du système de fichiers ou même vers un autre système de fichiers. En guise de remarque, pour vérifier les différentes statistiques du processus, vous pouvez jeter un coup d’œil dans le répertoire /proc/PID
(PID est l’ID de processus du processus donné). Vous pouvez même ouvrir le fichier exécutable en tant que /proc/PID/exe
, même s'il n'a pas été lié au disque.
Maintenant, creusons le déménagement:
Lorsque vous déplacez un fichier au sein d'un même système de fichiers, l'appel système exécuté est rename()
, qui renomme simplement le fichier sous un autre nom. L'inode du fichier reste le même.
Alors qu'entre deux systèmes de fichiers différents, deux choses se passent:
Le contenu du fichier est d'abord copié dans le nouvel emplacement, par read()
et write()
Après cela, le fichier est dissocié du répertoire source à l'aide de unlink()
et le fichier recevra évidemment un nouvel inode sur le nouveau système de fichiers.
rm
est en fait juste unlink()
- file dans l'arborescence de répertoires. Par conséquent, si vous disposez de l'autorisation d'écriture sur le répertoire, vous aurez le droit de supprimer tout fichier de ce répertoire.
Maintenant, pour le plaisir, imaginez ce qui se passe lorsque vous déplacez des fichiers entre deux systèmes de fichiers et que vous n'avez pas la permission de unlink()
le fichier à partir de la source?
Eh bien, le fichier sera d'abord copié vers la destination (read()
, write()
), puis unlink()
échouera faute d'autorisations suffisantes. Donc, le fichier restera dans les deux systèmes de fichiers !!
Eh bien, c'est assez simple. Prenons un exécutable nommé/usr/local/bin/whoopdeedoo. Ce n'est qu'une référence à ce qu'on appelle inode (structure de base des fichiers sur les systèmes de fichiers Unix). C'est l'inode qui est marqué "en cours d'utilisation".
Désormais, lorsque vous supprimez ou déplacez le fichier/usr/local/whoopdeedoo, la seule chose qui est déplacée (ou effacée) est la référence à l'inode. L'inode lui-même reste inchangé. C'est fondamentalement ça.
Je devrais le vérifier, mais je pense que vous pouvez également le faire sur les systèmes de fichiers Mac OS X.
Windows adopte une approche différente. Pourquoi? Qui sait...? Je ne suis pas familier avec les composants internes de NTFS. Théoriquement, tous les systèmes de fichiers qui utilisent des références à des structures internes pour les noms de fichiers devraient pouvoir le faire.
J'admets que j'ai trop simplifié, mais allez lire la section "Implications" sur Wikipedia, qui fait beaucoup mieux que moi.
Une chose qui semble manquer à toutes les autres réponses est que: ne fois qu'un fichier est ouvert et qu'un programme contient un descripteur de fichier ouvert, le fichier sera non être supprimés du système jusqu'à la fermeture du descripteur de fichier.
Les tentatives de suppression de l'inode référencé seront retardées jusqu'à la fermeture du fichier: renommer un système de fichiers identique ou différent ne peut affecter le fichier ouvert, quel que soit le comportement du changement de nom, ni supprimer ou écraser explicitement le fichier par un nouveau. Le seul moyen de gâcher un fichier consiste à ouvrir explicitement son inode et son contenu, et non à effectuer des opérations sur le répertoire, telles que renommer/supprimer le fichier.
De plus, lorsque le noyau exécute un fichier, il garde une référence sur le fichier exécutable, ce qui empêche à nouveau toute modification de celui-ci pendant son exécution.
Donc, même si ressemble à si vous êtes capable de supprimer/déplacer les fichiers qui constituent un programme en cours d'exécution, le contenu de ces fichiers est conservé en mémoire jusqu'à ce que programme se termine.
Dans un système de fichiers Linux, lorsque vous déplacez un fichier, tant qu'il ne franchit pas les limites du système de fichiers (read: reste sur le même disque/partition), vous ne modifiez que l'inode de ..
(répertoire parent) par celui du nouveau emplacement. Les données réelles ne se sont pas déplacées du tout sur le disque, juste le pointeur pour que le système de fichiers sache où les trouver.
C’est la raison pour laquelle les opérations de déplacement sont si rapides et probablement la raison pour laquelle il n’ya pas de problème à déplacer un programme en cours, car vous ne déplacez pas réellement le programme lui-même.
Cela est possible car le déplacement d'un programme n'a pas d'incidence sur les processus en cours d'exécution lancés lors de son lancement.
Une fois qu'un programme est lancé, ses bits sur le disque sont protégés contre l'écrasement, mais il n'est pas nécessaire de protéger le fichier à renommer, de le déplacer vers un autre emplacement du même système de fichiers, ce qui revient à renommer le fichier ou de le déplacer. dans un système de fichiers différent, ce qui revient à copier le fichier ailleurs, puis à le supprimer.
Supprimer un fichier en cours d'utilisation, soit parce qu'un processus a un descripteur de fichier ouvert, soit parce qu'un processus l'exécute, ne supprime pas les données du fichier, qui restent référencées par l'inode du fichier mais suppriment uniquement l'entrée de répertoire. c'est-à-dire un chemin à partir duquel l'inode peut être atteint.
Notez que le lancement d'un programme ne charge pas tout en même temps dans la mémoire (physique). Au contraire, seul le strict minimum requis pour le démarrage du processus est chargé. Ensuite, les pages requises sont chargées à la demande pendant toute la durée du processus. c'est ce qu'on appelle la pagination à la demande. S'il y a une pénurie de RAM, le système d'exploitation est libre de libérer le RAM contenant ces pages afin qu'il soit possible pour un processus de charger plusieurs fois la même page à partir de l'inode exécutable.
La raison pour laquelle cela n’était pas possible avec Windows est probablement due au fait que le système de fichiers sous-jacent (FAT) ne prenait pas en charge le concept de division des entrées de répertoire par rapport aux inodes. Cette limitation n'était plus présente avec NTFS mais la conception du système d'exploitation a été conservée pendant une longue période, ce qui a rendu la contrainte désagréable de devoir redémarrer pour installer une nouvelle version d'un binaire, ce qui n'est plus le cas avec les versions récentes de Windows.
Fondamentalement, dans Unix et ses dépendances, un nom de fichier (y compris le chemin du répertoire qui y conduit) est utilisé pour associer/rechercher un fichier lorsque ouverture (Exécuter un fichier est un moyen de l’ouvrir dans un manière). Après ce moment, l'identité du fichier (via son "inode") est établie et n'est plus remise en question. Vous pouvez supprimer le fichier, le renommer, modifier ses autorisations. Tant que tout processus ou chemin de fichier a un handle sur ce fichier/inode, il restera comme un canal entre processus (en fait, dans l'historique UNIX, un canal était un inode sans nom avec une taille qui vient d’être insérée dans la référence de stockage sur disque "blocs directs" dans l’inode (environ 10 blocs).
Si vous avez un visualiseur PDF ouvert sur un fichier PDF, vous pouvez le supprimer et en ouvrir un nouveau portant le même nom. Tant que l'ancien visualiseur est ouvert, vous pouvez toujours accéder à l’ancien fichier (sauf s’il surveille activement le système de fichiers afin de remarquer la disparition du fichier sous son nom original).
Les programmes ayant besoin de fichiers temporaires peuvent simplement ouvrir un tel fichier sous un nom quelconque, puis immédiatement le supprimer (ou plutôt son entrée de répertoire) tant qu'il est toujours ouvert. Ensuite, le fichier n'est plus accessible par son nom, mais tous les processus ayant un descripteur de fichier ouvert peuvent toujours y accéder, et s'il y a une sortie inattendue du programme, le fichier sera supprimé et la mémoire récupérée automatiquement.
Ainsi, le chemin d'accès à un fichier n'est pas une propriété du fichier lui-même (en fait, les liens physiques peuvent fournir plusieurs chemins d'accès différents) et est nécessaire uniquement pour l'ouvrir, et non pour un accès continu par les processus l'ayant déjà ouvert.