GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Richtiges Sperren in Shell-Skripten?

Manchmal müssen Sie sicherstellen, dass nur eine Instanz eines Shell-Skripts gleichzeitig ausgeführt wird.

Zum Beispiel ein Cron-Job, der über crond ausgeführt wird, der
selbst kein Locking bereitstellt (z. B. der Standard-Crond von Solaris).

Ein gängiges Muster zum Implementieren von Sperren ist Code wie dieser:

#!/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

Natürlich hat ein solcher Code eine Race-Bedingung. Es gibt ein Zeitfenster, in dem die
Ausführung von zwei Instanzen beide nach Zeile 3 fortschreiten kann, bevor man $LOCK berühren kann Datei.

Für einen Cron-Job ist das normalerweise kein Problem, da zwischen zwei Aufrufen
Minuten liegen.

Es kann aber auch mal was schiefgehen – zum Beispiel wenn die Lockfile auf einem NFS-Server liegt –
der sich aufhängt. In diesem Fall können mehrere Cron-Jobs auf Zeile 3 blockieren und anstehen. Wenn
der NFS-Server wieder aktiv ist, dann haben Sie eine donnernde Herde parallel
laufender Jobs.

Bei der Suche im Internet fand ich das Tool lockrun, das eine gute
Lösung für dieses Problem zu sein scheint. Damit führen Sie ein Skript aus, das wie
dieses gesperrt werden muss:

$ lockrun --lockfile=/var/tmp/mylock myscript.sh

Sie können dies in einen Wrapper packen oder es von Ihrem Crontab verwenden.

Es verwendet lockf() (POSIX) falls verfügbar und greift auf flock() zurück (BSD). Und lockf() Unterstützung über NFS sollte relativ weit verbreitet sein.

Gibt es Alternativen zu lockrun ?

Was ist mit anderen Cron-Daemons? Gibt es übliche Cronds, die das Sperren auf
vernünftige Weise unterstützen? Ein kurzer Blick in die Manpage von Vixie Crond (Standard auf
Debian/Ubuntu-Systemen) zeigt nichts über das Sperren.

Wäre es eine gute Idee, ein Tool wie lockrun einzubinden in Coreutils?

Meiner Meinung nach implementiert es ein Thema, das timeout sehr ähnlich ist , nice und Freunde.

Akzeptierte Antwort:

Hier ist eine andere Möglichkeit, ein Shell-Skript zu sperren, das die oben beschriebene Race-Bedingung verhindern kann, bei der zwei Jobs beide Zeile 3 passieren können. Der noclobber Option funktioniert in ksh und bash. Verwenden Sie nicht set noclobber weil Sie nicht in csh/tcsh scripten sollten. 😉

lockfile=/var/tmp/mylock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then

        trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

        # do stuff here

        # clean up after yourself, and release your trap
        rm -f "$lockfile"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi

YMMV mit Sperrung auf NFS (Sie wissen schon, wenn NFS-Server nicht erreichbar sind), aber im Allgemeinen ist es viel robuster als früher. (vor 10 Jahren)

Wenn Sie Cron-Jobs haben, die von mehreren Servern gleichzeitig dasselbe tun, aber nur eine Instanz benötigen, um tatsächlich ausgeführt zu werden, könnte so etwas für Sie funktionieren.

Verwandte:einfacher MacOS Bluetooth Toggle Shell-Befehl?

Ich habe keine Erfahrung mit Lockrun, aber eine voreingestellte Sperrumgebung zu haben, bevor das Skript tatsächlich ausgeführt wird, könnte hilfreich sein. Oder vielleicht auch nicht. Sie setzen nur den Test für die Sperrdatei außerhalb Ihres Skripts in einem Wrapper, und könnten Sie theoretisch nicht einfach dieselbe Race-Bedingung treffen, wenn zwei Jobs genau zur gleichen Zeit von lockrun aufgerufen würden, genau wie bei der 'Inside- the-script' Lösung?

Das Sperren von Dateien ist ohnehin so ziemlich das Verhalten des Systems, und alle Skripte, die vor der Ausführung nicht auf die Existenz der Sperrdatei prüfen, werden tun, was sie tun werden. Nur durch den Lockfile-Test und das richtige Verhalten lösen Sie 99 % der potenziellen Probleme, wenn nicht sogar 100 %.

Wenn Sie häufig auf Lockfile-Rennbedingungen stoßen, kann dies ein Indikator für ein größeres Problem sein, z. B. wenn Ihre Jobs nicht richtig getaktet sind, oder wenn das Intervall nicht so wichtig ist wie der Abschluss des Jobs, ist Ihr Job möglicherweise besser geeignet, um dämonisiert zu werden .

UNTEN BEARBEITEN – 2016-05-06 (wenn Sie KSH88 verwenden)

Basierend auf dem Kommentar von @Clint Pachl unten, wenn Sie ksh88 verwenden, verwenden Sie mkdir statt noclobber . Dies mildert meistens eine potenzielle Rennbedingung, schränkt sie jedoch nicht vollständig ein (obwohl das Risiko minimal ist). Für weitere Informationen lesen Sie den Link, den Clint unten gepostet hat.

lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid

if ( mkdir ${lockdir} ) 2> /dev/null; then
        echo $$ > $pidfile
        trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
        # do stuff here

        # clean up after yourself, and release your trap
        rm -rf "$lockdir"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi

Und als zusätzlichen Vorteil können Sie lockdir verwenden, wenn Sie tmpfiles in Ihrem Skript erstellen müssen Verzeichnis für sie, da Sie wissen, dass sie bereinigt werden, wenn das Skript beendet wird.

Für moderneres Bash sollte die Noclobber-Methode oben geeignet sein.


Linux
  1. Setuid für Shell-Skripte zulassen?

  2. Shell-Skripte über eine Website ausführen?

  3. Gemeinsame Nutzung von Variablen über mehrere Shell-Skripte hinweg?

  4. Wie testet man die Posix-Konformität von Shell-Skripten?

  5. Lange Befehle in Shell-Skripten aufteilen?

So erstellen Sie Shell-Skripte

Arrays in Shell-Skripten

Wie verwende ich if-else in Shell-Skripten?

Die for-Schleife in Shell-Skripten verstehen

Die While-Schleife in Shell-Skripten

Führen Sie alle Shell-Skripte im Ordner aus