Quali comandi Unix possono essere usati come semaforo / blocco?


34

Voglio eseguire più script shell Bash in parallelo. Tuttavia, voglio evitare le condizioni di gara. Quali comandi Unix sono veramente atomici che potrei usare per questo scopo e come posso usarli?


Cosa stai facendo che richiede un lavoro parallelo? Non riesci a esprimere le dipendenze in un modo che permetta a un parallelo make(1)di prendere il controllo? (vale a dire, make -j 9se hai 8 core)? Ciò ha l'ulteriore vantaggio del lavoro interlacciato con granularità più fine.
— vonbrand

Risposte:


30

Se lockfilenon è installato sul tuo sistema, allora mkdirfarà il lavoro: si tratta di un'operazione atomica e fallisce se la directory esiste già (a patto di non aggiungere l'opzione della -priga di comando).

create_lock_or_wait () {
  path="$1"
  wait_time="${2:-10}"
  while true; do
        if mkdir "${path}.lock.d"; then
           break;
        fi
        sleep $wait_time
  done
}

remove_lock () {
  path="$1"
  rmdir "${path}.lock.d"
}

26

flock(1)

#!/bin/bash

# Makes sure we exit if flock fails.
set -e

(
  # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
  flock -x -w 10 200

  # Do stuff

) 200>/var/lock/.myscript.exclusivelock

Ciò garantisce che il codice tra "(" e ")" venga eseguito solo da un processo alla volta e che il processo attenda un blocco troppo a lungo.


Bello, non lo sapevo. Tuttavia, apparentemente è specifico per Linux ...
— Riccardo Murri,

1
@Riccardo, FreeBSD ha un comando simile: lockf(1).
— Alex B,

lockf(1)non funziona nel modo usato in questo esempio, però. Non può accettare un numero descrittore di file come argomento.
— Charley,

11

lockfile (1) sembra un buon candidato, anche se attenzione che fa parte del pacchetto procmail , che potresti non aver ancora installato sul tuo computer. È un pacchetto abbastanza popolare che dovrebbe essere impacchettato per il tuo sistema se non è ancora installato. Tre dei quattro sistemi che ho controllato ce l'hanno e l'altro ce l'ha a disposizione.

Usarlo è semplice:

#!/bin/sh
LOCKFILE=$HOME/.myscript/lock
mkdir -p `dirname $LOCKFILE`

echo Waiting for lock $LOCKFILE...
if lockfile -1 -r15 $LOCKFILE
then
    # Do protected stuff here
    echo Doing protected stuff...

    # Then, afterward, clean up so another instance of this script can run
    rm -f $LOCKFILE
else
    echo "Failed to acquire lock!  lockfile(1) returned $?"
    exit 1
fi

Le opzioni che ho fornito lo fanno riprovare una volta al secondo per un massimo di 15 secondi. Rilascia il flag "-r" se vuoi che aspetti per sempre.


2
Solo per riferimento - la pagina man: linux.die.net/man/1/lockfile . :)
— Lucas Jones,

Tenere presente che (in base alla manpage), "Una volta che un file è bloccato, il blocco deve essere toccato almeno una volta ogni cinque minuti o il blocco sarà considerato non aggiornato e i successivi tentativi di blocco avranno esito positivo."
— Jay,

6

La chiamata di sistema mkdir()è atomica sui filesystem POSIX. Quindi, usare il mkdircomando in modo tale da comportare esattamente una chiamata per mkdir()raggiungere il tuo scopo. (IOW, non usare mkdir -p). Lo sblocco corrispondente è rmdirovviamente.

Caveat emptor: mkdir()potrebbe non essere atomico sui filesystem di rete.


è rmdirquindi anche atomico?
— Alexej Magura,

3

Forse il comando lockfile farà quello che ti serve.

lockfile ~/.config/mylockfile.lock
.....
rm -f important.lock

Questo sembra eliminare il file sbagliato.
— Benjamin W.,

1

Se stai utilizzando Unix, usa fifos. È possibile scrivere record di lavoro sul Fifo e far leggere i processi da questo file e i lettori lo bloccheranno.

I file di blocco sono ok, ma per quello che descrivi andrei con FIFOS


1
Potresti approfondire come pensi che i quindici possano prevenire le condizioni di gara?
— G-Man dice "Ripristina Monica" il

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.