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?
process
architecture
fork
sarthak
fonte
fonte
Respostas:
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.Em A evolução do sistema de compartilhamento de tempo Unix (o texto relevante foi destacado ):
Desde esse artigo, o Unix evoluiu.
fork
seguido porexec
não é mais a única maneira de executar um programa.O 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.
fonte
posix_spawn()
executar as mesmas tarefas de reabastecimento pós-garfo que podem ser feitas facilmente usando ofork()
código embutido tornam um argumento convincente porfork()
ser muito mais simples de usar.[Vou repetir parte da minha resposta daqui .]
Por que não apenas ter um comando que cria um novo processo do zero? Não é absurdo e ineficiente copiar um que só será substituído imediatamente?
De fato, isso provavelmente não seria tão eficiente por alguns motivos:
A "cópia", produzido por
fork()
um pouco de uma abstração, uma vez que o kernel usa um copy-on-write sistema ; tudo o que realmente precisa ser criado é um mapa de memória virtual. Se a cópia ligar imediatamenteexec()
, a maioria dos dados que teriam sido copiados se tivessem sido modificados pela atividade do processo nunca precisará realmente ser copiada / criada 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 ou configurados individualmente com base em uma análise complexa do contexto etc. Eles assumem que eles são os mesmos 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 nesse contexto pode ser se você bifurcar um processo e depois encerrar o processo pai antes que o filho se substitua
exec()
. Eu digo poder porque muito do pai poderia ser armazenada em cache se não houver memória livre suficiente, e eu não tenho certeza até que ponto isso seria explorado (que dependem da implementação OS).Obviamente, isso não torna a cópia mais eficiente do que usar 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 copie da mesma maneira, 1 mas que, na verdade, não salvaria nada em comparação com o fork de copiar na gravação. Portanto, o nº 1 apenas demonstra que o uso de 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, elas deverão ser duas cópias separadas. O da criança é produzido pelo originalfork()
.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 na memória do disco.
fonte
fork()
pode muito rapidamente (como GL mencionado, na ordem de 27 linhas de montagem). Olhando na outra direção, se você deseja "criar um processo do zero",fork()
custa apenas um pouquinho mais do que começar com um processo criado em branco (27 linhas de montagem + custo de fechamento de identificadores de arquivo). Portanto,fork
lida com o garfo e cria bem, enquantocreate
só pode lidar com criar bem.fork
copiava toda a memória do processo e era muito caro.Eu acho que o motivo pelo qual o Unix tinha apenas a
fork
função de criar novos processos é resultado da filosofia do UnixEles 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 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.Assim, você obtém um maior grau de liberdade, pois pode usar
e, além disso você só tem que memorizar os
fork
e asexec*
chamadas de função, que na década de 1970 que tinha que fazer.fonte
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 gravando 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.
fonte
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:
conforme mostrado quando o processo é o fork pai () retorna o ID do processo filho; caso
PID
contrário, ele retorna0
por exemplo, você pode utilizá-lo se tiver um processo (servidor da web) que recebe as solicitações e, a cada solicitação, cria um
son process
para processar essa solicitação, aqui o pai e seus filhos têm trabalhos diferentes.Portanto, não é possível executar uma cópia de um processo exatamente como fork ().
fonte
fork
, se você assumir que você querfork
, em primeiro lugarO redirecionamento de E / S é mais facilmente implementado após o fork e antes do 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.
fonte
Acho que todo mundo aqui sabe como funciona o fork, mas a questão é por que precisamos criar uma duplicata exata do pai usando o fork? Resposta ==> Veja um exemplo de servidor (sem bifurcação), enquanto o cliente-1 está acessando o servidor, se ao mesmo tempo o segundo cliente-2 chegar e quiser acessar o servidor, mas o servidor não der permissão ao recém-chegado cliente-2 porque o servidor está ocupado para atender o cliente-1, portanto o cliente-2 precisa aguardar. após a conclusão de todos os serviços para o cliente-1, o cliente-2 agora pode acessar o servidor. Agora, considere se ao mesmo tempo o cliente-3 chega, portanto o cliente-3 precisa aguardar até que todos os serviços para o cliente-2 sejam concluídos.Tome o cenário em que milhares de clientes precisam acessar o servidor ao mesmo tempo ... então todos os clientes precisam aguarde (o servidor está ocupado !!).
Isso é evitado através da criação (usando bifurcação) de cópia duplicada exata (ou seja, filho) do servidor, onde cada filho (que é cópia duplicada exata de seu pai ou seja, servidor) é dedicado ao cliente recém-chegado, portanto, simultaneamente, todos os clientes acessam o mesmo servidor.
fonte
fork
chamada que copia o processo pai é que você não precisa ter dois programas separados - mas ter programas separados (por exemplo,inetd
) pode tornar o sistema mais modular.