desenv-web-rp.com

Por que precisamos trabalhar para criar novos processos?

No Unix, sempre que queremos criar um novo processo, bifurcamos o processo atual, criando um novo processo filho que é exatamente igual ao processo pai; então, 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?

100
sarthak

A resposta curta é: fork está no Unix porque era fácil se encaixar no sistema existente na época e porque um sistema predecessor em Berkeley havia usado o conceito de garfos.

De A evolução do sistema de compartilhamento de tempo Unix (o texto relevante foi destacado ):

O controle de processo em sua forma moderna foi projetado e implementado em alguns dias. É surpreendente como ele se encaixou facilmente no sistema existente; ao mesmo tempo, é fácil ver como alguns dos recursos levemente incomuns do design estão presentes precisamente porque representavam pequenas alterações 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 executando o mesmo programa que seu pai até executar um exec explícito. A separação das funções certamente não é exclusiva do Unix e estava presente no sistema de compartilhamento de tempo de Berkeley, que era bem conhecido por Thompson Ainda assim, parece razoável supor que exista no Unix principalmente devido à facilidade com que o fork pode ser implementado sem alterar muito mais . O sistema já manipulou vários processos (ou seja, dois); havia uma tabela de processos e os processos foram trocados entre a memória principal e o disco. A implementação inicial do fork exigia apenas

1) Expansão da tabela de processos

2) Adição de uma chamada de bifurcação que copiou o processo atual para a área de troca de disco, usando a troca já existente IO primitivas, e fez alguns ajustes na tabela de processos.

De fato, a chamada da bifurcação do PDP-7 exigiu precisamente 27 linhas de código de montagem. Obviamente, outras alterações no sistema operacional e nos programas do usuário foram necessárias, e algumas delas foram bastante interessantes e inesperadas. Mas um fork-exec combinado seria consideravelmente mais complicado , apenas porque o exec em si não existia; sua função já foi executada, usando E/S explícita, pelo Shell.

Desde esse artigo, o Unix evoluiu. fork seguido por exec não é mais a única maneira de executar um programa.

  • vfork foi criado para ser um fork mais eficiente para o caso em que o novo processo pretende executar um executivo logo após o fork. Depois de executar 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_spawn cria um novo processo e executa um arquivo em uma única chamada do sistema. São necessários vários parâmetros que permitem compartilhar seletivamente os arquivos abertos do chamador e copiar sua disposição do sinal e outros atributos para o novo processo.

65
Mark Plotnick

[Vou repetir parte da minha resposta de aqui .]

Por que não apenas um comando que cria um novo processo do zero? Não é absurdo e ineficiente copiar um que só será substituído corretamente longe?

De fato, isso provavelmente não seria tão eficiente por alguns motivos:

  1. A "cópia" produzida por fork() é uma abstração, pois o kernel usa um sistema copiar na gravação; tudo o que realmente precisa ser criado é um mapa de memória virtual. Se a cópia chamar imediatamente exec(), a maioria dos dados que seriam copiados se tivessem sido modificados pela atividade do processo nunca precisará realmente ser copiada/criada porque o processo não faz nada que exija sua usar.

  2. Vários aspectos significativos do processo filho (por exemplo, seu ambiente) não precisam ser duplicados ou configurados individualmente com base em uma análise complexa do contexto etc. Eles assumem que eles são iguais aos do processo de chamada e este é o sistema bastante intuitivo com o qual estamos familiarizados.

Para explicar # 1 um pouco mais, a memória que é "copiada" mas nunca acessada posteriormente nunca é realmente copiada, pelo menos na maioria dos casos. Uma exceção neste contexto pode seja se você bifurcou um processo, depois o processo pai foi encerrado antes que o filho se substituísse por exec(). Eu digo pode porque grande parte do pai pode ser armazenada em cache se houver memória livre suficiente e não sei até que ponto isso seria explorado (o que dependeria da implementação do SO).

Obviamente, isso não torna superficial o uso de uma cópia mais eficiente do que a utilização de uma folha em branco - exceto "a folha em branco" não é literalmente nada e deve envolver alocação. O sistema pode ter um modelo de processo em branco/novo genérico que copia da mesma maneira,1 mas isso realmente não salvaria nada em comparação com o fork da cópia na gravação. Portanto, o nº 1 apenas demonstra que usar um "novo" processo vazio não seria mais eficiente.

