Quais comandos do Unix podem ser usados ​​como um semáforo / bloqueio?

34

Eu quero executar vários scripts de shell Bash em paralelo. No entanto, quero evitar as condições da corrida. Quais comandos do Unix são realmente atômicos que eu poderia usar para esse fim, e como posso usá-los?

Larry Wang
fonte
O que você está fazendo que requer trabalho paralelo? Você não pode expressar as dependências de uma maneira que permita que um paralelo make(1)assuma o controle? (ou seja, faça um make -j 9se você tiver 8 núcleos)? Isso tem a vantagem adicional de intercalar o trabalho com granularidade mais fina.
vonbrand

Respostas:

30

Se lockfilenão estiver instalado no seu sistema, ele mkdirfará o trabalho: é uma operação atômica e falhará se o diretório já existir (contanto que você não adicione a -popção da linha de comandos).

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
fonte
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

Isso garante que o código entre "(" e ")" seja executado apenas por um processo por vez e que o processo aguarde um bloqueio por muito tempo.

Alex B
fonte
Legal, não sabia disso. No entanto, é específico do Linux, aparentemente ...
Riccardo Murri
11
@Riccardo, FreeBSD tem um comando semelhante: lockf(1).
Alex B
lockf(1)não funciona da maneira usada neste exemplo, no entanto. Não pode aceitar um número de descritor de arquivo como argumento.
Charley
11

O lockfile (1) parece um bom candidato, apesar de ser parte do pacote procmail , que talvez você ainda não tenha instalado em sua máquina. É um pacote bastante popular que deve ser empacotado para o seu sistema se ainda não estiver instalado. Três dos quatro sistemas que verifiquei o possuem e o outro está disponível.

Usá-lo é simples:

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

As opções que eu forneci fazem com que tente novamente uma vez por segundo por até 15 segundos. Solte a bandeira "-r" se desejar esperar para sempre.

Warren Young
fonte
2
Apenas para referência - a página de manual : linux.die.net/man/1/lockfile . :)
Lucas Jones
Esteja ciente de que (de acordo com a página de manual), "Depois que um arquivo é bloqueado, o bloqueio deve ser tocado pelo menos uma vez a cada cinco minutos ou o bloqueio será considerado obsoleto e as tentativas subseqüentes de bloqueio serão bem-sucedidas".
Jay
6

A chamada do sistema mkdir()é atômica nos sistemas de arquivos POSIX. Portanto, usando o mkdircomando de forma a envolver exatamente uma chamada paramkdir() atingir seu objetivo. (IOW, não use mkdir -p). O desbloqueio correspondente é rmdirclaro.

Advertência emptor: mkdir()pode não ser atômico nos sistemas de arquivos da rede.

Hari
fonte
é, rmdirportanto, também atômica?
Alexej Magura
3

Talvez o comando lockfile faça o que você precisa.

lockfile ~/.config/mylockfile.lock
.....
rm -f important.lock
jacksonh
fonte
Isso parece excluir o arquivo errado.
Benjamin W.
1

Se você estiver rodando apenas no Unix, use fifos. Você pode gravar registros de trabalho no fifo e fazer com que os processos sejam lidos neste arquivo, e seus leitores serão bloqueados no fifo.

Os arquivos de bloqueio estão ok, mas pelo que você descreve, eu usaria fifos

Chris
fonte
11
Você poderia explicar como o fifos pode impedir as condições da corrida?
G-Man diz 'Reinstate Monica'