Um bloqueio de arquivo é anexado a um arquivo, por meio de uma descrição do arquivo . Em um nível alto, a sequência de operações em uma instância do script é:
- Abra o arquivo ao qual o bloqueio está anexado ("o arquivo de bloqueio").
- Faça um bloqueio no arquivo de bloqueio.
- Fazer coisas.
- Feche o arquivo de bloqueio. Isso libera o bloqueio anexado à descrição do arquivo criada pela abertura de um arquivo.
Manter o bloqueio impede que outra cópia do mesmo script seja executada, porque é isso que os bloqueios fazem. Desde que exista um bloqueio exclusivo em um arquivo em algum lugar do sistema, é impossível criar uma segunda instância do mesmo bloqueio, mesmo através de uma descrição de arquivo diferente.
Abrir um arquivo cria uma descrição do arquivo . Este é um objeto do kernel que não tem muita visibilidade direta nas interfaces de programação. Você acessa uma descrição do arquivo indiretamente por meio de descritores de arquivos, mas normalmente pensa nisso como acessando o arquivo (lendo ou gravando seu conteúdo ou metadados). Um bloqueio é um dos atributos que são uma propriedade para a descrição do arquivo, em vez de um arquivo ou um descritor.
No início, quando um arquivo é aberto, a descrição do arquivo possui um único descritor de arquivo, mas mais descritores podem ser criados criando outro descritor (a dup
família de chamadas do sistema) ou bifurcando um subprocesso (após o qual o pai e o criança tem acesso à mesma descrição do arquivo). Um descritor de arquivo pode ser fechado explicitamente ou quando o processo em que ele está morre. Quando o último descritor de arquivo anexado a um arquivo é fechado, a descrição do arquivo é fechada.
Veja como a sequência de operações acima afeta a descrição do arquivo.
- O redirecionamento
<$0
abre o arquivo de script no subshell, criando uma descrição do arquivo. Neste ponto, há um descritor de arquivo único anexado à descrição: descritor número 0 no subshell.
- O subshell chama
flock
e aguarda a saída. Enquanto o flock está em execução, há dois descritores anexados à descrição: número 0 no subshell e número 0 no processo do flock. Quando o flock assume o bloqueio, isso define uma propriedade da descrição do arquivo. Se outra descrição do arquivo já tiver um bloqueio no arquivo, o rebanho não poderá aceitá-lo, pois é um bloqueio exclusivo.
- O subshell faz coisas. Como ainda possui um descritor de arquivo aberto na descrição com o bloqueio, essa descrição permanece existente e mantém seu bloqueio, pois ninguém jamais remove o bloqueio.
- O subshell morre no parêntese de fechamento. Isso fecha o último descritor de arquivo na descrição do arquivo que possui o bloqueio, portanto o bloqueio desaparece nesse momento.
A razão pela qual o script usa um redirecionamento $0
é que o redirecionamento é a única maneira de abrir um arquivo no shell, e manter um redirecionamento ativo é a única maneira de manter um descritor de arquivo aberto. O subshell nunca lê de sua entrada padrão, apenas precisa mantê-lo aberto. Em um idioma que fornece acesso direto à chamada aberta e fechada, você pode usar
fd = open($0)
flock(fd, LOCK_EX)
do stuff
close(fd)
Você pode realmente obter a mesma sequência de operações no shell se fizer o redirecionamento com o exec
builtin.
exec <$0
flock -n -x 0
# do stuff
exec <&-
O script poderia usar um descritor de arquivo diferente se quisesse continuar acessando a entrada padrão original.
exec 3<$0
flock -n -x 0
# do stuff
exec 3<&-
ou com um subshell:
(
flock -n -x 3
# do stuff
) 3<$0
O bloqueio não precisa estar no arquivo de script. Pode estar em qualquer arquivo que possa ser aberto para leitura (portanto, ele deve existir, deve ser um tipo de arquivo que possa ser lido, como um arquivo regular ou um pipe nomeado, mas não um diretório, e o processo de script deve ter a permissão para lê-lo). O arquivo de script tem a vantagem de garantir sua presença e legibilidade (exceto no caso de borda em que foi excluído externamente entre o momento em que o script foi chamado e o momento em que o script chega ao <$0
redirecionamento).
Desde que seja flock
bem-sucedido, e o script esteja em um sistema de arquivos em que os bloqueios não sejam com erros (alguns sistemas de arquivos de rede como o NFS podem ser com erros), não vejo como o uso de um arquivo de bloqueio diferente pode permitir uma condição de corrida. Suspeito de um erro de manipulação da sua parte.