Tente ler Stevens novamente e esclareça o que você não entende.
vlabrecque
Respostas:
244
De forma simplista, no UNIX, você tem o conceito de processos e programas. Um processo é um ambiente no qual um programa é executado.
A ideia simples por trás do "modelo de execução" do UNIX é que existem duas operações que você pode fazer.
O primeiro é para fork(), que cria um novo processo contendo uma duplicata (principalmente) do programa atual, incluindo seu estado. Existem algumas diferenças entre os dois processos que lhes permitem descobrir quem é o pai e quem é o filho.
A segunda é para exec(), que substitui o programa no processo atual por um programa totalmente novo.
A partir dessas duas operações simples, todo o modelo de execução do UNIX pode ser construído.
Para adicionar mais alguns detalhes ao acima:
O uso fork()e exec()exemplifica o espírito do UNIX, pois fornece uma maneira muito simples de iniciar novos processos.
A fork()chamada torna quase uma duplicação do processo atual, idêntico em quase todos os sentidos (nem tudo é copiado, por exemplo, limites de recursos em algumas implementações, mas a ideia é criar uma cópia o mais próxima possível). Apenas um processo chama,fork() mas dois processos retornam dessa chamada - parece bizarro, mas é realmente muito elegante
O novo processo (chamado de filho) obtém um ID de processo (PID) diferente e tem o PID do processo antigo (o pai) como seu PID pai (PPID).
Como os dois processos agora estão executando exatamente o mesmo código, eles precisam ser capazes de dizer qual é qual - o código de retorno fork()fornece essa informação - o filho obtém 0, o pai obtém o PID do filho (se o fork()falhar, não filho é criado e o pai recebe um código de erro).
Dessa forma, o pai conhece o PID do filho e pode se comunicar com ele, matá-lo, esperá-lo e assim por diante (a criança sempre pode encontrar seu processo pai com uma chamada para getppid()).
A exec()chamada substitui todo o conteúdo atual do processo por um novo programa. Ele carrega o programa no espaço do processo atual e o executa a partir do ponto de entrada.
Portanto, fork()e exec()são frequentemente usados em sequência para fazer um novo programa rodar como filho de um processo atual. Os shells normalmente fazem isso sempre que você tenta executar um programa como find- o shell bifurca, então o filho carrega o findprograma na memória, configurando todos os argumentos da linha de comando, E / S padrão e assim por diante.
Mas eles não precisam ser usados juntos. É perfeitamente aceitável que um programa chame fork()sem seguimento exec()se, por exemplo, o programa contiver código pai e filho (você precisa ter cuidado com o que faz, cada implementação pode ter restrições).
Isso era muito usado (e ainda é) para daemons que simplesmente ouvem em uma porta TCP e bifurcam uma cópia de si mesmos para processar uma solicitação específica enquanto o pai volta a ouvir. Para essa situação, o programa contém o código pai e o filho.
Da mesma forma, os programas que sabem que foram concluídos e apenas desejam executar outro programa não precisam fazê-lo fork(), exec()e wait()/waitpid()para a criança. Eles podem simplesmente carregar o filho diretamente em seu espaço de processo atual com exec().
Algumas implementações do UNIX têm um otimizado fork()que usa o que eles chamam de cópia na gravação. Este é um truque para atrasar a cópia do espaço do processo fork()até que o programa tente alterar algo nesse espaço. Isso é útil para aqueles programas que usam apenas fork()e não exec()porque não precisam copiar um espaço de processo inteiro. No Linux, fork()apenas faz uma cópia das tabelas de páginas e uma nova estrutura de tarefas, exec()fará o trabalho árduo de "separar" a memória dos dois processos.
Se o execfor chamado em seguida fork(e isso é o que acontece principalmente), isso causa uma gravação no espaço do processo e é então copiado para o processo filho, antes que as modificações sejam permitidas.
O Linux também possui um vfork(), ainda mais otimizado, que compartilha quase tudo entre os dois processos. Por causa disso, existem certas restrições quanto ao que a criança pode fazer, e os pais param até que a criança chame exec()ou _exit().
O pai deve ser interrompido (e o filho não tem permissão para retornar da função atual) uma vez que os dois processos compartilham a mesma pilha. Isso é um pouco mais eficiente para o caso de uso clássico de fork()seguido imediatamente por exec().
Note-se que há uma família inteira de execchamadas ( execl, execle, execvee assim por diante), mas execno contexto aqui significa qualquer um deles.
O diagrama a seguir ilustra a fork/execoperação típica em que o bashshell é usado para listar um diretório com o lscomando:
+--------+| pid=7|| ppid=4|| bash |+--------+|| calls fork
V+--------++--------+| pid=7| forks | pid=22|| ppid=4|---------->| ppid=7|| bash || bash |+--------++--------+||| waits for pid 22| calls exec to run ls| V|+--------+|| pid=22||| ppid=7||| ls |
V +--------++--------+|| pid=7|| exits| ppid=4|<---------------+| bash |+--------+|| continues
V
Obrigado pela referência do shell com o programa find. Exatamente o que eu precisava entender.
Usuário
Por que execo utilitário é usado para redirecionar o IO do processo atual? Como o caso "nulo", executando exec sem um argumento, foi usado para essa convenção?
Ray,
@Ray, sempre pensei nisso como uma extensão natural. Se você pensar execno meio de substituir o programa atual (o shell) neste processo por outro, não especificar esse outro programa para substituí-lo pode simplesmente significar que você não deseja substituí-lo.
paxdiablo
Eu vejo o que você quer dizer se por "extensão natural" você quer dizer algo na linha de "crescimento orgânico". Parece que o redirecionamento teria sido adicionado para oferecer suporte à função de substituição de programa, e posso ver esse comportamento permanecendo no caso degenerado de execser chamado sem um programa. Mas é um pouco estranho neste cenário, já que a utilidade original de redirecionar para um novo programa - um programa que seria realmente execexecutado - desaparece e você tem um artefato útil, redirecionando o programa atual - que não está sendo execusado ou iniciado de qualquer forma - em vez disso.
Ray
36
As funções na família exec () têm comportamentos diferentes:
l: os argumentos são passados como uma lista de strings para o main ()
v: os argumentos são passados como uma matriz de strings para o main ()
p: caminho / s para pesquisar o novo programa em execução
Curiosamente, você perdeu execve()em sua lista, que é definida por POSIX, e adicionou execvpe(), que não é definida por POSIX (principalmente por razões de precedente histórico; ele completa o conjunto de funções). Caso contrário, uma explicação útil da convenção de nomenclatura para a família - um complemento útil para paxdiablo 'uma resposta que explica mais sobre o funcionamento das funções.
Jonathan Leffler
E, em sua defesa, vejo que a página de manual do Linux para execvpe()(et al) não lista execve(); ele tem sua própria página de manual separada (pelo menos no Ubuntu 16.04 LTS) - a diferença é que as outras exec()funções da família estão listadas na seção 3 (funções), enquanto que execve()estão listadas na seção 2 (chamadas de sistema). Basicamente, todas as outras funções da família são implementadas em termos de uma chamada para execve().
Jonathan Leffler
18
A execfamília de funções faz com que seu processo execute um programa diferente, substituindo o antigo programa que estava rodando. Ou seja, se você ligar
execl("/bin/ls","ls", NULL);
então o lsprograma é executado com o id do processo, diretório de trabalho atual e usuário / grupo (direitos de acesso) do processo que chamou execl. Depois disso, o programa original não está mais funcionando.
Para iniciar um novo processo, a forkchamada do sistema é usada. Para executar um programa sem substituir o original, você precisa fork, então exec.
Obrigado, isso foi muito útil. Atualmente estou fazendo um projeto que exige que usemos exec () e sua descrição solidificou meu entendimento.
TwilightSparkleTheGeek
7
qual é a função exec e sua família.
A execfamília função é todas as funções usadas para executar um arquivo, como execl, execlp, execle, execv, e execvp.Eles são todos os frontends para execvee fornecer diferentes métodos de chamá-lo.
porque esta função é usada
As funções Exec são usadas quando você deseja executar (lançar) um arquivo (programa).
e como isso funciona.
Eles funcionam substituindo a imagem do processo atual por aquela que você iniciou. Eles substituem (encerrando) o processo atualmente em execução (aquele que chamou o comando exec) pelo novo processo que foi iniciado.
execé frequentemente usado em conjunto com fork, pelo qual vi que você também perguntou, então discutirei isso com isso em mente.
exectransforma o processo atual em outro programa. Se você já assistiu Doctor Who, então é como quando ele se regenera - seu antigo corpo é substituído por um novo corpo.
A maneira como isso acontece com o seu programa execé que muitos dos recursos que o kernel do sistema operacional verifica para ver se o arquivo que você está passando execcomo o argumento do programa (primeiro argumento) é executável pelo usuário atual (id do usuário do processo fazer a execchamada) e, em caso afirmativo, ele substitui o mapeamento de memória virtual do processo atual por uma memória virtual do novo processo e copia os dados argve envpque foram passados na execchamada para uma área desse novo mapa de memória virtual. Várias outras coisas também podem acontecer aqui, mas os arquivos que foram abertos para o programa que chamou execainda estarão abertos para o novo programa e eles compartilharão o mesmo ID de processo, mas o programa que chamou execirá parar (a menos que exec falhou).
O motivo pelo qual isso é feito dessa maneira é que, separando a execução de um novo programa em duas etapas como essa, você pode fazer algumas coisas entre as duas etapas. A coisa mais comum a fazer é certificar-se de que o novo programa possui determinados arquivos abertos como determinados descritores de arquivo. (lembre-se de que os descritores de arquivo não são iguais FILE *, mas são intvalores que o kernel conhece). Fazendo isso, você pode:
int X = open("./output_file.txt", O_WRONLY);pid_t fk = fork();if(!fk){/* in child */
dup2(X,1);/* fd 1 is standard output,
so this makes standard out refer to the same file as X */
close(X);/* I'm using execl here rather than exec because
it's easier to type the arguments. */
execl("/bin/echo","/bin/echo","hello world");
_exit(127);/* should not get here */}elseif(fk ==-1){/* An error happened and you should do something about it. */
perror("fork");/* print an error message */}
close(X);/* The parent doesn't need this anymore */
Quando um processo usa fork (), ele cria uma cópia duplicada de si mesmo e essa duplicata se torna filha do processo. O fork () é implementado usando uma chamada de sistema clone () no linux que retorna duas vezes do kernel.
Um valor diferente de zero (ID de processo do filho) é retornado ao pai.
Um valor zero é retornado ao filho.
Caso o filho não seja criado com sucesso devido a algum problema como memória insuficiente, -1 é retornado ao fork ().
Vamos entender isso com um exemplo:
pid = fork();// Both child and parent will now start execution from here.if(pid <0){//child was not created successfullyreturn1;}elseif(pid ==0){// This is the child process// Child process code goes here}else{// Parent process code goes here}
printf("This is code common to parent and child");
No exemplo, assumimos que exec () não é usado dentro do processo filho.
Mas um pai e um filho diferem em alguns dos atributos do PCB (bloco de controle do processo). Esses são:
PID - filho e pai têm um ID de processo diferente.
Sinais pendentes - a criança não herda os sinais pendentes dos pais. Ele estará vazio para o processo filho quando criado.
Bloqueios de memória - a criança não herda os bloqueios de memória de seus pais. Os bloqueios de memória são bloqueios que podem ser usados para bloquear uma área de memória e, em seguida, essa área de memória não pode ser trocada para o disco.
Bloqueios de registro - o filho não herda os bloqueios de registro do pai. Os bloqueios de registro são associados a um bloco de arquivo ou a um arquivo inteiro.
A utilização de recursos do processo e o tempo de CPU consumido são definidos como zero para o filho.
O filho também não herda temporizadores do pai.
Mas e quanto à memória infantil? Um novo espaço de endereço foi criado para uma criança?
As respostas em não. Após o fork (), pai e filho compartilham o espaço de endereço de memória do pai. No Linux, esse espaço de endereço é dividido em várias páginas. Somente quando a criança escreve em uma das páginas de memória dos pais, uma duplicata dessa página é criada para a criança. Isso também é conhecido como cópia na gravação (Copiar páginas pai apenas quando o filho gravar nelas).
Vamos entender o copy on write com um exemplo.
int x =2;
pid = fork();if(pid ==0){
x =10;// child is changing the value of x or writing to a page// One of the parent stack page will contain this local variable. That page will be duplicated for child and it will store the value 10 in x in duplicated page. }else{
x =4;}
Mas por que a cópia na gravação é necessária?
A criação de um processo típico ocorre por meio da combinação fork () - exec (). Vamos primeiro entender o que exec () faz.
O grupo de funções Exec () substitui o espaço de endereço da criança por um novo programa. Depois que exec () é chamado dentro de um filho, um espaço de endereço separado será criado para o filho, que é totalmente diferente do pai.
Se não houvesse cópia no mecanismo de gravação associado a fork (), páginas duplicadas teriam sido criadas para o filho e todos os dados teriam sido copiados para as páginas do filho. Alocar nova memória e copiar dados é um processo muito caro (leva tempo do processador e outros recursos do sistema). Também sabemos que, na maioria dos casos, a criança chamará exec () e isso substituirá a memória da criança por um novo programa. Portanto, a primeira cópia que fizemos teria sido um desperdício se a cópia com escrita não estivesse lá.
pid = fork();if(pid ==0){
execlp("/bin/ls","ls",NULL);
printf("will this line be printed");// Think about it// A new memory space will be created for the child and that memory will contain the "/bin/ls" program(text section), it's stack, data section and heap sectionelse{
wait(NULL);// parent is waiting for the child. Once child terminates, parent will get its exit status and can then continue}return1;// Both child and parent will exit with status code 1.
Por que o pai espera por um processo filho?
O pai pode atribuir uma tarefa a seu filho e esperar até que ele conclua sua tarefa. Então, ele pode realizar algum outro trabalho.
Depois que o filho termina, todos os recursos associados ao filho são liberados, exceto o bloco de controle do processo. Agora, a criança está em estado de zumbi. Usando wait (), o pai pode perguntar sobre o status da criança e então pedir ao kernel para liberar o PCB. Caso o pai não use a espera, a criança permanecerá no estado zumbi.
Por que a chamada de sistema exec () é necessária?
Não é necessário usar exec () com fork (). Se o código que o filho irá executar estiver dentro do programa associado ao pai, exec () não é necessário.
Mas pense nos casos em que a criança precisa executar vários programas. Vejamos o exemplo do programa shell. Ele suporta vários comandos como find, mv, cp, date etc. Será correto incluir o código do programa associado a esses comandos em um programa ou fazer com que o filho carregue esses programas na memória quando necessário?
Tudo depende do seu caso de uso. Você tem um servidor web que fornece uma entrada x que retorna 2 ^ x aos clientes. Para cada solicitação, o servidor da web cria um novo filho e pede para ele computar. Você escreverá um programa separado para calcular isso e usar exec ()? Ou você apenas escreverá código de computação dentro do programa pai?
Normalmente, a criação de um processo envolve uma combinação de chamadas fork (), exec (), wait () e exit ().
As exec(3,3p)funções substituem o processo atual por outro. Ou seja, o processo atual para e outro é executado, assumindo alguns dos recursos que o programa original tinha.
Não exatamente. Ele substitui a imagem do processo atual por uma nova imagem do processo. O processo é o mesmo processo com o mesmo pid, o mesmo ambiente e a mesma tabela de descritor de arquivo. O que mudou foi toda a memória virtual e o estado da CPU.
JeremyP
@JeremyP "O mesmo descritor de arquivo" foi importante aqui, ele explica como o redirecionamento funciona em shells! Fiquei intrigado sobre como o redirecionamento pode funcionar se o exec sobrescrever tudo! Obrigado
Respostas:
De forma simplista, no UNIX, você tem o conceito de processos e programas. Um processo é um ambiente no qual um programa é executado.
A ideia simples por trás do "modelo de execução" do UNIX é que existem duas operações que você pode fazer.
O primeiro é para
fork()
, que cria um novo processo contendo uma duplicata (principalmente) do programa atual, incluindo seu estado. Existem algumas diferenças entre os dois processos que lhes permitem descobrir quem é o pai e quem é o filho.A segunda é para
exec()
, que substitui o programa no processo atual por um programa totalmente novo.A partir dessas duas operações simples, todo o modelo de execução do UNIX pode ser construído.
Para adicionar mais alguns detalhes ao acima:
O uso
fork()
eexec()
exemplifica o espírito do UNIX, pois fornece uma maneira muito simples de iniciar novos processos.A
fork()
chamada torna quase uma duplicação do processo atual, idêntico em quase todos os sentidos (nem tudo é copiado, por exemplo, limites de recursos em algumas implementações, mas a ideia é criar uma cópia o mais próxima possível). Apenas um processo chama,fork()
mas dois processos retornam dessa chamada - parece bizarro, mas é realmente muito eleganteO novo processo (chamado de filho) obtém um ID de processo (PID) diferente e tem o PID do processo antigo (o pai) como seu PID pai (PPID).
Como os dois processos agora estão executando exatamente o mesmo código, eles precisam ser capazes de dizer qual é qual - o código de retorno
fork()
fornece essa informação - o filho obtém 0, o pai obtém o PID do filho (se ofork()
falhar, não filho é criado e o pai recebe um código de erro).Dessa forma, o pai conhece o PID do filho e pode se comunicar com ele, matá-lo, esperá-lo e assim por diante (a criança sempre pode encontrar seu processo pai com uma chamada para
getppid()
).A
exec()
chamada substitui todo o conteúdo atual do processo por um novo programa. Ele carrega o programa no espaço do processo atual e o executa a partir do ponto de entrada.Portanto,
fork()
eexec()
são frequentemente usados em sequência para fazer um novo programa rodar como filho de um processo atual. Os shells normalmente fazem isso sempre que você tenta executar um programa comofind
- o shell bifurca, então o filho carrega ofind
programa na memória, configurando todos os argumentos da linha de comando, E / S padrão e assim por diante.Mas eles não precisam ser usados juntos. É perfeitamente aceitável que um programa chame
fork()
sem seguimentoexec()
se, por exemplo, o programa contiver código pai e filho (você precisa ter cuidado com o que faz, cada implementação pode ter restrições).Isso era muito usado (e ainda é) para daemons que simplesmente ouvem em uma porta TCP e bifurcam uma cópia de si mesmos para processar uma solicitação específica enquanto o pai volta a ouvir. Para essa situação, o programa contém o código pai e o filho.
Da mesma forma, os programas que sabem que foram concluídos e apenas desejam executar outro programa não precisam fazê-lo
fork()
,exec()
ewait()/waitpid()
para a criança. Eles podem simplesmente carregar o filho diretamente em seu espaço de processo atual comexec()
.Algumas implementações do UNIX têm um otimizado
fork()
que usa o que eles chamam de cópia na gravação. Este é um truque para atrasar a cópia do espaço do processofork()
até que o programa tente alterar algo nesse espaço. Isso é útil para aqueles programas que usam apenasfork()
e nãoexec()
porque não precisam copiar um espaço de processo inteiro. No Linux,fork()
apenas faz uma cópia das tabelas de páginas e uma nova estrutura de tarefas,exec()
fará o trabalho árduo de "separar" a memória dos dois processos.Se o
exec
for chamado em seguidafork
(e isso é o que acontece principalmente), isso causa uma gravação no espaço do processo e é então copiado para o processo filho, antes que as modificações sejam permitidas.O Linux também possui um
vfork()
, ainda mais otimizado, que compartilha quase tudo entre os dois processos. Por causa disso, existem certas restrições quanto ao que a criança pode fazer, e os pais param até que a criança chameexec()
ou_exit()
.O pai deve ser interrompido (e o filho não tem permissão para retornar da função atual) uma vez que os dois processos compartilham a mesma pilha. Isso é um pouco mais eficiente para o caso de uso clássico de
fork()
seguido imediatamente porexec()
.Note-se que há uma família inteira de
exec
chamadas (execl
,execle
,execve
e assim por diante), masexec
no contexto aqui significa qualquer um deles.O diagrama a seguir ilustra a
fork/exec
operação típica em que obash
shell é usado para listar um diretório com ols
comando:fonte
exec
o utilitário é usado para redirecionar o IO do processo atual? Como o caso "nulo", executando exec sem um argumento, foi usado para essa convenção?exec
no meio de substituir o programa atual (o shell) neste processo por outro, não especificar esse outro programa para substituí-lo pode simplesmente significar que você não deseja substituí-lo.exec
ser chamado sem um programa. Mas é um pouco estranho neste cenário, já que a utilidade original de redirecionar para um novo programa - um programa que seria realmenteexec
executado - desaparece e você tem um artefato útil, redirecionando o programa atual - que não está sendoexec
usado ou iniciado de qualquer forma - em vez disso.As funções na família exec () têm comportamentos diferentes:
Você pode misturá-los, portanto, você tem:
Para todos eles, o argumento inicial é o nome de um arquivo a ser executado.
Para obter mais informações, leia a página de manual exec (3) :
fonte
execve()
em sua lista, que é definida por POSIX, e adicionouexecvpe()
, que não é definida por POSIX (principalmente por razões de precedente histórico; ele completa o conjunto de funções). Caso contrário, uma explicação útil da convenção de nomenclatura para a família - um complemento útil para paxdiablo 'uma resposta que explica mais sobre o funcionamento das funções.execvpe()
(et al) não listaexecve()
; ele tem sua própria página de manual separada (pelo menos no Ubuntu 16.04 LTS) - a diferença é que as outrasexec()
funções da família estão listadas na seção 3 (funções), enquanto queexecve()
estão listadas na seção 2 (chamadas de sistema). Basicamente, todas as outras funções da família são implementadas em termos de uma chamada paraexecve()
.A
exec
família de funções faz com que seu processo execute um programa diferente, substituindo o antigo programa que estava rodando. Ou seja, se você ligarentão o
ls
programa é executado com o id do processo, diretório de trabalho atual e usuário / grupo (direitos de acesso) do processo que chamouexecl
. Depois disso, o programa original não está mais funcionando.Para iniciar um novo processo, a
fork
chamada do sistema é usada. Para executar um programa sem substituir o original, você precisafork
, entãoexec
.fonte
A
exec
família função é todas as funções usadas para executar um arquivo, comoexecl
,execlp
,execle
,execv
, eexecvp
.Eles são todos os frontends paraexecve
e fornecer diferentes métodos de chamá-lo.As funções Exec são usadas quando você deseja executar (lançar) um arquivo (programa).
Eles funcionam substituindo a imagem do processo atual por aquela que você iniciou. Eles substituem (encerrando) o processo atualmente em execução (aquele que chamou o comando exec) pelo novo processo que foi iniciado.
Para mais detalhes: veja este link .
fonte
exec
é frequentemente usado em conjunto comfork
, pelo qual vi que você também perguntou, então discutirei isso com isso em mente.exec
transforma o processo atual em outro programa. Se você já assistiu Doctor Who, então é como quando ele se regenera - seu antigo corpo é substituído por um novo corpo.A maneira como isso acontece com o seu programa
exec
é que muitos dos recursos que o kernel do sistema operacional verifica para ver se o arquivo que você está passandoexec
como o argumento do programa (primeiro argumento) é executável pelo usuário atual (id do usuário do processo fazer aexec
chamada) e, em caso afirmativo, ele substitui o mapeamento de memória virtual do processo atual por uma memória virtual do novo processo e copia os dadosargv
eenvp
que foram passados naexec
chamada para uma área desse novo mapa de memória virtual. Várias outras coisas também podem acontecer aqui, mas os arquivos que foram abertos para o programa que chamouexec
ainda estarão abertos para o novo programa e eles compartilharão o mesmo ID de processo, mas o programa que chamouexec
irá parar (a menos que exec falhou).O motivo pelo qual isso é feito dessa maneira é que, separando a execução de um novo programa em duas etapas como essa, você pode fazer algumas coisas entre as duas etapas. A coisa mais comum a fazer é certificar-se de que o novo programa possui determinados arquivos abertos como determinados descritores de arquivo. (lembre-se de que os descritores de arquivo não são iguais
FILE *
, mas sãoint
valores que o kernel conhece). Fazendo isso, você pode:Isso realiza a execução de:
a partir do shell de comando.
fonte
Quando um processo usa fork (), ele cria uma cópia duplicada de si mesmo e essa duplicata se torna filha do processo. O fork () é implementado usando uma chamada de sistema clone () no linux que retorna duas vezes do kernel.
Vamos entender isso com um exemplo:
No exemplo, assumimos que exec () não é usado dentro do processo filho.
Mas um pai e um filho diferem em alguns dos atributos do PCB (bloco de controle do processo). Esses são:
Mas e quanto à memória infantil? Um novo espaço de endereço foi criado para uma criança?
As respostas em não. Após o fork (), pai e filho compartilham o espaço de endereço de memória do pai. No Linux, esse espaço de endereço é dividido em várias páginas. Somente quando a criança escreve em uma das páginas de memória dos pais, uma duplicata dessa página é criada para a criança. Isso também é conhecido como cópia na gravação (Copiar páginas pai apenas quando o filho gravar nelas).
Vamos entender o copy on write com um exemplo.
Mas por que a cópia na gravação é necessária?
A criação de um processo típico ocorre por meio da combinação fork () - exec (). Vamos primeiro entender o que exec () faz.
O grupo de funções Exec () substitui o espaço de endereço da criança por um novo programa. Depois que exec () é chamado dentro de um filho, um espaço de endereço separado será criado para o filho, que é totalmente diferente do pai.
Se não houvesse cópia no mecanismo de gravação associado a fork (), páginas duplicadas teriam sido criadas para o filho e todos os dados teriam sido copiados para as páginas do filho. Alocar nova memória e copiar dados é um processo muito caro (leva tempo do processador e outros recursos do sistema). Também sabemos que, na maioria dos casos, a criança chamará exec () e isso substituirá a memória da criança por um novo programa. Portanto, a primeira cópia que fizemos teria sido um desperdício se a cópia com escrita não estivesse lá.
Por que o pai espera por um processo filho?
Por que a chamada de sistema exec () é necessária?
Não é necessário usar exec () com fork (). Se o código que o filho irá executar estiver dentro do programa associado ao pai, exec () não é necessário.
Mas pense nos casos em que a criança precisa executar vários programas. Vejamos o exemplo do programa shell. Ele suporta vários comandos como find, mv, cp, date etc. Será correto incluir o código do programa associado a esses comandos em um programa ou fazer com que o filho carregue esses programas na memória quando necessário?
Tudo depende do seu caso de uso. Você tem um servidor web que fornece uma entrada x que retorna 2 ^ x aos clientes. Para cada solicitação, o servidor da web cria um novo filho e pede para ele computar. Você escreverá um programa separado para calcular isso e usar exec ()? Ou você apenas escreverá código de computação dentro do programa pai?
Normalmente, a criação de um processo envolve uma combinação de chamadas fork (), exec (), wait () e exit ().
fonte
As
exec(3,3p)
funções substituem o processo atual por outro. Ou seja, o processo atual para e outro é executado, assumindo alguns dos recursos que o programa original tinha.fonte