Parfois, vous devez vous assurer qu'une seule instance d'un script shell est en cours d'exécution en même temps.
Par exemple, un travail cron exécuté via crond ne fournit pas de verrouillage (par exemple, le crond Solaris par défaut).
Un modèle commun pour implémenter le verrouillage est le code suivant:
#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then # 'test' -> race begin
echo Job is already running\!
exit 6
fi
touch $LOCK # 'set' -> race end
# do some work
rm $LOCK
Bien sûr, un tel code a une condition de concurrence. Il existe une fenêtre temporelle dans laquelle l'exécution de deux instances peut avancer après la ligne 3 avant que l'une d'elles puisse toucher le $LOCK
fichier.
Pour un travail cron, cela ne pose généralement pas de problème, car vous disposez d'un intervalle de minutes entre deux invocations.
Mais des problèmes peuvent survenir - par exemple, lorsque le fichier de verrouillage est sur un serveur NFS -, cela se bloque. Dans ce cas, plusieurs tâches cron peuvent bloquer sur la ligne 3 et se mettre en file d'attente. Si le serveur NFS est à nouveau actif, vous avez un troupeau de travaux en parallèle.
En cherchant sur le Web, j'ai trouvé l'outil lockrun qui semble être une bonne solution à ce problème. Avec lui, vous exécutez un script qui nécessite un verrouillage comme ceci:
$ lockrun --lockfile=/var/tmp/mylock myscript.sh
Vous pouvez le mettre dans un wrapper ou l'utiliser depuis votre crontab.
Il utilise lockf()
(POSIX) s'il est disponible et revient à flock()
(BSD). Et le lockf()
support sur NFS devrait être relativement répandu.
Y a-t-il des alternatives à lockrun
?
Qu'en est-il des autres démons cron? Y a-t-il des clients communs qui soutiennent le verrouillage de manière saine? Un rapide coup d’œil dans la page de manuel de Vixie Crond (par défaut sur les systèmes Debian / Ubuntu) n’affiche rien sur le verrouillage.
Serait-ce une bonne idée d'inclure un outil comme lockrun
dans coreutils ?
À mon avis, il met en œuvre un thème très similaire à timeout
, nice
et amis.
kill
édité; et il semble être une bonne pratique de stocker son propre pid dans le fichier verrou plutôt que de le toucher.Réponses:
Voici un autre moyen de verrouiller un script shell afin d’empêcher la situation de concurrence critique décrite ci-dessus, selon laquelle deux tâches peuvent toutes deux passer à la ligne 3. L’
noclobber
option fonctionnera dans ksh et bash. Ne pas utiliserset noclobber
car vous ne devriez pas utiliser de script dans csh / tcsh. ;)YMMV avec verrouillage sur NFS (vous savez, lorsque les serveurs NFS ne sont pas accessibles), mais en général, il est beaucoup plus robuste que par le passé. (Il ya 10 ans)
Si vous avez des tâches cron qui font la même chose au même moment, à partir de plusieurs serveurs, mais que vous n'avez besoin que d'une seule instance pour être exécutée, une tâche comme celle-ci pourrait fonctionner pour vous.
Je n'ai aucune expérience de lockrun, mais avoir un environnement de verrouillage prédéfini avant le script en cours d'exécution peut aider. Ou peut-être pas. Vous définissez simplement le test du fichier lock en dehors de votre script dans un wrapper et, théoriquement, vous ne pourriez pas simplement atteindre la même situation de concurrence critique si deux tâches étaient appelées par lockrun exactement au même moment, comme avec le la solution du script?
De toute façon, le verrouillage de fichier est en quelque sorte un respect du comportement du système, et tous les scripts qui ne vérifient pas l'existence du fichier verrou avant son exécution feront tout ce qu'ils vont faire. En mettant en place le test du fichier verrou et un comportement correct, vous résoudrez 99% des problèmes potentiels, voire 100%.
Si vous rencontrez beaucoup de problèmes de situation de fichiers verrouillés, cela peut être le signe d'un problème plus important, comme le fait que votre travail ne soit pas correctement chronométré, ou si l'intervalle n'est pas aussi important que le travail terminé, peut-être que votre travail est mieux adapté pour être démonisé. .
EDIT BELOW - 2016-05-06 (si vous utilisez KSH88)
Sur la base du commentaire de @Clint Pachl ci-dessous, si vous utilisez ksh88, utilisez
mkdir
plutôt quenoclobber
. Cela atténue généralement une éventuelle condition de concurrence, mais ne la limite pas totalement (même si le risque est minime). Pour plus d'informations, lisez le lien que Clint a posté ci-dessous .De plus, si vous avez besoin de créer des fichiers tmp dans votre script, vous pouvez utiliser le
lockdir
répertoire pour eux, sachant qu'ils seront nettoyés à la fin du script.Pour les bashs plus modernes, la méthode noclobber au sommet devrait convenir.
la source
lockf()
appel système - quand il est sauvegardé, tous les processus sont repris mais un seul processus remporte le verrouillage. Aucune condition de concurrence. Je ne rencontre pas souvent de tels problèmes avec les tâches cron, le contraire est vrai, mais c'est un problème quand il vous frappe, il peut potentiellement causer beaucoup de douleur.set -o noclobber && echo "$$" > "$lockfile"
obtenir un repli sûr lorsque le shell ne supporte pas l'option noclobber.noclobber
option peut être sujette à des conditions de concurrence. Voir mywiki.wooledge.org/BashFAQ/045 pour des pistes de réflexion.noclobber
(ou-C
) dans ksh88 ne fonctionne pas car ksh88 n’utilise pasO_EXCL
pournoclobber
. Si vous utilisez un nouveau shellJe préfère utiliser des liens durs.
Les liens physiques sont atomiques par rapport à NFS et, pour la plupart, mkdir l’est également . Utiliser
mkdir(2)
oulink(2)
sont à peu près les mêmes, à un niveau pratique; Je préfère simplement utiliser des liens durs, car davantage d'implémentations de NFS permettaient des liens durs atomiques que des liaisons atomiquesmkdir
. Avec les versions modernes de NFS, vous ne devriez plus avoir à vous soucier de cela.la source
Je comprends que
mkdir
c'est atomique, alors peut-être:la source
mkdir()
sur si NFS (> = 3) est normalisé pour être atomique.mkdir
être atomique (elle le fait pourrename
). En pratique, on sait que certaines implémentations ne le sont pas. En relation: un fil intéressant, comprenant une contribution de l'auteur de GNU arch .Un moyen facile consiste à utiliser
lockfile
généralement leprocmail
paquet.la source
sem
ce qui fait partie desparallel
outils GNU peut être ce que vous cherchez:Un péché:
sortie:
Notez que l'ordre n'est pas garanti. De plus, la sortie n'est affichée qu'une fois terminée (irritant!). Mais même dans ce cas, c’est le moyen le plus concis que je connaisse pour se protéger des exécutions simultanées, sans se soucier des fichiers verrouillés, des tentatives et du nettoyage.
la source
sem
poignée est-il abattu à mi-exécution?Je l'utilise
dtach
.la source
J'utilise l'outil de ligne de commande "flock" pour gérer les verrous dans mes scripts bash, comme décrit ici et ici . J'ai utilisé cette méthode simple depuis la page de manuel flock, pour exécuter certaines commandes dans un sous-shell ...
Dans cet exemple, il échoue avec le code de sortie de 1 s'il ne peut pas acquérir le fichier de verrouillage. Mais flock peut également être utilisé de manière à ne pas nécessiter que des commandes soient exécutées dans un sous-shell :-)
la source
flock()
appel système ne fonctionne pas sur NFS .flock()
et est donc problématique sur NFS. Btw, dans l'intervalle, flock () sous Linux revient maintenant àfcntl()
lorsque le fichier est situé sur un montage NFS. Par conséquent, dans un environnement NFS uniquement Linux, fonctionneflock()
désormais sur NFS.N'utilisez pas de fichier.
Si votre script est exécuté comme ceci, par exemple:
Vous pouvez détecter si cela fonctionne en utilisant:
la source
my_script
? Dans le cas où une autre instance est en cours d'exécution - nerunning_proc
contient pas deux lignes correspondantes? J'aime l'idée, mais bien sûr, vous obtiendrez de faux résultats lorsqu'un autre utilisateur exécute un script du même nom ...$!
plutôt que$$
dans votre exemple.grep -v $$
). Fondamentalement, j'essayais de proposer une approche différente du problème.Pour une utilisation réelle, vous devez utiliser la réponse la plus votée .
Cependant, je souhaite aborder diverses approches brisées et semi-exploitables,
ps
ainsi que leurs nombreuses mises en garde, car je constate que de nombreuses personnes les utilisent.Cette réponse est vraiment la réponse à "Pourquoi ne pas utiliser
ps
etgrep
gérer le verrouillage dans la coque?"Approche brisée # 1
Premièrement, une approche donnée dans une autre réponse qui a eu quelques votes positifs malgré le fait que cela ne fonctionnait pas (et ne pourrait jamais) et n’avait clairement jamais été testée:
Corrigeons les erreurs de syntaxe et les
ps
arguments cassés et obtenons:Ce script quittera toujours 6 à chaque fois, quelle que soit la manière dont vous l'exécutez.
Si vous l'exécutez avec
./myscript
, laps
sortie sera simplement12345 -bash
, ce qui ne correspond pas à la chaîne requise12345 bash ./myscript
, donc cela échouera.Si vous le lancez avec
bash myscript
, les choses deviennent plus intéressantes. Le processus bash fourches pour exécuter le pipeline, et l' enfant shell court leps
etgrep
. Le shell original et le shell enfant apparaîtront dans laps
sortie, comme ceci:Ce n'est pas la sortie attendue
$$ bash $0
, votre script va donc se fermer.Approche brisée # 2
Maintenant, en toute justice pour l'utilisateur qui a écrit l'approche cassée n ° 1, j'ai fait quelque chose de similaire moi-même lorsque j'ai essayé pour la première fois:
Cela fonctionne presque . Mais le fait de forcer pour faire fonctionner le tuyau jette cela au large. Donc, celui-ci sortira toujours, aussi.
Approche peu fiable n ° 3
Cette version évite le problème de forçage de pipeline dans l'approche n ° 2 en obtenant d'abord tous les PID qui ont le script actuel dans leurs arguments de ligne de commande, puis en filtrant cette pidlist, séparément, pour omettre le PID du script actuel.
Cela pourrait fonctionner ... à condition qu'aucun autre processus n'ait une ligne de commande correspondant à l'élément
$0
et que le script soit toujours appelé de la même manière (par exemple, s'il est appelé avec un chemin relatif puis un chemin absolu, la dernière instance ne remarquera pas le premier. )Approche peu fiable n ° 4
Alors, que se passe-t-il si nous omettons de vérifier la ligne de commande complète, car cela n’indiquera peut-être pas un script en cours d’exécution, et
lsof
rechercherons à la place tous les processus pour lesquels ce script est ouvert?Eh bien, oui, cette approche n’est pas si mauvaise en réalité:
Bien sûr, si une copie du script est en cours d'exécution, la nouvelle instance démarrera correctement et vous aurez deux copies en cours d'exécution.
Ou si le script en cours d'exécution est modifié (par exemple avec Vim ou avec a
git checkout
), la "nouvelle" version du script démarrera sans problème, car Vim et legit checkout
résultat, un nouveau fichier (un nouvel inode) à la place du fichier le vieux.Cependant, si le script n'est jamais modifié ni copié, cette version est plutôt bonne. Il n'y a pas de condition de concurrence, car le fichier de script doit déjà être ouvert avant que le contrôle puisse être atteint.
Il peut toujours y avoir des faux positifs si le fichier de script est ouvert par un autre processus, mais notez que même s'il est ouvert à l'édition dans Vim, vim ne tient pas le fichier de script ouvert et ne génère pas de faux positifs.
Mais rappelez-vous, n'utilisez pas cette approche si le script peut être édité ou copié car vous obtiendrez de faux négatifs, c'est-à-dire plusieurs instances s'exécutant en même temps. Le fait que l'édition avec Vim ne génère pas de faux positifs ne devrait donc pas avoir d'importance. à toi. Je le mentionne cependant car l'approche n ° 3 ne donne de faux positifs (c. -à- refuse de démarrer) si vous avez le script ouvert avec Vim.
Alors que faire, alors?
La réponse la plus votée à cette question donne une bonne approche solide.
Peut-être pourriez-vous en écrire un meilleur ... mais si vous ne comprenez pas tous les problèmes et toutes les mises en garde concernant toutes les approches ci-dessus, il est peu probable que vous écriviez une méthode de verrouillage qui les évite toutes.
la source
À l'aide de l' outil FLOM (Free LOck Manager) , la sérialisation des commandes devient aussi simple que l'exécution
FLOM vous permet d'implémenter des cas d'utilisation plus souples (verrouillage distribué, lecteurs / rédacteurs, ressources numériques, etc.), comme expliqué ici: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
la source
Voici quelque chose que j'ajoute parfois sur un serveur pour gérer facilement les conditions de concurrence pour tous les travaux de la machine. Il en va de même pour le message de Tim Kennedy, mais vous obtenez ainsi le traitement de la course en ajoutant seulement une ligne à chaque script bash qui en a besoin.
Placez le contenu ci-dessous dans / opt / racechecker / racechecker, par exemple:
Voici comment l'utiliser. Notez la ligne après le shebang:
La façon dont cela fonctionne est qu’il détermine le nom principal du fichier bashscript et crée un fichier pid sous "/ tmp". Il ajoute également un auditeur au signal d'arrivée. L'écouteur supprimera le pidfile lorsque le script principal se terminera correctement.
À la place, si un fichier pid existe lors du lancement d'une instance, l'instruction if contenant le code à l'intérieur de la deuxième instruction if sera exécutée. Dans ce cas, j'ai décidé de lancer un mail d'alarme lorsque cela se produit.
Que se passe-t-il si le script plante?
Un autre exercice consisterait à gérer les collisions. Idéalement, le fichier pid devrait être supprimé même si le script principal se bloque pour une raison quelconque, cela ne se fait pas dans ma version ci-dessus. Cela signifie que si le script se bloque, le pidfile devra être supprimé manuellement pour restaurer la fonctionnalité.
En cas de crash du système
C'est une bonne idée de stocker le fichier pidfile / lockfile sous par exemple / tmp. De cette façon, vos scripts continueront définitivement à s'exécuter après un plantage du système puisque les pidfiles seront toujours supprimés au démarrage.
la source
Vérifiez mon script ...
Vous pouvez l' aimer ....
la source
Je propose la solution suivante, dans un script nommé 'flocktest'
la source