Então, achei que tinha um bom entendimento disso, mas apenas fiz um teste (em resposta a uma conversa em que discordava de alguém) e descobri que meu entendimento é defeituoso ...
Com o máximo de detalhes possível, o que exatamente acontece quando executo um arquivo no meu shell? O que quero dizer é que, se eu digitar: ./somefile some arguments
no meu shell e pressionar return (e somefile
existir no cwd e eu tiver lido + executar permissões ativadas somefile
), o que acontecerá sob o capô?
Eu pensei que a resposta era:
- O shell faz um syscall to
exec
, passando o caminho parasomefile
- O kernel examina
somefile
e analisa o número mágico do arquivo para determinar se é um formato que o processador pode manipular - Se o número mágico indicar que o arquivo está em um formato que o processador possa executar,
- um novo processo é criado (com uma entrada na tabela de processos)
somefile
é lido / mapeado para a memória. Uma pilha é criada e a execução salta para o ponto de entrada do código desomefile
,ARGV
inicializado em uma matriz dos parâmetros (achar**
,["some","arguments"]
)
- Se o número mágico for um shebang ,
exec()
gera um novo processo como acima, mas o executável usado é o intérprete referenciado pelo shebang (por exemplo,/bin/bash
ou/bin/perl
) esomefile
é passado paraSTDIN
- Se o arquivo não tiver um número mágico válido, ocorrerá um erro como "arquivo inválido (número mágico incorreto): erro de formato Exec"
No entanto, alguém me disse que, se o arquivo é texto simples, o shell tenta executar os comandos (como se eu tivesse digitado bash somefile
). Eu não acreditava nisso, mas apenas tentei e estava correto. Portanto, tenho claramente algumas idéias erradas sobre o que realmente acontece aqui e gostaria de entender a mecânica.
O que exatamente acontece quando executo um arquivo no meu shell? (com o máximo de detalhes possível ...)
source somefile
é muito diferente de um novo processo que está sendo iniciado por./somefile
, no entanto../somefile
faria com que o bash execute os comandossomefile
se o arquivo não tiver um número mágico. Eu pensei que seria apenas exibir um erro, e em vez disso, parece efetivamentesource somefile
somefile
for um arquivo de texto, um novo shell será gerado se tentar executá-lo. Um arquivoecho $$
se comporta de maneira diferente se eu executar o código-fonte.Respostas:
A resposta definitiva para "como os programas são executados" no Linux é o par de artigos no LWN.net intitulado, surpreendentemente, Como os programas são executados e Como os programas são executados: binários ELF . O primeiro artigo aborda os scripts brevemente. (A rigor, a resposta definitiva está no código-fonte, mas esses artigos são mais fáceis de ler e fornecem links para o código-fonte.)
Um pouco de experimentação mostra que você praticamente acertou e que a execução de um arquivo contendo uma lista simples de comandos, sem um shebang, precisa ser manipulada pelo shell. A página de manual execve (2) contém código fonte para um programa de teste, execve; usaremos isso para ver o que acontece sem um shell. Primeiro, escreva um script de teste
testscr1
, contendoe outro
testscr2
, contendo apenasTorne os dois executáveis e verifique se os dois são executados a partir de um shell:
Agora tente novamente, usando
execve
(assumindo que você o criou no diretório atual):testscr1
ainda funciona, mastestscr2
produzIsso mostra que o shell lida de maneira
testscr2
diferente. Porém, ele não processa o próprio script, ainda o usa/bin/sh
; isso pode ser verificado canalizandotestscr2
paraless
:No meu sistema, eu recebo
Como você pode ver, há o shell que eu estava usando,
zsh
que foi iniciadoless
, e um segundo shell, simplessh
(dash
no meu sistema), para executar o script, que foi executadopstree
. Emzsh
este é tratado porzexecve
emSrc/exec.c
: a casca usosexecve(2)
para tentar executar o comando, e se isso falhar, ele lê o arquivo para ver se ele tem um shebang, processá-lo em conformidade (qual o kernel também terá feito), e se isso falha ao tentar executar o arquivosh
, desde que não tenha lido nenhum byte zero no arquivo:bash
tem o mesmo comportamento, implementadoexecute_cmd.c
com um comentário útil (como apontado por taliezin ):POSIX define um conjunto de funções, conhecidos como as
exec(3)
funções , que o envoltórioexecve(2)
e fornecer essa funcionalidade também; veja a resposta de muru para detalhes. No Linux, pelo menos essas funções são implementadas pela biblioteca C, não pelo kernel.fonte
Em parte, isso depende da
exec
função específica da família usada.execve
, como Stephen Kitt mostrou em detalhes, apenas executa arquivos no formato binário correto ou scripts que começam com um shebang adequado.No entanto ,
execlp
eexecvp
vá um passo além: se o shebang não estiver correto, o arquivo será executado/bin/sh
no Linux. Deman 3 exec
:Isso é um pouco suportado pelo POSIX (ênfase minha):
Isso não especifica como o interpretador de comandos é obtido, portanto, mas não especifica que um erro deve ser dado. Eu acho, portanto, que os desenvolvedores do Linux permitiram que esses arquivos fossem executados
/bin/sh
(ou isso já era uma prática comum e eles apenas seguiram o exemplo).FWIW, a página de manual
exec(3)
do FreeBSD também menciona comportamentos semelhantes:AFAICT, no entanto, nenhum shell comum usa
execlp
ouexecvp
diretamente, presumivelmente para um controle mais preciso do ambiente. Todos eles implementam a mesma lógica usandoexecve
.fonte
execl
,execlp
,execle
,execv
,execvp
eexecvpe
são todos front-ends para oexecve
syscall; os primeiros são fornecidos pela biblioteca C, o kernel apenas conheceexecve
(eexecveat
atualmente).Isso pode ser uma adição à resposta de Stephen Kitt, como um comentário da
bash
fonte no arquivoexecute_cmd.c
:fonte
Ele é executado como um shell script, é não originado (por exemplo, variáveis definidas no arquivo executado não afetam fora). Provavelmente vestigial do passado enevoado, quando havia um formato de shell e um executável. Não é um executável, deve ser um script de shell.
fonte
exec()
ou o shell? Quero significativamente mais internos