129 votes

Empêcher l'exécution de tâches cron en double

J'ai programmé une tâche cron pour qu'elle s'exécute toutes les minutes, mais parfois le script prend plus d'une minute pour se terminer et je ne veux pas que les tâches commencent à "s'empiler" les unes sur les autres. Je suppose que c'est un problème de concurrence - c'est-à-dire que l'exécution de script doit être mutuellement exclusive.

Pour résoudre le problème, j'ai fait en sorte que le script recherche l'existence d'un fichier particulier (" fichier de verrouillage.txt ") et quitter s'il existe ou touch si ce n'est pas le cas. Mais ce n'est pas un bon sémaphore ! Existe-t-il une meilleure pratique que je devrais connaître ? Aurais-je dû écrire un démon à la place ?

180voto

Ryan Sampson Points 2898

Il existe quelques programmes qui automatisent cette fonction, éliminent l'ennui et les bogues potentiels liés au fait de le faire soi-même, et évitent le problème de la serrure périmée en utilisant également le flock en arrière-plan (ce qui est un risque si vous n'utilisez que le tactile). J'ai utilisé lockrun y lckdo dans le passé, mais maintenant il y a flock (1) (dans les nouvelles versions de util-linux) ce qui est génial. C'est vraiment facile à utiliser :

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job

3 votes

Lckdo va être retiré de moreutils, maintenant que flock(1) est dans util-linux. Et ce paquetage est fondamentalement obligatoire dans les systèmes Linux, donc vous devriez pouvoir compter sur sa présence. Pour l'utilisation, regardez ci-dessous.

0 votes

Oui, le flocage est maintenant mon option préférée. Je vais même mettre à jour ma réponse en conséquence.

0 votes

Quelqu'un connaît-il la différence entre flock -n file command y flock -n file -c command ?

31voto

David Pokluda Points 4284

La meilleure façon dans Shell est d'utiliser troupeau(1)

(
  flock -x -w 5 99
  ## Do your stuff here
) 99>/path/to/my.lock

2 votes

Je ne peux pas ne pas upvoter une utilisation astucieuse de la redirection des fd. C'est juste trop arcaniquement génial.

1 votes

Il n'est pas analysé par moi en Bash ou ZSH, il faut éliminer l'espace entre 99 y > c'est donc 99> /...

0 votes

@Kyle Ouaip, correct ! Corrigé ! @womble Haha, d'accord :)

26voto

Amir Points 757

En fait, flock -n peut être utilisé à la place de lckdo *, vous utiliserez donc le code des développeurs du noyau.

Construire sur l'exemple de womble vous écrirez quelque chose comme :

* * * * * flock -n /some/lockfile command_to_run_every_minute

BTW, en regardant le code, tous les flock , lockrun et lckdo font exactement la même chose, il s'agit juste de savoir laquelle est la plus facilement accessible pour vous.

7voto

Amir Points 757

Maintenant que systemd est sorti, il existe un autre mécanisme d'ordonnancement sur les systèmes Linux :

A systemd.timer

/etc/systemd/system/myjob.service o ~/.config/systemd/user/myjob.service :

[Service]
ExecStart=/usr/local/bin/myjob

/etc/systemd/system/myjob.timer o ~/.config/systemd/user/myjob.timer :

[Timer]
OnCalendar=minutely

[Install]
WantedBy=timers.target

Si l'unité de service est déjà en cours d'activation au moment de la prochaine activation de la minuterie, une autre instance du service sera alors pas être lancé.

Une alternative, qui lance le travail une fois au démarrage et une minute après la fin de chaque exécution :

[Timer]
OnBootSec=1m
OnUnitInactiveSec=1m 

[Install]
WantedBy=timers.target

4voto

TonyB Points 2482

Vous n'avez pas précisé si vous voulez que le script attende que l'exécution précédente soit terminée ou non. Par "Je ne veux pas que les travaux commencent à "s'empiler" les uns sur les autres", je suppose que vous sous-entendez que vous voulez que le script se termine s'il est déjà en cours d'exécution,

Donc, si vous ne voulez pas dépendre de lckdo ou autre, vous pouvez faire ceci :

PIDFILE=/tmp/`basename $0`.pid

if [ -f $PIDFILE ]; then
  if ps -p `cat $PIDFILE` > /dev/null 2>&1; then
      echo "$0 already running!"
      exit
  fi
fi
echo $$ > $PIDFILE

trap 'rm -f "$PIDFILE" >/dev/null 2>&1' EXIT HUP KILL INT QUIT TERM

# do the work

0 votes

Merci, votre exemple est utile - je veux que le script se termine s'il est déjà en cours d'exécution. Merci de l'avoir mentionné ickdo - cela semble faire l'affaire.

0 votes

Pour information : j'aime cette solution parce qu'elle peut être incluse dans un script, de sorte que le verrouillage fonctionne indépendamment de la façon dont le script est invoqué.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X