Deixe-me dividi-lo.
Quando você executa um executável, uma seqüê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 copy-on-write, portanto, é eficiente) . Ele retorna duas vezes: no pai, 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 um argumento e substitui o processo de chamada pelo executável. Neste 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 em seu ponto de entrada (em um estado especificado pelas regras da plataforma ABI para novos processos).
Neste ponto, o carregador ELF do kernel mapeou o texto e dados segmentos do executável na memória, como se tivesse usado o mmap()
chamada de sistema (com mapeamentos de leitura-gravação compartilhados e somente leitura respectivamente). O BSS também é mapeado como se com MAP_ANONYMOUS. (BTW, estou ignorando a vinculação dinâmica aqui para simplificar: o vinculador dinâmico open()
se mmap()
s todas as bibliotecas dinâmicas antes de pular para o ponto de entrada do executável principal.)
Apenas algumas páginas são realmente carregadas na memória do disco antes de um recém-executado executar o seu próprio código. Outras páginas são pagas paginadas conforme necessário, se / quando o processo tocar essas partes de seu espaço de endereço virtual. (O pré-carregamento de 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çou 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 com backup de arquivos. Assim, você pode facilmente mover o executável para outro local do sistema de arquivos ou até mesmo em um sistema de arquivos diferente. Como uma nota lateral, para verificar várias estatísticas do processo, você pode espiar no diretório /proc/PID
(PID é o ID do processo de determinado processo). Você pode até mesmo abrir o arquivo executável como /proc/PID/exe
, mesmo que ele tenha sido desvinculado do disco.
Agora vamos analisar 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.
Considerando que entre dois sistemas de arquivos diferentes, duas coisas acontecem:
-
O conteúdo do arquivo é copiado primeiro para o novo local, por
read()
ewrite()
-
Depois disso, o arquivo é desvinculado do diretório de origem usando
unlink()
e, obviamente, o arquivo receberá um novo inode no novo sistema de arquivos.
rm
é na verdade apenas unlink()
-ing do arquivo fornecido na árvore de diretórios, portanto, ter a permissão de gravação no diretório fornecerá a você o direito de remover qualquer arquivo desse diretório.
Agora, por diversão, imagine o que acontece quando você está movendo arquivos entre dois arquivos e não tem permissão para unlink()
do arquivo da fonte?
Bem, o arquivo será copiado para o destino inicialmente ( read()
, write()
) e, em seguida, unlink()
falhará devido a permissão insuficiente. Então, o arquivo permanecerá em ambos os sistemas de arquivos!