O ponto 2 explica por que o uso do garfo é provavelmente mais eficiente. O ambiente de uma criança é herdado de seu pai, mesmo que seja um executável completamente diferente. Por exemplo, se o processo pai for um Shell e o filho um navegador da Web, $HOME Ainda for o mesmo para os dois, mas como qualquer um deles poderá alterá-lo posteriormente, essas duas cópias deverão ser separadas. O da criança é produzido pelo original fork().

1. Uma estratégia que pode não fazer muito sentido literal, mas o que quero dizer é que a criação de um processo envolve mais do que copiar sua imagem na memória do disco.

36
goldilocks

Eu acho que a razão pela qual o Unix tinha apenas a função fork para criar novos processos é resultado da filosofia do Unix

Eles constroem uma função que faz uma coisa bem. Cria um processo filho.

O que se faz com o novo processo depende do programador. Ele pode usar um dos exec* funciona e inicia um programa diferente, ou ele não pode usar exec e usar as duas instâncias do mesmo programa, o que pode ser útil.

Assim, você obtém um maior grau de liberdade, pois pode usar

  1. garfo sem exec *
  2. bifurque-se com exec * ou
  3. apenas exec * sem garfo

além disso, você só precisa memorizar os fork e os exec* chamadas de função, que na década de 1970 você tinha que fazer.

6
Raphael Ahrens

Existem duas filosofias de criação de processos: bifurcar-se com herança e criar com argumentos. Unix usa garfo, obviamente. (OSE, por exemplo, e VMS usam o método create.) O Unix possui MUITAS características herdáveis ​​e mais são adicionadas periodicamente. Por herança, essas novas características podem ser adicionadas SEM ALTERAR PROGRAMAS EXISTENTES! Usando um modelo de criação com argumentos, adicionar novas características significaria adicionar novos argumentos à chamada de criação. O modelo Unix é mais simples.

Ele também oferece o modelo fork-sem-exec altamente útil, onde um processo pode se dividir em várias partes. Isso era vital quando não havia forma de E/S assíncrona e é útil ao tirar vantagem de várias CPUs em um sistema. (Pré-threads.) Fiz isso muito ao longo dos anos, mesmo recentemente. Em essência, ele permite agrupar vários 'programas' em um único programa, para que não haja absolutamente espaço para corrupção ou incompatibilidade de versão, etc.

O modelo fork/exec também oferece a capacidade de um filho específico herdar um ambiente radicalmente estranho, configurado entre o fork e o exec. Coisas como descritores de arquivos herdados, especialmente. (Uma extensão do stdio fd's.) O modelo de criação não oferece a capacidade de herdar qualquer coisa que não foi prevista pelos criadores da chamada de criação.

Alguns sistemas também podem oferecer suporte à compilação dinâmica de código nativo, onde o processo está efetivamente escrevendo seu próprio programa de código nativo. Em outras palavras, ele quer um novo programa que esteja gravando em tempo real, SEM ter que passar pelo ciclo de código-fonte/compilador/vinculador e ocupando espaço em disco. (Acredito que exista um sistema de linguagem Verilog que faça isso.) O modelo de fork suporta isso, o modelo de criação normalmente não.

5
Jim Cathey

A função fork () não serve apenas para copiar o processo pai, ele retorna um valor que se refere ao processo pai ou filho, a imagem abaixo explica como você pode usar o fork () como pai e filho:

enter image description here

conforme mostrado quando o processo é o fork pai () retorna o ID do processo filho PID else else retorna 0

por exemplo, você pode usá-lo se tiver um processo (servidor da web) que recebe as solicitações e, em cada solicitação, cria um son process para processar esta solicitação, aqui o pai e seus filhos têm empregos diferentes.

Portanto, não é possível executar uma cópia de um processo exatamente como fork ().

2
Nidal

O redirecionamento de E/S é mais facilmente implementado após o fork e o exec. A criança, sabendo que é a criança, pode fechar os descritores de arquivos, abrir novos, dup () ou dup2 () para obter o número fd correto, etc., tudo sem afetar o pai. Depois de fazer isso, e talvez qualquer variável de ambiente desejada seja alterada (também não afetando o pai), ele pode executar o novo programa no ambiente personalizado.

0
Richard Hamilton