
Pergunta
Suponha que eu tenha algum não-diretório (arquivo, nomeado pipe/socket, qualquer que seja) no pathname /tmp/foo
e algum outro não-diretório no pathname /tmp/bar
. Então dois (ou mais) processos começam a ser executados simultaneamente:
O processo um faz:
unlink('/tmp/foo') /* or rename('/tmp/foo', '/tmp/removed') */
unlink('/tmp/bar') /* or rename('/tmp/bar', '/tmp/removed') */
O processo dois (e assim por diante) faz:
link('/tmp/foo', '/tmp/bar')
Pelo que entendi, não há como o processo dois ter sucesso (seja link(2)
tentado enquanto /tmp/foo
ainda está presente, caso em que /tmp/bar
também está presente, portanto deve falhar com EEXIST
, ou /tmp/foo
desapareceu, então deve falhar com ENOENT
).
Mas essa intuição se baseia na suposição de que as chamadas de sistema unlink(2)
e/ou rename(2)
são inerentemente sequenciais em seus efeitos de desvinculação, então estou procurando uma verificação do meu entendimento: Existe algum sistema semelhante a *nix cujo kernel permite os dois unlink(2)
e/ou rename(2)
as chamadas são bem-sucedidas, mas simultaneamente fazem com link(2)
que também sejam bem-sucedidas (seja devido à reordenação da desvinculação /tmp/foo
e /tmp/bar
à não abstração/ocultação do processo de chamada link(2)
ou por meio de alguma outra condição/bug peculiar de corrida)?
Entendimento Atual
Eu li as páginas de manual do unlink(2)
, rename(2)
e link(2)
do Linux e alguns BSDs, e a especificação POSIX para essas funções. Mas não creio que eles realmente contenham algo tranquilizador sobre esse assunto, após consideração cuidadosa. Pelo menos comrename(2)
, nos foi prometido que odestinoé substituído atomicamente se já estiver presente (bugs no próprio sistema operacional de lado), mas nada mais.
Eu tenho vistoreivindicaçõesque múltiplas execuções simultâneas de rename(foo, qux)
will de forma atômica e portável tenham todas as renomeações, exceto uma, falhando ENOENT
- então isso é promissor! Só não tenho certeza se isso também pode ser estendido a uma link(foo, bar)
falha nas mesmas circunstâncias.ENOENT
Respostas preferidas
Eu percebo que esta é uma daquelas situações "não é possível provar uma negativa" - podemos, na melhor das hipóteses, apenas observar que não há evidências de que exista um sistema semelhante ao * nix que permitirá que o processo dois link(2)
seja bem-sucedido.
Então, o que estou procurando são respostas que cubram o maior número possível de sistemas do tipo * nix (pelo menos Linux, OS X e os vários BSDs, mas idealmente também os sistemas proprietários ainda em uso, como o Solaris 10) - de pessoas que têm familiaridade suficiente com esses sistemas e com esse conjunto restrito de problemas (operações atômicas/bem ordenadas do sistema de arquivos) que estão confiantes (tanto quanto alguém pode estar realisticamente) de que conheceriam problemas como o Mac mencionado acima OS X rename(2)
- bug não atômico se eles existissem nas plataformas com as quais estão familiarizados. Isso me daria confiança suficiente de que funciona da maneira que penso, de maneira portátil o suficiente para confiar.
Nota Final
Esta não é uma questão de "problema X/Y" - não há nenhum problema subjacente que possa ser respondido referindo-me aos vários mecanismos de bloqueio/IPC ou qualquer outra coisa que contorne a incerteza sobre como essas chamadas de sistema específicas interagem: eu quero especificamente saber se é possível confiar nas chamadas de sistema acima interagindo de maneira portável conforme esperado em sistemas semelhantes a *nix em uso prático hoje.
Responder1
Observe padrões comoPOSIXpara garantias de portabilidade. Na prática, a maioria dos sistemas compatíveis com POSIX apresentam pequenos desvios das especificações, mas de modo geral você pode confiar nas garantias fornecidas nas especificações. A maioria dos dispositivos modernos atende às especificações, mesmo que não tenham sido testados formalmente. Eles podem precisar ser executados em modo POSIX, por exemplo, configurando POSIXLY_CORRECT=1
com bash ou certificando-se de que /usr/xpg4/bin
estão à frente /bin
e /usr/bin
dentro PATH
do Solaris.
Único Unix v2(uma extensão mais antiga do POSIX) tem isto a dizer sobrelink
:
A
link()
função criará atomicamente um novo link para o arquivo existente e a contagem de links do arquivo será incrementada em um.
Sobrerename
:
Se o link nomeado pelonovoargumento existe, ele é removido evelhorenomeado paranovo. Neste caso, um link denominado new permanecerá visível para outros processos durante a operação de renomeação e se referirá ao arquivo referido pornovoouvelhoantes do início da operação.
O POSIX afirma explicitamente que se o destino existir, sua substituição deverá ser atômica. No entanto, não afirma que a própria renomeação deva ser atômica, ou seja, que não há nenhum momento em que ambosvelhoenovoconsulte o arquivo em questão ou quando nenhum dos dois o fizer. Na prática, essas propriedades são verdadeiras em sistemas Unix, pelo menos em sistemas de arquivos locais.
Além disso a ordem das operações é garantida: em C, ;
garante a execução sequencial; em sh, ;
/newline garante execução sequencial (como do &&
e assim por diante); outras linguagens de programação oferecem garantias semelhantes. Então em
unlink("/tmp/foo");
unlink("/tmp/bar");
é garantido que não há nenhum momento em que /tmp/foo
exista mas não /tmp/bar
(assumindo que /tmp/bar
exista inicialmente). Portanto, a execução de um processo simultâneo link("/tmp/foo", "/tmp/bar")
não pode ser bem-sucedida.
Observe que a atomicidade não garanteresiliência. Atomicidade trata do comportamento observável em um sistema ativo. A resiliência, no contexto dos sistemas de arquivos, trata do que acontece no caso de uma falha do sistema. Muitos sistemas de arquivos sacrificam a resiliência pelo desempenho, portanto, se a execução unlink("foo"); unlink("bar");
for interrompida (com o diretório atual no armazenamento em disco), é possível que ele bar
seja excluído e foo
permaneça para trás.
Alguns sistemas de arquivos de rede oferecem menos garantias quando as operações acontecem em clientes diferentes. Implementações mais antigas de NFS eram notórias por isso. Acho que as implementações modernas são melhores, mas não tenho experiência com NFS moderno.