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.