Por que precisamos nos dedicar para criar novos processos?

89

No Unix sempre que desejamos criar um novo processo, aplicamos 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 pelo do novo processo.

Por que criamos uma cópia do processo pai em primeiro lugar e não criamos um novo processo diretamente?

    
por sarthak 11.06.2014 / 20:09

7 respostas

59

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

De A Evolução do Sistema de Compartilhamento de Tempo Unix (texto relevante foi realçado ):

Process control in its modern form was designed and implemented within a couple of days. It is astonishing how easily it fitted into the existing system; at the same time it is easy to see how some of the slightly unusual features of the design are present precisely because they represented small, easily-coded changes to what existed. A good example is the separation of the fork and exec functions. The most common model for the creation of new processes involves specifying a program for the process to execute; in Unix, a forked process continues to run the same program as its parent until it performs an explicit exec. The separation of the functions is certainly not unique to Unix, and in fact it was present in the Berkeley time-sharing system, which was well-known to Thompson. Still, it seems reasonable to suppose that it exists in Unix mainly because of the ease with which fork could be implemented without changing much else. The system already handled multiple (i.e. two) processes; there was a process table, and the processes were swapped between main memory and the disk. The initial implementation of fork required only

1) Expansion of the process table

2) Addition of a fork call that copied the current process to the disk swap area, using the already existing swap IO primitives, and made some adjustments to the process table.

In fact, the PDP-7's fork call required precisely 27 lines of assembly code. Of course, other changes in the operating system and user programs were required, and some of them were rather interesting and unexpected. But a combined fork-exec would have been considerably more complicated, if only because exec as such did not exist; its function was already performed, using explicit IO, by the shell.

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

    O
  • vfork foi criado para ser uma bifurcação mais eficiente para o caso em que o O novo processo pretende fazer um exec 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 de sistema . São necessários vários parâmetros que permitem compartilhar seletivamente os arquivos abertos do chamador e copiar sua disposição de sinal e outros atributos para o novo processo.

por 11.06.2014 / 23:59
32

[repito parte da minha resposta de aqui .]

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

Na verdade, isso provavelmente não seria tão eficiente por alguns motivos:

  1. A "cópia" produzida por fork() é um pouco de abstração, já que o kernel usa um sistema copy-on-write ; 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 teriam sido copiados se tivessem sido modificados pela atividade do processo nunca terão que ser copiados / criados porque o processo não faz nada que exija seu uso.

  2. Vários aspectos significativos do processo filho (por exemplo, seu ambiente) não precisam ser individualmente duplicados ou definidos com base em uma análise complexa do contexto, etc. Eles são apenas considerados como sendo os mesmos do processo de chamada, e este é o sistema razoavelmente intuitivo com o qual estamos familiarizados.

Para explicar # 1 um pouco mais, a memória que é "copiada" mas nunca acessada subseqüentemente nunca é realmente copiada, pelo menos na maioria dos casos. Uma exceção neste contexto pode ser se você bifurcou um processo, depois teve o processo pai sair antes que o filho fosse substituído por exec() . Eu digo poder porque muito do pai poderia ser armazenado 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 SO).

É claro que isso não faz com que a utilização de uma cópia mais seja mais eficiente do que usar uma placa em branco - exceto que "a placa em branco" não é literalmente nada e deve envolver alocação. O sistema pode ter um modelo de processo genérico em branco / novo que copie da mesma forma, 1 , mas que, na verdade, não salvaria nada em comparação com o fork copy-on-write. 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 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 for um navegador da web, $HOME ainda será o mesmo para ambos, mas, como qualquer um poderia alterá-lo posteriormente, essas devem ser duas cópias separadas. O da criança é produzido pelo original fork() .

1. Uma estratégia que pode não fazer muito sentido literal, mas meu ponto é que criar um processo envolve mais do que copiar sua imagem para a memória a partir do disco.

    
por 11.06.2014 / 20:59
5

Acho que o motivo pelo qual o Unix tinha apenas a função fork para criar novos processos é o resultado da filosofia Unix

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

O que se faz com o novo processo é então do programador. Ele pode usar uma das funções exec* e iniciar um programa diferente, ou ele não poderia usar exec e usar as duas instâncias do mesmo programa, o que pode ser útil.

