Les commandes chaînées sont-elles atomiques?

8

S'il y avait un processus continuellement écrit dans un fichier et que je voulais prendre le contrôle du fichier avec root, je pouvais faire quelque chose comme ceci:

sudo rm somefile; sudo touch somefile

Est-il possible que le processus d'ajout soit ajouté au fichier entre ces deux commandes? Si oui, existe-t-il un moyen de garantir qu'aucune autre commande ne soit exécutée entre les deux?

Dark Egregious
la source
4
Cela pourrait être un problème XY . Pouvez-vous expliquer le problème sous-jacent que vous essayez de résoudre?
Nate Eldredge du
1
@NateEldredge: Absolument pas. Je peux comprendre pourquoi cela semble être le cas en fonction de mon hypothèse artificielle. Je n'ai même pas de problème. Juste curieux.
Dark Egregious du
5
Le processus va probablement continuer à écrire dans le fichier supprimé et ne même pas essayer d'ouvrir le nouveau.
Random832
Mon hypothèse dans l'hypothétique s'est avérée un peu erronée, mais je suppose que vous pouvez changer "ajouter" à "ouvrir" ou "toucher" et cela aurait toujours du sens.
Dark Egregious du

Réponses:

12

Une ligne de commande chaînée est essentiellement un petit script shell; il exécutera la première commande en utilisant la procédure fork + exec habituelle, attendez qu'elle se termine, puis exécutez la seconde de la même manière. Entre les deux commandes, il y a un certain temps arbitraire que le shell prend dans sa comptabilité et son traitement, pendant lequel le multitraitement ordinaire a lieu et d'autres processus arbitraires peuvent faire d'autres choses arbitraires. Donc la réponse est non'. (Si vous faites cela, vous constaterez que l'entrée de répertoire de somefiledisparaît, mais le fichier lui-même reste (puisqu'il est ouvert par un processus) jusqu'à sa fermeture. L'espace disque utilisé par le fichier ne sera pas récupéré jusqu'à ce que cela se produise. , la touchcommande créera un nouveau fichier sans rapport avec le même nom et le même chemin d'accès.)

