Eu me pergunto como aplicativos matadores como Thunderbird ou Firefox podem ser atualizados através do gerenciador de pacotes do sistema enquanto ainda estão em execução. O que acontece com o código antigo enquanto ele está sendo atualizado? O que devo fazer quando quero escrever um programa que se atualize enquanto está em execução?
Responder1
Substituindo arquivos em geral
Primeiro, existem várias estratégias para substituir um arquivo:
Abriro arquivo existente para gravação, trunque-o para comprimento 0 e grave o novo conteúdo. (Uma variante menos comum é abrir o arquivo existente, substituir o conteúdo antigo pelo novo conteúdo, truncar o arquivo para o novo comprimento se for mais curto.) Em termos de shell:
echo 'new content' >somefile
Removero arquivo antigo e crie um novo arquivo com o mesmo nome. Em termos de casca:
rm somefile echo 'new content' >somefile
Grave em um novo arquivo com um nome temporário e, em seguida,movero novo arquivo para o nome existente. A mudança exclui o arquivo antigo. Em termos de casca:
echo 'new content' >somefile.new mv somefile.new somefile
Não vou listar todas as diferenças entre as estratégias, apenas citarei algumas que são importantes aqui. Com a estratégia 1, se algum processo estiver usando o arquivo, o processo verá o novo conteúdo à medida que ele for atualizado. Isso pode causar alguma confusão se o processo espera que o conteúdo do arquivo permaneça o mesmo. Observe que se trata apenas de processos que possuem o arquivo aberto (como visível em lsof
ou em ; aplicativos interativos que possuem um documento aberto (por exemplo, abrir um arquivo em um editor) geralmente não mantêm o arquivo aberto, eles carregam o conteúdo do arquivo durante o operação “abrir documento” e substituem o arquivo (usando uma das estratégias acima) durante a operação “salvar documento”./proc/PID/fd/
Com as estratégias 2 e 3, se algum processo estiver com o arquivo somefile
aberto, o arquivo antigo permanece aberto durante a atualização do conteúdo. Com a estratégia 2, a etapa de remoção do arquivo, na verdade, remove apenas a entrada do arquivo no diretório. O arquivo em si só é removido quando não possui nenhuma entrada de diretório que leve a ele (em sistemas de arquivos Unix típicos, pode havermais de uma entrada de diretório para o mesmo arquivo)enenhum processo está aberto. Aqui está uma maneira de observar isso - o arquivo só é removido quando o sleep
processo é encerrado ( rm
remove apenas sua entrada de diretório).
echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .
Com a estratégia 3, a etapa de mover o novo arquivo para o nome existente remove a entrada de diretório que leva ao conteúdo antigo e cria uma entrada de diretório que leva ao novo conteúdo. Isso é feito em uma operação atômica, então essa estratégia tem uma grande vantagem: se um processo abrir o arquivo a qualquer momento, ele verá o conteúdo antigo ou o novo — não há risco de obter conteúdo misto ou de o arquivo não funcionar. existir.
Substituindo executáveis
Se você tentar a estratégia 1 com um executável em execução no Linux, receberá um erro.
cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy
Um “arquivo de texto” significa um arquivo contendo código executávelpor razões históricas obscuras. O Linux, como muitas outras variantes do Unix, recusa-se a sobrescrever o código de um programa em execução; algumas variantes Unix permitem isso, levando a travamentos, a menos que o novo código seja uma modificação muito bem elaborada do código antigo.
No Linux, você pode substituir o código de uma biblioteca carregada dinamicamente. É provável que cause uma falha no programa que o está usando. (Talvez você não consiga observar isso sleep
porque ele carrega todo o código da biblioteca necessário quando é iniciado. Tente um programa mais complexo que faça algo útil depois de dormir, como perl -e 'sleep 9; print lc $ARGV[0]'
.)
Se um intérprete estiver executando um script, o arquivo de script será aberto normalmente pelo intérprete, portanto não há proteção contra a substituição do script. Alguns intérpretes leem e analisam todo o script antes de começarem a executar a primeira linha, outros leem o script conforme necessário. VerO que acontece se você editar um script durante a execução?eComo o Linux lida com scripts de shell?para mais detalhes.
As estratégias 2 e 3 também são seguras para executáveis: embora executáveis em execução (e bibliotecas carregadas dinamicamente) não sejam arquivos abertos no sentido de terem um descritor de arquivo, eles se comportam de maneira muito semelhante. Enquanto algum programa estiver executando o código, o arquivo permanecerá no disco mesmo sem uma entrada de diretório.
Atualizando um aplicativo
A maioria dos gerenciadores de pacotes usa a estratégia 3 para substituir arquivos, devido à grande vantagem mencionada acima – a qualquer momento, abrir o arquivo leva a uma versão válida dele.
O ponto em que as atualizações de aplicativos podem falhar é que, embora a atualização de um arquivo seja atômica, a atualização do aplicativo como um todo não o será se o aplicativo consistir em vários arquivos (programas, bibliotecas, dados,…). Considere a seguinte sequência de eventos:
- Uma instância do aplicativo é iniciada.
- O aplicativo foi atualizado.
- O aplicativo da instância em execução abre um de seus arquivos de dados.
Na etapa 3, a instância em execução da versão antiga do aplicativo abre um arquivo de dados da nova versão. Se isso funciona ou não depende do aplicativo, de qual arquivo se trata e de quanto o arquivo foi modificado.
Após uma atualização, você notará que o programa antigo ainda está em execução. Se quiser executar a nova versão, você terá que sair do programa antigo e executar a nova versão. Os gerenciadores de pacotes geralmente eliminam e reiniciam os daemons em uma atualização, mas deixam os aplicativos do usuário final em paz.
Alguns daemons têm procedimentos especiais para lidar com atualizações sem ter que encerrar o daemon e aguardar a reinicialização da nova instância (o que causa uma interrupção do serviço). Isto é necessário no caso deiniciar, que não pode ser morto; sistemas init fornecem uma maneira de solicitar que a instância em execução chameexecve
para substituir-se pela nova versão.
Responder2
A atualização pode ser executada enquanto o programa é executado, mas o programa em execução que você vê é na verdade a versão antiga dele. O binário antigo permanece no disco até você fechar o programa.
Explicação:em sistemas Linux, um arquivo é apenas um inode, que pode ter vários links para ele. Por exemplo. o que /bin/bash
você vê é apenas um link inode 3932163
no meu sistema. Você pode descobrir para qual inode algo está vinculado emitindo ls --inode /path
no link. Um arquivo (inode) só é removido se não houver nenhum link apontando para ele e não estiver em uso por nenhum programa. Quando o gerenciador de pacotes é atualizado, por exemplo. /usr/bin/firefox
, ele primeiro desvincula (remove o link físico /usr/bin/firefox
) e, em seguida, cria um novo arquivo chamado /usr/bin/firefox
que é um link físico para um inode diferente (aquele que contém a nova firefox
versão). O inode antigo agora está marcado como livre e pode ser reutilizado para armazenar novos dados, mas permanece no disco (os inodes só são criados quando você constrói seu sistema de arquivos e nunca são excluídos). Na próxima inicialização de firefox
, o novo será usado.
Se você deseja escrever um programa que se "atualize" durante a execução, a única solução possível que consigo pensar é verificar periodicamente o carimbo de data e hora de seu próprio arquivo binário e, se for mais recente que o horário de início do programa, recarregue-se.
Responder3
Eu me pergunto como aplicativos matadores como Thunderbird ou Firefox podem ser atualizados através do gerenciador de pacotes do sistema enquanto ainda estão em execução? Bem, posso dizer que isso realmente não funciona bem... O Firefox quebrou horrivelmente se eu o deixei aberto enquanto uma atualização de pacote estava em execução. Às vezes tive que matá-lo à força e reiniciá-lo, porque estava tão quebrado que não conseguia nem fechá-lo direito.
O que acontece com o código antigo enquanto ele está sendo atualizado? Normalmente no Linux um programa é carregado na memória, portanto o executável no disco não é necessário ou usado enquanto o programa está em execução. Na verdade, você pode até excluir o executável e o programa não deve se importar... No entanto, alguns programas podem precisar do executável, e certos sistemas operacionais (como o Windows) bloquearão o arquivo executável, impedindo a exclusão ou até mesmo renomear/mover, enquanto o programa está em execução. O Firefox quebra porque é bastante complexo e usa vários arquivos de dados que informam como construir sua GUI (interface do usuário). Durante uma atualização de pacote, esses arquivos são sobrescritos (atualizados); portanto, quando um executável antigo do Firefox (na memória) tenta usar os novos arquivos GUI, coisas estranhas podem acontecer...
O que devo fazer quando quero escrever um programa que se atualize enquanto está em execução? Já existem muitas respostas para sua pergunta. Veja isso: https://stackoverflow.com/questions/232347/how-should-i-implement-an-auto-updater A propósito, perguntas sobre programação ficam melhor no StackOverflow.