Assim, você obtém um grau maior de liberdade, já que pode usar

  1. garfo sem exec *
  2. garfo com exec * ou
  3. apenas exec * sem garfo

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

    
por 11.06.2014 / 23:24
4

Existem duas filosofias de criação de processos: bifurcar com herança e criar com argumentos. O Unix usa fork, obviamente. (OSE, por exemplo, e VMS usam o método de criação.) O Unix possui MUITAS características hereditárias e mais são adicionadas periodicamente. Através da herança, estas novas características podem ser adicionadas sem mudar os programas existentes! Usando um modelo create-with-arguments, adicionar novas características significaria adicionar novos argumentos à chamada de criação. O modelo Unix é mais simples.

Ele também oferece o modelo fork-without-exec, altamente útil, em que um processo pode se dividir em várias partes. Isso foi vital quando não havia nenhuma forma de E / S assíncrona e é útil quando se aproveita várias CPUs em um sistema. (Pré-tópicos.) Eu fiz isso muito ao longo dos anos, até recentemente. Em essência, permite contatar vários "programas" em um único programa, de modo que não há espaço para corrupção ou incompatibilidade de versões, etc.

O modelo fork / exec também permite que um filho específico herde 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 tenha sido imaginada pelos criadores da chamada de criação.

Alguns sistemas também podem suportar a compilação dinâmica de código nativo, onde o processo está, com efeito, escrevendo seu próprio programa de código nativo. Em outras palavras, ele quer um novo programa que esteja escrevendo em tempo real, sem ter que passar pelo ciclo de código fonte / compilador / vinculador e ocupando espaço em disco. (Eu acredito que existe um sistema de linguagem Verilog que faz isso.) O modelo fork suporta isso, o modelo de criação normalmente não.

    
por 17.06.2016 / 01:50
2

A função fork () não é apenas copiar o processo pai, ele retorna um valor que indica que o processo é o pai ou o processo filho, a imagem abaixo explica como você pode fazer uso de fork () como um pai e filho:

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

por exemplo, você pode fazer uso dele se você tiver um processo (servidor web) que receba os pedidos e em cada requisição crie um son process para processar este pedido, aqui o pai e seus filhos têm trabalhos diferentes. / p>

Portanto, não executar uma cópia de um processo não é a coisa exata como fork ().

    
por 12.06.2014 / 00:21
0

O redirecionamento de E / S é mais facilmente implementado após o fork e antes do exec. A criança, ciente de que é o filho, pode fechar descritores de arquivos, abrir novos, dup () ou dup2 () para obtê-los no número fd correto, etc, tudo sem afetar o pai. Depois de fazer isso, e talvez todas as mudanças de variáveis de ambiente desejadas (também não afetando o pai), ele pode executar o novo programa no ambiente sob medida.

    
por 19.07.2016 / 20:36
-2

Acho que todos aqui sabem que o fork é trabalho, mas a questão é por que precisamos criar uma duplicata exata do pai usando o fork? Resposta == > Tome um exemplo de servidor (sem fork), enquanto o client-1 está acessando o servidor, se ao mesmo tempo o segundo cliente-2 for acessado e desejar acessar o servidor, mas o servidor não dá a permissão para o recém-chegado cliente-2 porque o servidor está ocupado para servir o client-1, então o client-2 tem que esperar.Depois de todos os serviços para o client-1 serem concluídos, o client-2 agora pode acessar o servidor Agora, considere se, ao mesmo tempo, o cliente-3 chegar, então o cliente-3 tem que esperar até que todos os serviços para o cliente-2 sejam concluídos. Realize o cenário em que os milhares de clientes precisam acessar o servidor ao mesmo tempo. .. então todos os clientes têm que esperar (o servidor está ocupado !!).

Isso é evitado criando (usando fork) uma cópia exata (ie child) do servidor, onde cada filho (que é cópia duplicada exata de seu servidor pai) é dedicado ao cliente recém-chegado, assim, simultaneamente, todos os clientes acessar o mesmo servidor.

    
por 21.09.2016 / 14:00