Quando um processo é iniciado a partir de um shell, por que o shell se bifurca antes de executar o processo?
Por exemplo, quando o usuário digita grep blabla foo
, por que o shell não pode simplesmente chamar exec()
grep sem um shell filho?
Além disso, quando um shell se bifurca dentro de um emulador de terminal da GUI, ele inicia outro emulador de terminal? (como pts/13
iniciar pts/14
)
fonte
exec grep blabla foo
. Obviamente, nesse caso em particular, não será muito útil (já que a janela do terminal será fechada assim que o grep terminar), mas pode ser útil ocasionalmente (por exemplo, se você estiver iniciando outro shell, talvez via ssh / sudo / screen, e não pretende retornar ao original, ou se o processo do shell em que você está executando isso for um sub-shell que nunca deve executar mais de um comando).bash -c 'grep foo bar'
e exec chamando existe forma de festa otimização faz para você automaticamenteDe acordo com o
pts
, verifique você mesmo: em um shell, executepara conhecer seu ID do processo (PID), eu tenho por exemplo
Em seguida, execute por exemplo
sleep 60
e, em outro terminalPortanto, não, em geral você tem o mesmo tty associado ao processo. (Observe que este é seu
sleep
porque ele tem seu shell como pai).fonte
TL; DR : porque este é o método ideal para criar novos processos e manter o controle no shell interativo
fork () é necessário para processos e tubos
Para responder à parte específica desta pergunta, se
grep blabla foo
fosse chamadoexec()
diretamente via pai, o pai aproveitaria a existência e seu PID com todos os recursos seria assumidogrep blabla foo
.No entanto, vamos falar em geral sobre
exec()
efork()
. A principal razão para esse comportamento é porquefork()/exec()
é o método padrão de criação de um novo processo no Unix / Linux, e isso não é algo específico do bash; esse método existe desde o início e é influenciado por esse mesmo método nos sistemas operacionais já existentes da época. Parafraseando um pouco a resposta de goldilocks em uma pergunta relacionada,fork()
criar um novo processo é mais fácil, pois o kernel tem menos trabalho a ser feito no que diz respeito à alocação de recursos e muitas propriedades (como descritores de arquivo, ambiente etc.) - tudo pode ser herdado do processo pai (neste caso, debash
).Em segundo lugar, no que diz respeito aos shells interativos, você não pode executar um comando externo sem fazer bifurcação. Para iniciar um executável que vive no disco (por exemplo
/bin/df -h
), é necessário chamar uma dasexec()
funções da família, comoexecve()
, que substituirá o pai pelo novo processo, assumirá o seu PID e os descritores de arquivo existentes, etc. Para o shell interativo, você deseja que o controle retorne ao usuário e deixe o shell interativo pai continuar. Portanto, a melhor maneira é criar um subprocesso viafork()
e deixar que esse processo seja retomado viaexecve()
. Portanto, o PID 1156 do shell interativo geraria um filho viafork()
PID 1157 e depois chamariaexecve("/bin/df",["df","-h"],&environment)
, o que é/bin/df -h
executado com o PID 1157. Agora, o shell precisa aguardar o processo sair e retornar o controle a ele.No caso de você precisar criar um canal entre dois ou mais comandos, por exemplo
df | grep
, é necessário criar dois descritores de arquivo (isto é, ler e gravar o final do canal que vem dopipe()
syscall) e, de alguma forma, permitir que dois novos processos os herdem. Isso é feito no processo de bifurcação de novos processos e, em seguida, copiando a extremidade de gravação do canal viadup2()
chamada para seustdout
aka fd 1 (por isso, se a extremidade de gravação for fd 4, nós o fazemosdup2(4,1)
). Quando ocorre aexec()
desova,df
o processo filho não pensa em nadastdout
e escreve para ela sem estar ciente (a menos que verifique ativamente) de que sua saída realmente é prejudicial. Mesmo processo acontecegrep
, exceto nósfork()
, tome fim de leitura de tubo com fd 3 edup(3,0)
antes da desovagrep
comexec()
. Todo esse processo pai ainda está lá, esperando para recuperar o controle assim que o pipeline for concluído.No caso de comandos internos, geralmente o shell não funciona
fork()
, com exceção dosource
comando. Subshells exigemfork()
.Em suma, este é um mecanismo necessário e útil.
Desvantagens de bifurcação e otimizações
Agora, isso é diferente para shells não interativos , como
bash -c '<simple command>'
. Apesar defork()/exec()
ser o método ideal para processar muitos comandos, é um desperdício de recursos quando você tem apenas um único comando. Para citar Stéphane Chazelas a partir deste post :Portanto, muitos shells (não apenas
bash
) são usadosexec()
para permitir que issobash -c ''
seja assumido por esse único comando simples. E exatamente pelas razões expostas acima, é melhor minimizar os pipelines nos scripts de shell. Muitas vezes, você pode ver iniciantes fazendo algo assim:Claro, isso vai
fork()
3 processos. Este é um exemplo simples, mas considere um arquivo grande, no intervalo de Gigabytes. Seria muito mais eficiente com um processo:O desperdício de recursos, na verdade, pode ser uma forma de ataque de negação de serviço e, em particular, bombas de garfo são criadas por meio de funções de shell que se chamam em pipeline, o que bifurca várias cópias de si mesmas. Atualmente, isso é mitigado via limitação do número máximo de processos nos cgroups no systemd , que o Ubuntu também usa desde a versão 15.04.
Claro que isso não significa bifurcação é apenas ruim. Ainda é um mecanismo útil, como discutido anteriormente, mas, no caso de você poder se safar com menos processos e consecutivamente menos recursos e, portanto, com melhor desempenho, evite,
fork()
se possível.Veja também
fonte
Para cada comando (exemplo: grep) emitido no prompt do bash, você realmente pretende iniciar um novo processo e depois retornar ao prompt do bash após a execução.
Se o processo do shell (bash) chamar exec () para executar o grep, o processo do shell será substituído pelo grep. O Grep funcionará bem, mas após a execução, o controle não poderá retornar ao shell porque o processo bash já foi substituído.
Por esse motivo, o bash chama fork (), que não substitui o processo atual.
fonte