Permettez-moi de le 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 (principalement) 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, il est donc efficace). Il renvoie deux fois: dans le parent, il renvoie le PID enfant. Chez l'enfant, il renvoie 0. Normalement, les appels de processus enfant s'exécutent immédiatement:
execve()
prend un chemin complet vers l'exécutable comme argument et remplace le processus appelant par l'exécutable. À ce stade, le processus nouvellement créé obtient son propre espace d'adressage virtuel, c'est-à-dire 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 plateforme ABI pour les nouveaux processus).
À ce stade, le chargeur ELF du noyau a mappé les segments de texte et de données de l'exécutable en mémoire, comme s'il avait utilisé l' mmap()
appel système (avec des mappages partagés en lecture seule et privés en lecture-écriture respectivement). Le BSS est également mappé comme avec MAP_ANONYMOUS. (BTW, j'ignore les liens dynamiques ici pour plus de simplicité: l'éditeur de liens dynamiques open()
et mmap()
toutes les bibliothèques dynamiques avant de passer au point d'entrée de l'exécutable principal.)
Seules quelques pages sont réellement chargées dans la mémoire à partir du disque avant qu'un nouvel exécuteur () commence à exécuter son propre code. Des pages supplémentaires sont demandées en fonction des besoins, si / quand le processus touche ces parties de son espace d'adressage virtuel. (Le préchargement des pages de code ou de données avant de commencer à exécuter le code de l'espace utilisateur n'est qu'une optimisation des performances.)
Le fichier exécutable est identifié par l'inode au niveau inférieur. Une fois que le fichier a commencé à être exécuté, le noyau conserve le contenu du fichier intact par la référence d'inode, pas par nom de fichier, comme pour les descripteurs de fichier ouverts ou les mappages de mémoire sauvegardés sur fichier. Vous pouvez donc facilement déplacer l'exécutable vers un autre emplacement du système de fichiers ou même sur un autre système de fichiers. En remarque, pour vérifier les différentes statistiques du processus, vous pouvez consulter le /proc/PID
répertoire (PID est l'ID de processus du processus donné). Vous pouvez même ouvrir le fichier exécutable car /proc/PID/exe
, même s'il a été dissocié du disque.
Maintenant, creusons le mouvement:
Lorsque vous déplacez un fichier dans un même système de fichiers, l'appel système qui est 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 produisent:
Le contenu du fichier est d'abord copié vers le nouvel emplacement, par read()
etwrite()
Après cela, le fichier est dissocié du répertoire source à l'aide unlink()
et, évidemment, le fichier obtiendra un nouvel inode sur le nouveau système de fichiers.
rm
est en fait juste unlink()
-ing le fichier donné de l'arborescence de répertoires, donc avoir l'autorisation d'écriture sur le répertoire vous donnera le droit suffisant pour supprimer n'importe quel 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 l'autorisation d'accéder au unlink()
fichier à partir de la source?
Eh bien, le fichier sera copié vers la destination dans un premier temps ( read()
, write()
) puis unlink()
échouera en raison d'une autorisation insuffisante. Ainsi, le fichier restera dans les deux systèmes de fichiers !!