Às vezes, você precisa garantir que apenas uma instância de um shell script esteja sendo executada ao mesmo tempo.
Por exemplo, uma tarefa cron que é executada via crond que não fornece bloqueio por si só (por exemplo, a crond Solaris padrão).
Um padrão comum para implementar o bloqueio é um código como este:
#!/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
Obviamente, esse código tem uma condição de corrida. Há uma janela de tempo em que a execução de duas instâncias pode avançar após a linha 3 antes que alguém possa tocar no $LOCK
arquivo.
Para um trabalho cron, isso geralmente não é um problema, porque você tem um intervalo de minutos entre duas invocações.
Mas as coisas podem dar errado - por exemplo, quando o arquivo de bloqueio está em um servidor NFS - que trava. Nesse caso, vários trabalhos cron podem bloquear na linha 3 e fazer fila. Se o servidor NFS estiver ativo novamente, você terá um rebanho trovejante de trabalhos em execução paralelos.
Pesquisando na web, encontrei a ferramenta lockrun, que parece ser uma boa solução para esse problema. Com ele, você executa um script que precisa ser bloqueado assim:
$ lockrun --lockfile=/var/tmp/mylock myscript.sh
Você pode colocar isso em um invólucro ou usá-lo no seu crontab.
Ele usa lockf()
(POSIX) se disponível e volta a flock()
(BSD). E o lockf()
suporte ao NFS deve ser relativamente amplo.
Existem alternativas para lockrun
?
E os outros daemons cron? Existem fundos comuns que suportam o bloqueio de maneira sã? Uma rápida olhada na página de manual do Vixie Crond (padrão nos sistemas Debian / Ubuntu) não mostra nada sobre o bloqueio.
Seria uma boa ideia incluir uma ferramenta como lockrun
no coreutils ?
Na minha opinião ele implementa um tema muito semelhante a timeout
, nice
e amigos.
kill
editado; e parece uma boa prática armazenar o próprio pid no arquivo de bloqueio, em vez de apenas tocá-lo.Respostas:
Aqui está outra maneira de bloquear o script shell que pode impedir a condição de corrida descrita acima, onde dois trabalhos podem passar na linha 3. A
noclobber
opção funcionará no ksh e no bash. Não use,set noclobber
porque você não deve criar scripts no csh / tcsh. ;)YMMV com bloqueio no NFS (você sabe, quando os servidores NFS não estão acessíveis), mas em geral é muito mais robusto do que costumava ser. (Há 10 anos)
Se você tiver tarefas cron que fazem a mesma coisa ao mesmo tempo, de vários servidores, mas você só precisa de 1 instância para executar, algo como isso pode funcionar para você.
Não tenho experiência com lockrun, mas ter um ambiente de bloqueio pré-definido antes da execução do script realmente pode ajudar. Ou talvez não. Você está apenas configurando o teste para o arquivo de bloqueio fora do seu script em um invólucro e, teoricamente, não poderia simplesmente atingir a mesma condição de corrida se dois trabalhos fossem chamados pelo lockrun exatamente ao mesmo tempo, assim como o 'inside- solução do script?
O bloqueio de arquivos é praticamente o comportamento do sistema, e qualquer script que não verifique a existência do arquivo de bloqueio antes da execução fará o que for necessário. Apenas colocando o teste do arquivo de bloqueio e o comportamento adequado, você estará resolvendo 99% dos problemas em potencial, se não 100%.
Se você enfrentar muitas condições de corrida no arquivo de bloqueio, pode ser um indicador de um problema maior, como não ter seus trabalhos no tempo certo ou talvez se o intervalo não for tão importante quanto a conclusão do trabalho, talvez seu trabalho seja mais adequado para ser daemonizado .
EDITAR ABAIXO - 2016-05-06 (se você estiver usando o KSH88)
Com base no comentário do @Clint Pachl abaixo, se você usar o ksh88, use em
mkdir
vez denoclobber
. Isso atenua principalmente uma condição potencial de corrida, mas não a limita inteiramente (embora o risco seja minúsculo). Para mais informações, leia o link que Clint postou abaixo .E, como uma vantagem adicional, se você precisar criar tmpfiles em seu script, poderá usar o
lockdir
diretório para eles, sabendo que eles serão limpos quando o script sair.Para uma festa mais moderna, o método noclobber na parte superior deve ser adequado.
fonte
lockf()
chamada do sistema - quando é feito o backup, todos os processos são retomados, mas apenas um processo vence o bloqueio. Sem condição de corrida. Não encontro muitos problemas com o cronjobs - o oposto é o caso -, mas esse é um problema quando o atinge, pois tem o potencial de criar muita dor.set -o noclobber && echo "$$" > "$lockfile"
obter um fallback seguro quando o shell não suporta a opção noclobber.noclobber
opção pode estar sujeita a condições de corrida. Veja mywiki.wooledge.org/BashFAQ/045 para um pouco de reflexão.noclobber
(ou-C
) em ksh88 não funciona porque ksh88 não usaO_EXCL
paranoclobber
. Se você estiver executando com um novo shell você pode ser OK ...Eu prefiro usar links físicos.
Os links físicos são atômicos sobre o NFS e, na maioria das vezes, o mkdir também . Usando
mkdir(2)
oulink(2)
são praticamente os mesmos, em um nível prático; Prefiro usar links físicos, porque mais implementações do NFS permitiam links físicos atômicos do que atômicosmkdir
. Nas versões modernas do NFS, você também não precisa se preocupar em usá-lo.fonte
Eu entendo que
mkdir
é atômico, então talvez:fonte
mkdir()
sobre se o NFS (> = 3) é padronizado para ser atômico.mkdir
para ser atômico (ele faz pararename
). Na prática, sabe-se que algumas implementações não são. Relacionado: um tópico interessante, incluindo uma contribuição do autor do GNU arch .Uma maneira fácil é usar
lockfile
vindo normalmente com oprocmail
pacote.fonte
sem
que é parte dasparallel
ferramentas GNU , pode ser o que você está procurando:Como em:
saída:
Observe que o pedido não é garantido. Além disso, a saída não é exibida até terminar (irritante!). Mas, mesmo assim, é a maneira mais concisa que conheço para me proteger contra a execução simultânea, sem se preocupar com arquivos de bloqueio, novas tentativas e limpeza.
fonte
sem
identificador está sendo abatido no meio da execução?Eu uso
dtach
.fonte
Eu uso a ferramenta de linha de comando "flock" para gerenciar bloqueios nos meus scripts do bash, conforme descrito aqui e aqui . Eu usei esse método simples na página de manual do flock, para executar alguns comandos em um subshell ...
Nesse exemplo, ele falha com o código de saída 1 se não puder adquirir o arquivo de bloqueio. Mas o flock também pode ser usado de maneiras que não exigem que os comandos sejam executados em um sub-shell :-)
fonte
flock()
chamada do sistema não funciona no NFS .flock()
e, portanto, é problemático no NFS. Enquanto isso, o flock () no Linux agora volta parafcntl()
quando o arquivo está localizado em uma montagem NFS; portanto, em um ambiente NFS somente para Linux,flock()
agora funciona sobre NFS.Não use um arquivo.
Se o seu script for executado assim, por exemplo:
Você pode detectar se está sendo executado usando:
fonte
my_script
? No caso de outra instância estar em execução - nãorunning_proc
contém duas linhas correspondentes? Eu gosto da idéia, mas é claro - você vai ter resultados falsos quando outro usuário estiver executando um script com o mesmo nome ...$!
vez de$$
no seu exemplo.grep -v $$
). Basicamente, eu estava tentando fornecer uma abordagem diferente para o problema.Para uso real, você deve usar a resposta mais votada .
No entanto, quero discutir algumas abordagens quebradas e semi-viáveis usando
ps
e as muitas advertências que elas têm, já que continuo vendo as pessoas usá-las.Esta resposta é realmente a resposta para "Por que não usar
ps
egrep
manipular o bloqueio no shell?"Abordagem quebrada # 1
Primeiro, uma abordagem dada em outra resposta que tem alguns votos positivos, apesar de não funcionar (e nunca poderia) funcionar e claramente nunca ter sido testada:
Vamos corrigir os erros de sintaxe e os
ps
argumentos quebrados e obter:Esse script sempre sai do 6, toda vez, não importa como você o execute.
Se você executá-lo
./myscript
, aps
saída será apenas12345 -bash
, o que não corresponde à string necessária12345 bash ./myscript
, e isso falhará.Se você executá-lo
bash myscript
, as coisas ficam mais interessantes. O processo do bash bifurca-se para executar o pipeline e o shell filho executa ops
egrep
. O shell original e o shell filho aparecerão naps
saída, algo como isto:Essa não é a saída esperada
$$ bash $0
; portanto, seu script será encerrado.Abordagem quebrada # 2
Agora, com toda a justiça para o usuário que escreveu a abordagem quebrada nº 1, fiz algo parecido quando tentei isso pela primeira vez:
Isso quase funciona. Mas o fato de bifurcar-se para passar o tubo acaba com isso. Portanto, este sempre sairá também.
Abordagem não confiável # 3
Essa versão evita o problema de bifurcação de pipeline na abordagem nº 2, obtendo primeiro todos os PIDs que possuem o script atual em seus argumentos de linha de comando e, em seguida, filtrando essa lista de pidlists separadamente para omitir o PID do script atual.
Isso pode funcionar ... desde que nenhum outro processo tenha uma linha de comando correspondente ao
$0
, e fornecendo o script sempre chamado da mesma maneira (por exemplo, se for chamado com um caminho relativo e depois um caminho absoluto, a última instância não notará o anterior )Abordagem não confiável # 4
E se ignorarmos a verificação da linha de comando completa, pois isso pode não indicar um script realmente em execução e, em
lsof
vez disso, verificarmos todos os processos que têm esse script aberto?Bem, sim, essa abordagem não é realmente tão ruim:
Obviamente, se uma cópia do script estiver em execução, a nova instância será iniciada muito bem e você terá duas cópias em execução.
Ou se o script em execução for modificado (por exemplo, com Vim ou com a
git checkout
), a versão "nova" do script será iniciada sem problemas, pois o Vim egit checkout
resulta em um novo arquivo (um novo inode) no lugar do antigo.No entanto, se o script nunca for modificado e nunca copiado, essa versão será muito boa. Não há condição de corrida porque o arquivo de script já precisa ser aberto antes que a verificação possa ser alcançada.
Ainda pode haver falsos positivos se outro processo tiver o arquivo de script aberto, mas observe que, mesmo que esteja aberto para edição no Vim, o vim na verdade não mantém o arquivo de script aberto, portanto não resultará em falsos positivos.
Mas lembre-se, não use essa abordagem se o script puder ser editado ou copiado, pois você obterá falsos negativos, ou seja, várias instâncias sendo executadas ao mesmo tempo - portanto, o fato de a edição com o Vim não fornecer falso-positivos não deve importar para você. I mencionar isso, porém, porque abordagem # 3 não dar falsos positivos (ou seja, se recusa a iniciar) se você tiver o script aberto com Vim.
Então o que fazer então?
A resposta mais votada para esta pergunta fornece uma boa abordagem sólida.
Talvez você possa escrever uma melhor ... mas se você não entender todos os problemas e advertências com todas as abordagens acima, provavelmente não escreverá um método de bloqueio que evite todas elas.
fonte
Usando a ferramenta FLOM (Free LOck Manager) , os comandos de serialização se tornam tão fáceis quanto executar
O FLOM permite implementar casos de uso mais sofisticados (bloqueio distribuído, leitores / gravadores, recursos numéricos, etc.), conforme explicado aqui: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
fonte
Aqui está algo que às vezes adiciono em um servidor para lidar facilmente com as condições de corrida de qualquer trabalho na máquina. É semelhante à postagem de Tim Kennedy, mas dessa maneira você obtém manipulação de corrida adicionando apenas uma linha a cada script do bash que precisa.
Coloque o conteúdo abaixo em, por exemplo, / opt / racechecker / racechecker:
Aqui está como usá-lo. Observe a linha após o shebang:
A maneira como funciona é que ele descobre o nome principal do arquivo bashscript e cria um pidfile em "/ tmp". Ele também adiciona um ouvinte ao sinal de chegada. O ouvinte removerá o pidfile quando o script principal estiver finalizando corretamente.
Em vez disso, se existir um arquivo pid quando uma instância é iniciada, a instrução if contendo o código dentro da segunda instrução if será executada. Nesse caso, decidi lançar um e-mail de alarme quando isso acontecer.
E se o script falhar
Um exercício adicional seria lidar com falhas. Idealmente, o pidfile deve ser removido, mesmo se o script principal travar por qualquer motivo, isso não é feito na minha versão acima. Isso significa que, se o script travar, o pidfile precisará ser removido manualmente para restaurar a funcionalidade.
Em caso de falha do sistema
É uma boa idéia armazenar o arquivo pidfile / lockfile em, por exemplo / tmp. Dessa forma, seus scripts continuarão a ser executados após uma falha no sistema, pois os arquivos pid sempre serão excluídos na inicialização.
fonte
Verifique meu script ...
Você pode adorar ....
fonte
Eu ofereço a seguinte solução, em um script chamado 'flocktest'
fonte