Le 19 août 2013, Randal L. Schwartz a posté ce script shell, qui visait à garantir, sous Linux, "qu'une seule instance du [script] est en cours d'exécution, sans condition de concurrence ni devoir nettoyer les fichiers de verrouillage":
#!/bin/sh
# randal_l_schwartz_001.sh
(
if ! flock -n -x 0
then
echo "$$ cannot get flock"
exit 0
fi
echo "$$ start"
sleep 10 # for testing. put the real task here
echo "$$ end"
) < $0
Il semble fonctionner comme annoncé:
$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end
[1]+ Done ./randal_l_schwartz_001.sh
$
Voici ce que je comprends:
- Le script redirige (
<
) une copie de son propre contenu (ie depuis$0
) vers le STDIN (ie descripteur de fichier0
) d'un sous-shell. - Dans le sous-shell, le script tente d'obtenir un verrou exclusif (
flock -n -x
) non bloquant sur le descripteur de fichier0
.- Si cette tentative échoue, le sous-shell se ferme (tout comme le script principal, car il n'y a rien d'autre à faire).
- Si la tentative réussit, le sous-shell exécute la tâche souhaitée.
Voici mes questions:
- Pourquoi le script doit-il rediriger, vers un descripteur de fichier hérité par le sous-shell, une copie de son propre contenu plutôt que, disons, le contenu d'un autre fichier? (J'ai essayé de rediriger à partir d'un fichier différent et de réexécuter comme ci-dessus, et l'ordre d'exécution a changé: la tâche sans arrière-plan a obtenu le verrou avant l'arrière-plan. Donc, peut-être que l'utilisation du contenu du fichier évite les conditions de concurrence; mais comment?)
- Pourquoi le script doit-il de toute façon rediriger vers un descripteur de fichier hérité par le sous-shell, une copie du contenu d'un fichier?
- Pourquoi le fait de maintenir un verrou exclusif sur le descripteur de fichier
0
dans un shell empêche une copie du même script, exécuté dans un shell différent, d'obtenir un verrou exclusif sur le descripteur de fichier0
? Ne pas coquilles ont leurs propres copies séparées des descripteurs de fichier standard (0
,1
et2
, par exemple STDIN, STDOUT et STDERR)?
linux
shell-script
io-redirection
subshell
lock
sampablokuper
la source
la source
Réponses:
Vous pouvez utiliser n'importe quel fichier, tant que toutes les copies du script utilisent le même. L'utilisation
$0
lie simplement le verrou au script lui-même: si vous copiez le script et le modifiez pour une autre utilisation, vous n'avez pas besoin de trouver un nouveau nom pour le fichier de verrouillage. C'est pratique.Si le script est appelé via un lien symbolique, le verrou se trouve sur le fichier réel et non sur le lien.
(Bien sûr, si un processus exécute le script et lui donne une valeur composée comme argument zéro au lieu du chemin réel, alors cela se casse. Mais c'est rarement fait.)
Êtes-vous sûr que c'était à cause du fichier utilisé, et pas seulement d'une variation aléatoire? Comme avec un pipeline, il n'y a vraiment aucun moyen de savoir dans quel ordre les commandes s'exécutent
cmd1 & cmd
. Cela dépend principalement du planificateur du système d'exploitation. J'obtiens des variations aléatoires sur mon système.Il semblerait que le shell lui-même contienne une copie de la description du fichier contenant le verrou, au lieu de simplement l'
flock
utilitaire qui le détient. Un verrou créé avecflock(2)
est libéré lorsque les descripteurs de fichier le contenant sont fermés.flock
a deux modes, soit pour prendre un verrou basé sur un nom de fichier et exécuter une commande externe (auquel casflock
contient le descripteur de fichier ouvert requis), soit pour prendre un descripteur de fichier de l'extérieur, de sorte qu'un processus externe est responsable de la conservation il.Notez que le contenu du fichier n'est pas pertinent ici et qu'aucune copie n'a été effectuée. La redirection vers le sous-shell ne copie aucune donnée autour d'elle-même, elle ouvre simplement une poignée au fichier.
Oui, mais le verrou se trouve sur le fichier , pas sur le descripteur de fichier. Une seule instance ouverte du fichier peut contenir le verrou à la fois.
Je pense que vous devriez pouvoir faire de même sans le sous-shell, en utilisant
exec
pour ouvrir une poignée vers le fichier de verrouillage:la source
{ }
au lieu de( )
fonctionnerait également et éviterait le sous-shell.exec
.Un verrou de fichier est attaché à un fichier, via une description de fichier . À un niveau élevé, la séquence d'opérations dans une instance du script est la suivante:
Maintenir le verrou empêche l'exécution d'une autre copie du même script, car c'est ce que font les verrous. Tant qu'un verrou exclusif sur un fichier existe quelque part sur le système, il est impossible de créer une deuxième instance du même verrou, même via une description de fichier différente.
L'ouverture d'un fichier crée une description de fichier . Il s'agit d'un objet noyau qui n'a pas beaucoup de visibilité directe dans les interfaces de programmation. Vous accédez indirectement à une description de fichier via des descripteurs de fichier, mais vous le considérez normalement comme l'accès au fichier (lecture ou écriture de son contenu ou de ses métadonnées). Un verrou est l'un des attributs qui sont une propriété de la description du fichier plutôt qu'un fichier ou un descripteur.
Au début, lorsqu'un fichier est ouvert, la description du fichier a un seul descripteur de fichier, mais d'autres descripteurs peuvent être créés soit en créant un autre descripteur (la
dup
famille d'appels système), soit en forçant un sous-processus (après quoi le parent et le l'enfant a accès à la même description de fichier). Un descripteur de fichier peut être fermé explicitement ou lorsque le processus dans lequel il se trouve meurt. Lorsque le dernier descripteur de fichier joint à un fichier est fermé, la description du fichier est fermée.Voici comment la séquence d'opérations ci-dessus affecte la description du fichier.
<$0
ouvre le fichier de script dans le sous-shell, créant une description de fichier. À ce stade, un descripteur de fichier unique est attaché à la description: le numéro de descripteur 0 dans le sous-shell.flock
et attend sa sortie. Pendant que flock est en cours d'exécution, deux descripteurs sont attachés à la description: le numéro 0 dans le sous-shell et le numéro 0 dans le processus de flock. Lorsque flock prend le verrou, cela définit une propriété de la description du fichier. Si une autre description de fichier a déjà un verrou sur le fichier, flock ne peut pas prendre le verrou, car il s'agit d'un verrou exclusif.La raison pour laquelle le script utilise une redirection
$0
est que la redirection est le seul moyen d'ouvrir un fichier dans le shell, et le maintien d'une redirection active est le seul moyen de garder un descripteur de fichier ouvert. Le sous-shell ne lit jamais à partir de son entrée standard, il suffit de le garder ouvert. Dans une langue qui donne un accès direct aux appels ouverts et fermés, vous pouvez utiliserVous pouvez réellement obtenir la même séquence d'opérations dans le shell si vous effectuez la redirection avec le programme
exec
intégré.Le script peut utiliser un descripteur de fichier différent s'il souhaite continuer à accéder à l'entrée standard d'origine.
ou avec un sous-shell:
Le verrou ne doit pas nécessairement se trouver sur le fichier de script. Il peut s'agir de n'importe quel fichier pouvant être ouvert en lecture (il doit donc exister, il doit s'agir d'un type de fichier pouvant être lu, tel qu'un fichier normal ou un canal nommé mais pas un répertoire, et le processus de script doit avoir l'autorisation de le lire). Le fichier de script a l'avantage qu'il est garanti d'être présent et lisible (sauf dans le cas de bord où il a été supprimé en externe entre le moment où le script a été appelé et le moment où le script arrive à la
<$0
redirection).Tant qu'il
flock
réussit, et que le script se trouve sur un système de fichiers où les verrous ne sont pas bogués (certains systèmes de fichiers réseau tels que NFS peuvent être bogués), je ne vois pas comment l'utilisation d'un fichier de verrouillage différent pourrait permettre une condition de concurrence critique. Je soupçonne une erreur de manipulation de votre part.la source
Le fichier utilisé pour le verrouillage n'a pas d'importance, le script l'utilise
$0
car il s'agit d'un fichier connu pour exister.L'ordre dans lequel les verrous sont obtenus sera plus ou moins aléatoire, selon la vitesse à laquelle votre machine est en mesure de démarrer les deux tâches.
Vous pouvez utiliser n'importe quel descripteur de fichier, pas nécessairement 0. Le verrou est maintenu sur le fichier ouvert au descripteur de fichier, pas sur le descripteur lui-même.
la source