Jakie polecenia uniksowe mogą być użyte jako semafor / blokada?

34

Chcę uruchomić wiele skryptów powłoki Bash równolegle. Chcę jednak uniknąć warunków wyścigowych. Jakie polecenia uniksowe są naprawdę atomowe, których mógłbym użyć do tego celu i jak ich używać?

Larry Wang
źródło
Co robisz, że wymaga pracy równoległej? Czy nie potrafisz wyrazić zależności w sposób umożliwiający make(1)przejęcie równoległości ? (tj. zrobić, make -j 9jeśli masz 8 rdzeni)? Ma to dodatkową zaletę polegającą na przeplataniu z większą dokładnością.
vonbrand

Odpowiedzi:

30

Jeśli lockfilenie jest zainstalowany w systemie, mkdirwykona pracę: jest to operacja atomowa i kończy się niepowodzeniem, jeśli katalog już istnieje (o ile nie dodasz -pprzełącznika wiersza polecenia).

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"
}
Riccardo Murri
źródło
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

Zapewnia to, że kod między „(” a „)” jest uruchamiany tylko przez jeden proces na raz i że proces zbyt długo czeka na blokadę.

Alex B.
źródło
Fajny, nie wiedziałem o tym. Jednak najwyraźniej jest specyficzny dla Linuksa ...
Riccardo Murri,
1
@Riccardo, FreeBSD ma podobną komendę: lockf(1).
Alex B,
lockf(1)nie działa jednak tak, jak w tym przykładzie. Nie może przyjmować numeru deskryptora pliku jako argumentu.
Charley,
11

lockfile (1) wygląda na dobrego kandydata, choć uważaj, że jest on częścią pakietu procmail , którego być może jeszcze nie zainstalowałeś na swoim komputerze. Jest to wystarczająco popularny pakiet, który powinien zostać spakowany dla twojego systemu, jeśli nie jest jeszcze zainstalowany. Trzy z czterech sprawdzonych systemów mają go, a drugi ma go dostępny.

Korzystanie z niego jest proste:

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

Opcje, które podałem, sprawiają, że ponawia próbę co sekundę przez maksymalnie 15 sekund. Opuść flagę „-r”, jeśli chcesz czekać wiecznie.

Warren Young
źródło
2
Tylko dla odniesienia - strona podręcznika : linux.die.net/man/1/lockfile . :)
Lucas Jones,
Należy pamiętać, że (według strony podręcznika): „Gdy plik zostanie zablokowany, blokada musi być dotykana co najmniej raz na pięć minut, w przeciwnym razie blokada zostanie uznana za przestarzałą, a kolejne próby blokady zakończą się powodzeniem”.
Jay
6

Wywołanie systemowe mkdir()jest atomowe w systemach plików POSIX. Zatem użycie mkdirpolecenia w taki sposób, aby mkdir()wymagało dokładnie jednego połączenia, aby osiągnąć cel. (IOW, nie używaj mkdir -p). Odpowiednie odblokowanie jest rmdiroczywiście.

Zastrzeżenie emptor: mkdir()może nie być atomowy w sieciowych systemach plików.

Hari
źródło
jest rmdirzatem również atomowy?
Alexej Magura
3

Może polecenie lockfile zrobi to, czego potrzebujesz.

lockfile ~/.config/mylockfile.lock
.....
rm -f important.lock
jacksonh
źródło
To wydaje się usuwać niewłaściwy plik.
Benjamin W.
1

Jeśli używasz tylko Uniksa, użyj fifos. Możesz zapisać rekordy pracy do fifo i odczytać procesy z tego pliku, a twoi czytelnicy będą blokować na fifo.

Zablokuj pliki są w porządku, ale dla tego, co opisujesz, wybrałbym z fifos

Chris
źródło
1
Czy mógłbyś wyjaśnić, w jaki sposób myślisz, że piątki mogą zapobiec warunkom wyścigowym?
G-Man mówi „Reinstate Monica”