
No Unix sempre que queremos criar um novo processo, bifurcamos o processo atual, criando um novo processo filho que é exatamente igual ao processo pai; em seguida, fazemos uma chamada de sistema exec para substituir todos os dados do processo pai pelos do novo processo.
Por que criamos uma cópia do processo pai em primeiro lugar e não criamos um novo processo diretamente?
Responder1
A resposta curta é,fork
está no Unix porque foi fácil de encaixar no sistema existente na época e porque umsistema antecessor em Berkeleyhavia usado o conceito de garfos.
DeA evolução do sistema Unix de compartilhamento de tempo(o texto relevante foidestacado):
O controle de processos em sua forma moderna foi projetado e implementado em poucos dias. É surpreendente a facilidade com que se adaptou ao sistema existente; ao mesmo tempo é fácil ver comoalgumas das características ligeiramente incomuns do design estão presentes precisamente porque representavam mudanças pequenas e facilmente codificadas no que existia. Um bom exemplo é a separação das funções fork e exec. O modelo mais comum para a criação de novos processos envolve a especificação de um programa para a execução do processo; no Unix, um processo bifurcado continua a executar o mesmo programa que seu pai até executar um exec explícito. A separação das funções certamente não é exclusiva do Unix, ena verdade, estava presente no sistema de time-sharing de Berkeley, que era bem conhecido por Thompson. Ainda assim, parece razoável supor queexiste no Unix principalmente por causa da facilidade com que o fork pode ser implementado sem mudar muito mais. O sistema já administrou vários (ou seja, dois) processos; havia uma tabela de processos e os processos eram trocados entre a memória principal e o disco. A implementação inicial do fork exigiu apenas
1) Expansão da tabela de processos
2) Adição de uma chamada fork que copiou o processo atual para a área de swap do disco, usando as primitivas de IO de swap já existentes, e fez alguns ajustes na tabela de processos.
Na verdade, a chamada de fork do PDP-7 exigia precisamente 27 linhas de código assembly. É claro que outras mudanças no sistema operacional e nos programas do usuário foram necessárias, e algumas delas foram bastante interessantes e inesperadas. Masum fork-exec combinado teria sido consideravelmente mais complicado, até porque exec como tal não existia; sua função já foi executada, usando IO explícito, pelo shell.
Desde esse artigo, o Unix evoluiu. fork
seguido por exec
não é mais a única maneira de executar um programa.
garfofoi criado para ser um fork mais eficiente para o caso em que o novo processo pretende fazer um exec logo após o fork. Depois de fazer um vfork, os processos pai e filho compartilham o mesmo espaço de dados, e o processo pai é suspenso até que o processo filho execute um programa ou saia.
posix_spawncria um novo processo e executa um arquivo em uma única chamada de sistema. São necessários vários parâmetros que permitem compartilhar seletivamente os arquivos abertos do chamador e copiar a disposição do sinal e outros atributos para o novo processo.
Responder2
[Vou repetir parte da minha resposta deaqui.]
Por que não ter apenas um comando que crie um novo processo do zero? Não é absurdo e ineficiente copiar um que só vai ser substituído imediatamente?
Na verdade, isso provavelmente não seria tão eficiente por alguns motivos:
A "cópia" produzida por
fork()
é um pouco uma abstração, já que o kernel usa umcopiar na gravaçãosistema; tudo o que realmente precisa ser criado é um mapa de memória virtual. Se a cópia chamar imediatamenteexec()
, a maioria dos dados que teriam sido copiados se tivessem sido modificados pela atividade do processo nunca precisarão ser copiados/criados porque o processo não faz nada que exija seu uso.Vários aspectos significativos do processo filho (por exemplo, seu ambiente) não precisam ser duplicados individualmente ou definidos com base em uma análise complexa do contexto, etc. este é o sistema bastante intuitivo com o qual estamos familiarizados.
Para explicar um pouco mais o número 1, a memória que é "copiada", mas nunca acessada posteriormente, nunca é realmente copiada, pelo menos na maioria dos casos. Uma exceção neste contextopoderseja se você bifurcou um processo e fez com que o processo pai fosse encerrado antes que o filho fosse substituído por exec()
. eu digopoderporque grande parte do pai poderia ser armazenada em cache se houvesse memória livre suficiente e não tenho certeza até que ponto isso seria explorado (o que dependeria da implementação do sistema operacional).
Claro, isso não significa que o uso de uma cópiamaiseficiente do que usar uma folha em branco - exceto que "a folha em branco" não é literalmente nada e deve envolver alocação. O sistema poderia ter um modelo de processo genérico em branco/novo que copiasse da mesma maneira, 1 mas isso não salvaria realmente nada em comparação com a bifurcação de cópia na gravação. Portanto, o número 1 apenas demonstra que usar um "novo" processo vazio não seria mais eficiente.
O ponto 2 explica por que usar o garfo é provavelmente mais eficiente. O ambiente de um filho é herdado de seu pai, mesmo que seja um executável completamente diferente. Por exemplo, se o processo pai for um shell e o processo filho um navegador da Web, $HOME
ainda será o mesmo para ambos, mas como qualquer um deles poderá alterá-lo posteriormente, essas deverão ser duas cópias separadas. Aquele na criança é produzido pelo original fork()
.
1. Uma estratégia que pode não fazer muito sentido literal, mas o que quero dizer é que criar um processo envolve mais do que copiar sua imagem do disco para a memória.
Responder3
Eu acho que a razão pela qual o Unix tinha apenas ofork
função de criar novos processos é resultado daFilosofia Unix
Eles constroem uma função que faz uma coisa bem. Ele cria um processo filho.
O que se faz com o novo processo depende do programador. Ele pode usar uma das exec*
funções e iniciar um programa diferente, ou não pode usar exec e usar as duas instâncias do mesmo programa, o que pode ser útil.
Então você obtém um maior grau de liberdade, já que pode usar
- garfo sem exec*
- bifurcar com exec* ou
- apenas exec* sem fork
e além disso você só precisa memorizar as fork
chamadas exec*
de função, o que na década de 1970 você tinha que fazer.
Responder4
A função fork() não serve apenas para copiar o processo pai, ela retorna um valor que indica que o processo é o processo pai ou filho, a imagem abaixo explica como você pode usar fork() como pai e como filho:
conforme mostrado quando o processo é o pai fork() retorna o ID do processo filho PID
, caso contrário, ele retorna0
por exemplo você pode fazer uso dele se tiver um processo (servidor web) que receba as requisições e a cada requisição ele crie um son process
para processar essa requisição, aqui o pai e seus filhos têm trabalhos diferentes.
ASSIM, não executar uma cópia de um processo não é exatamente a mesma coisa que fork().