Deixe-me explicar.
Quando você executa um executável, uma sequência de chamadas do sistema é executada, principalmente fork()
e execve()
:
fork()
cria um processo filho do processo de chamada, que é (principalmente) uma cópia exata do pai, ambos ainda executando o mesmo executável (usando páginas de memória de cópia na gravação, para que seja eficiente). Ele retorna duas vezes: no pai, ele retorna o PID filho. No filho, ele retorna 0. Normalmente, o processo filho chama execve imediatamente:
execve()
toma um caminho completo para o executável como argumento e substitui o processo de chamada pelo executável. Nesse ponto, o processo recém-criado obtém seu próprio espaço de endereço virtual, ou seja, memória virtual, e a execução começa no ponto de entrada (em um estado especificado pelas regras da plataforma ABI para novos processos).
Nesse momento, o carregador ELF do kernel mapeou os segmentos de texto e dados do executável na memória, como se tivesse usado a mmap()
chamada do sistema (com mapeamentos compartilhados somente leitura e leitura / gravação privados, respectivamente). O BSS também é mapeado como se estivesse com MAP_ANONYMOUS. (BTW, estou ignorando o vínculo dinâmico aqui por simplicidade: o vinculador dinâmico open()
es e mmap()
todas as bibliotecas dinâmicas antes de pular para o ponto de entrada do executável principal.)
Somente algumas páginas são realmente carregadas na memória do disco antes que um recém-exec () ed comece a executar seu próprio código. As páginas adicionais serão paginadas conforme necessário, se / quando o processo tocar essas partes do seu espaço de endereço virtual. (Pré-carregar qualquer página de código ou dados antes de começar a executar o código do espaço do usuário é apenas uma otimização de desempenho.)
O arquivo executável é identificado pelo inode no nível inferior. Depois que o arquivo começa a ser executado, o kernel mantém o conteúdo do arquivo intacto pela referência do inode, não pelo nome do arquivo, como para descritores de arquivos abertos ou mapeamentos de memória suportados por arquivos. Assim, você pode mover o executável facilmente para outro local do sistema de arquivos ou mesmo para um sistema de arquivos diferente. Como uma observação lateral, para verificar as várias estatísticas do processo, você pode espiar no /proc/PID
diretório (PID é o ID do processo do processo fornecido). Você pode até abrir o arquivo executável como /proc/PID/exe
, mesmo que tenha sido desvinculado do disco.
Agora vamos cavar o movimento:
Quando você move um arquivo dentro de um mesmo sistema de arquivos, a chamada do sistema que é executada é rename()
, que apenas renomeia o arquivo para outro nome, o inode do arquivo permanece o mesmo.
Enquanto entre dois sistemas de arquivos diferentes, duas coisas acontecem:
O conteúdo do arquivo foi copiado primeiro para o novo local, por read()
ewrite()
Depois disso, o arquivo é desvinculado do diretório de origem unlink()
e, obviamente, o arquivo receberá um novo inode no novo sistema de arquivos.
rm
está na verdade apenas unlink()
inserindo o arquivo fornecido na árvore de diretórios, portanto, ter a permissão de gravação no diretório garante o direito de remover qualquer arquivo desse diretório.
Agora, por diversão, imagine o que acontece quando você move arquivos entre dois itens e não tem permissão para unlink()
o arquivo da fonte?
Bem, o arquivo será copiado para o destino primeiro ( read()
, write()
) e depois unlink()
falhará devido à permissão insuficiente. Portanto, o arquivo permanecerá nos dois sistemas de arquivos !!