Si vous voulez changer la propriété du fichier en root, faites-le sudo chown root:root somefile(bien que je ne sais pas comment cela affectera les processus avec un descripteur de fichier ouvert). Si vous souhaitez détruire le contenu du fichier actuel, essayez truncate -s 0 somefile(le processus en cours continuera de s'ajouter au fichier maintenant vide). Si c'est autre chose, clarifiez peut-être ce que vous voulez faire.

Tom Hunt
la source
Merci Tom. Je n'étais pas tellement préoccupé par ma situation hypothétique. Je me demandais simplement s'il était possible de faire quelque chose comme ça.
Dark Egregious du
En ce qui concerne la question générale, je ne pense pas qu'il existe vraiment un moyen de garantir l'atomicité dans un script shell. Vous pouvez utiliser des fichiers de verrouillage et autres pour vous assurer que deux processus coopérants ne s'appuient pas l'un sur l'autre, et vous pouvez utiliser des autorisations pour vous assurer qu'un processus arbitraire ne peut pas marcher sur votre travail. Mais empêcher quoi que ce soit d'autre de s'exécuter perturberait fortement le multitraitement du noyau; Je ne pense pas que le code ring-3 puisse le faire du tout.
Tom Hunt du
Vous pouvez utiliser des verrous de fichiers obligatoires s'ils sont activés et disponibles sur votre système de fichiers.
roaima
5
chowning somefile n'affectera pas les processus qui ont un handle de fichier vers somefile.
PSkocik
Par exemple, vous pouvez le faire exec 3>somefile; sudo chmod 0600 somefile; echo hello world >&3; sudo cat somefile; exec 3>&-.
PSkocik
6

Non. Une rmcommande suivie d'une touchcommande n'est pas atomique du tout. Il s'écoulera beaucoup de temps entre les deux commandes, éventuellement dans la plage des millisecondes. Beaucoup de choses peuvent se produire pendant cette période. Si vous n'avez pas de chance, vos informations d'identification sudo peuvent même expirer.

Un seul programme appelant les appels unlinket openlaisserait une fenêtre beaucoup plus courte pour une course, mais cela pourrait encore arriver.

Une approche plus sûre serait de créer le nouveau fichier avec un nom temporaire et d'utiliser l' renameappel système. "Remplacer" un nom avec l' renameappel système est garanti atomique. Ceci pourrait être réalisé en utilisant touchet mv.

Mais un processus qui a ouvert l'ancien fichier pour l'écriture peut continuer à y écrire longtemps après sa suppression. Ce sera le cas à la fois pour un fichier supprimé à l'aide unlinket pour un fichier supprimé à l'aide rename.

kasperd
la source
3

Une fois qu'un processus a un descripteur de fichier ouvert pour la lecture, peu importe ce que vous faites avec la propriété ou les autorisations: le processus peut continuer à accéder au fichier. Vous pouvez même supprimer le fichier et le processus pourra continuer à y accéder via le descripteur de fichier.

Considérez les autorisations comme une porte de contrôle pour obtenir un descripteur de fichier.

Si vous souhaitez créer un fichier de manière atomique, au lieu de faire ceci:

sudo rm somefile
sudo touch somefile

vous pourriez considérer ceci:

sudo touch anotherfile
sudo perl -e "rename 'anotherfile', 'somefile'"

qui remplacera atomiquement somefilepar anotherfile. (J'aurais préféré utiliser mv -fmais je ne trouve pas de déclaration garantissant que cela appelle l' rename(2)appel système.


Vous pourriez peut-être mettre à jour votre question pour expliquer ce que vous entendez par «prendre le contrôle du fichier». Vous pouvez avoir un problème XY ici.

roaima
la source
Utilisez mv -fpour obtenir un rename(2)appel système. Vous avez raison ln, car il utilise toujours l' link(2)appel système, qui ne peut pas remplacer atomiquement. ln -ffait juste unlink(2)sur la cible en premier.
Peter Cordes
@PeterCordes ah oui bien sûr, merci. J'avais lnsur le cerveau. Je ne savais pas trop comment rename(2)m'impliquer et il était trop tard pour aller plus loin
roaima
POSIX garantit que mv"doit effectuer des actions équivalentes à" rename(old, new). pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html . mv -fagit comme mv -isi la destination n'est pas accessible en écriture, mais ce n'est pas le cas cp -fou ln -flà où elle se dissociera en premier.
Peter Cordes
2

Non mais

sudo rm somefile; sudo touch somefile

est sûr dans la plupart des situations.

La plupart des processus ouvrent des fichiers, puis utilisent le descripteur de fichier acquis pour accéder au contenu du fichier.

Si un processus ouvre un fichier, en sh:

 exec 3>somefile

Ensuite, un autre processus est libre de dissocier (= supprimer) certains fichiers et le descripteur de fichier sur lequel certains fichiers était ouvert par le premier processus (3 dans ce cas) continuera de faire référence au contenu d'origine de certains fichiers, qui est maintenant un fichier dans les limbes.

sudo touch somefile

créera un nouveau fichier non lié et tous les processus qui avaient l'ancien fichier ouvert et qui n'utilisent désormais qu'un descripteur de fichier pour s'y référer ne seront pas affectés car ils se réfèrent à un fichier différent - celui qui est maintenant dans les limbes.

Si un processus non root tente de faire référence à un fichier par son nom, il obtiendra une erreur EPERM, car le nouveau somefile appartient à la racine.

Si vous souhaitez empêcher plusieurs processus sous le même utilisateur (par exemple, root) de corrompre un fichier, Linux a un verrouillage de fichier obligatoire et consultatif. Vous pouvez utiliser la flockcommande dans les scripts shell pour le verrouillage des fichiers (consultez la page de manuel pour plus d'informations).

PSkocik
la source