GNU/Linux >> Linux Esercitazione >  >> Linux

Blocco corretto negli script della shell?

A volte devi assicurarti che sia in esecuzione solo un'istanza di uno script di shell alla volta.

Ad esempio un processo cron eseguito tramite crond che non fornisce il blocco
da solo (ad esempio il crond predefinito di Solaris).

Un modello comune per implementare il blocco è il codice come questo:

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

Naturalmente, tale codice ha una race condition. C'è una finestra temporale in cui l'
esecuzione di due istanze può avanzare entrambe dopo la riga 3 prima che una sia in grado di
toccare il $LOCK file.

Per un processo cron questo di solito non è un problema perché hai un intervallo di
minuti tra due chiamate.

Ma le cose possono andare storte, ad esempio quando il file di blocco si trova su un server NFS,
che si blocca. In tal caso, diversi lavori cron possono bloccarsi sulla linea 3 e fare la coda. Se
il server NFS è di nuovo attivo, allora hai una marea di lavori paralleli
in esecuzione.

Cercando sul web ho trovato lo strumento lockrun che sembra una buona
soluzione a quel problema. Con esso esegui uno script che deve essere bloccato come
questo:

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

Puoi metterlo in un involucro o usarlo dal tuo crontab.

Usa lockf() (POSIX) se disponibile e torna a flock() (BSD). E lockf() il supporto su NFS dovrebbe essere relativamente diffuso.

Ci sono alternative a lockrun ?

E gli altri demoni cron? Esistono crond comuni che supportano il blocco in modo sano? Una rapida occhiata alla pagina man di Vixie Crond (predefinita su
sistemi Debian/Ubuntu) non mostra nulla sul blocco.

Sarebbe una buona idea includere uno strumento come lockrun in coreutils?

A mio parere implementa un tema molto simile a timeout , nice e amici.

Risposta accettata:

Ecco un altro modo per eseguire il blocco nello script della shell che può prevenire la race condition che hai descritto sopra, in cui due lavori possono passare entrambi la riga 3. Il noclobber l'opzione funzionerà in ksh e bash. Non utilizzare set noclobber perché non dovresti eseguire script in csh/tcsh. 😉

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 con blocco su NFS (sai, quando i server NFS non sono raggiungibili), ma in generale è molto più robusto di prima. (10 anni fa)

Se hai cron job che fanno la stessa cosa contemporaneamente, da più server, ma hai solo bisogno di 1 istanza per essere effettivamente eseguita, qualcosa del genere potrebbe funzionare per te.

Correlati:semplice comando di commutazione della shell Bluetooth per MacOS?

Non ho esperienza con lockrun, ma avere un ambiente di blocco preimpostato prima che lo script sia effettivamente in esecuzione potrebbe essere d'aiuto. O potrebbe no. Stai solo impostando il test per il file di blocco al di fuori del tuo script in un wrapper e, in teoria, non potresti semplicemente raggiungere la stessa condizione di gara se due lavori fossero chiamati da lockrun esattamente nello stesso momento, proprio come con 'inside- soluzione dello script?

Il blocco dei file rispetta comunque il comportamento del sistema e tutti gli script che non verificano l'esistenza del file di blocco prima dell'esecuzione faranno tutto ciò che faranno. Semplicemente inserendo il test del file di blocco e un comportamento corretto, risolverai il 99% dei potenziali problemi, se non il 100%.

Se ti imbatti spesso in condizioni di gara di lockfile, potrebbe essere un indicatore di un problema più grande, come non avere il tempo giusto per i tuoi lavori, o forse se l'intervallo non è importante quanto il completamento del lavoro, forse il tuo lavoro è più adatto per essere demonizzato .

MODIFICA SOTTO – 06-05-2016 (se stai usando KSH88)

Basandosi sul commento di @Clint Pachl di seguito, se usi ksh88, usa mkdir invece di noclobber . Ciò mitiga principalmente una potenziale condizione di razza, ma non la limita del tutto (sebbene il rischio sia minuscolo). Per ulteriori informazioni leggi il link che Clint ha pubblicato di seguito.

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

E, come ulteriore vantaggio, se devi creare file tmp nel tuo script, puoi usare lockdir directory per loro, sapendo che verranno ripuliti alla chiusura dello script.

Per bash più moderno, dovrebbe essere adatto il metodo noclobber in alto.


Linux
  1. Consenti Setuid sugli script della shell?

  2. Array associativi negli script della shell?

  3. Come eliminare i privilegi di root negli script della shell?

  4. Eseguire script di shell tramite un sito Web?

  5. Condivisione di variabili su più script di shell?

Come creare script di shell

Array negli script di shell

Come utilizzare if-else negli script della shell?

Comprendere il ciclo for negli script della shell

Il ciclo while negli script della shell

Esegui tutti gli script della shell nella cartella