@ Justin, porque queremos que o SO se torne o lugar certo para perguntas sobre programação.
precisa saber é
4
@ Polaris878: ah, agora! : D
Janusz Lenar
então forké basicamente a clonagem: O
Sebastian Hojas
Respostas:
364
O uso forke execexemplifica o espírito do UNIX, pois fornece uma maneira muito simples de iniciar novos processos.
A forkchamada basicamente cria uma duplicata do processo atual, idêntica em quase todos os aspectos. Nem tudo é copiado (por exemplo, limites de recursos em algumas implementações), mas a idéia é criar uma cópia o mais próxima possível.
O novo processo (filho) obtém um ID de processo diferente (PID) e possui o PID do processo antigo (pai) como seu PID pai (PPID). Como os dois processos agora estão executando exatamente o mesmo código, eles podem dizer qual é o código de retorno de fork- o filho recebe 0, o pai recebe o PID do filho. Isso é tudo, é claro, supondo que a forkchamada funcione - caso contrário, nenhum filho será criado e o pai receberá um código de erro.
A execchamada é uma maneira de substituir basicamente todo o processo atual por um novo programa. Ele carrega o programa no espaço de processo atual e o executa a partir do ponto de entrada.
Portanto, forke execgeralmente são usados em sequência para executar um novo programa 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 um programa para forksi mesmo, sem execse, 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 foi usado bastante (e ainda é) para os daemons que simplesmente escutam uma porta TCP e forkuma cópia de si mesmos para processar uma solicitação específica enquanto o pai volta a escutar.
Da mesma forma, os programas que sabem que estão concluídos e que desejam executar outro programa não precisam fork , em execseguida, waitpara a criança. Eles podem simplesmente carregar a criança diretamente em seu espaço de processo.
Algumas implementações do UNIX têm um otimizado forkque usa o que chamam de cópia na gravação. Este é um truque para atrasar a cópia do espaço do processo forkaté que o programa tente alterar algo nesse espaço. Isso é útil para os programas que usam apenasfork e não execporque não precisam copiar um espaço de processo inteiro.
Se o execé chamado a seguir fork(e é o que acontece principalmente), isso causa uma gravação no espaço do processo e é copiada para o processo filho.
Observe que há uma família inteira de execchamadas ( execl, execle,execve e 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
fork()divide o processo atual em dois processos. Ou, em outras palavras, seu programa linear fácil de pensar se transforma em dois programas separados, executando um único código:
int pid = fork();if(pid ==0){
printf("I'm the child");}else{
printf("I'm the parent, my child is %i", pid);// here we can kill the child, but that's not very parently of us}
Isso pode meio que explodir sua mente. Agora você tem um pedaço de código com um estado praticamente idêntico sendo executado por dois processos. O processo filho herda todo o código e a memória do processo que o criou, incluindo o início de onde a fork()chamada parou. A única diferença é afork() código de retorno para informar se você é o pai ou a criança. Se você é o pai, o valor de retorno é o ID do filho.
execé um pouco mais fácil de entender, basta dizer execpara executar um processo usando o executável de destino e você não tem dois processos executando o mesmo código ou herdando o mesmo estado. Como @Steve Hawkins diz, execpode ser usado depois de você forkexecutar no processo atual o executável de destino.
há também a condição quando pid < 0ea fork()chamada falhou
Jonathan Fingland
3
Isso não me surpreende :-) Um pedaço de código sendo executado por dois processos acontece toda vez que uma biblioteca compartilhada ou DLL está sendo usada.
paxdiablo
31
Eu acho que alguns conceitos de "Advanced Unix Programming" de Marc Rochkind foram úteis para entender os diferentes papéis de fork()/ exec(), especialmente para alguém acostumado ao CreateProcess()modelo do Windows :
Um programa é uma coleção de instruções e dados que são mantidos em um arquivo regular no disco. (da 1.1.2 Programas, processos e threads)
.
Para executar um programa, primeiro solicita-se ao kernel que crie um novo processo , que é um ambiente no qual um programa é executado. (também da 1.1.2 Programas, processos e threads)
.
É impossível entender as chamadas do sistema exec ou fork sem entender completamente a distinção entre um processo e um programa. Se esses termos forem novos para você, convém voltar e revisar a Seção 1.1.2. Se você estiver pronto para prosseguir agora, resumiremos a distinção em uma frase: Um processo é um ambiente de execução que consiste em segmentos de instrução, dados do usuário e dados do sistema, além de muitos outros recursos adquiridos em tempo de execução , enquanto um programa é um arquivo que contém instruções e dados usados para inicializar a instrução e os segmentos de dados do usuário de um processo. (de 5,3exec Chamadas do sistema)
Depois de entender a distinção entre um programa e um processo, o comportamento de fork()eexec() função podem ser resumidos como:
fork() cria uma duplicata do processo atual
exec() substitui o programa no processo atual por outro programa
Fork cria uma cópia de um processo de chamada. geralmente segue a estrutura
int cpid = fork();if(cpid ==0){//child code
exit(0);}//parent code
wait(cpid);// end
(para texto do processo filho (código), dados, a pilha é igual ao processo de chamada) o processo filho executa o código no bloco if.
O EXEC substitui o processo atual pelo código, dados e pilha do novo processo. geralmente segue a estrutura
int cpid = fork();if(cpid ==0){//child code
exec(foo);
exit(0);}//parent code
wait(cpid);// end
(após a chamada exec, o unix kernel limpa o texto do processo filho, dados, pilha e preenchimento com texto / dados relacionados ao processo foo), portanto, o processo filho fica com um código diferente (o código foo {não é o mesmo do pai})
É um pouco não relacionado à pergunta, mas esse código acima não causa uma condição de corrida se o processo filho terminar seu código primeiro? Nesse caso, o processo pai ficaria para sempre esperando que a criança se encerrasse, certo?
stdout
7
Eles são usados juntos para criar um novo processo filho. Primeiro, a chamada forkcria uma cópia do processo atual (o processo filho). Em seguida, execé chamado de dentro do processo filho para "substituir" a cópia do processo pai pelo novo processo.
O processo é mais ou menos assim:
child = fork();//Fork returns a PID for the parent process, or 0 for the child, or -1 for Failif(child <0){
std::cout <<"Failed to fork GUI process...Exiting"<< std::endl;
exit (-1);}elseif(child ==0){// This is the Child Process// Call one of the "exec" functions to create the child process
execvp (argv[0],const_cast<char**>(argv));}else{// This is the Parent Process//Continue executing parent process}
Na 7ª linha, é mencionado que a função exec () cria o processo filho. Realmente é porque fork () já criou o processo filho e a chamada exec () substitui o programa do novo processo recém-criado
cbinder
4
fork () cria uma cópia do processo atual, com a execução no novo filho, iniciando logo após a chamada fork (). Após o fork (), eles são idênticos, exceto pelo valor de retorno da função fork (). (RTFM para mais detalhes.) Os dois processos podem divergir ainda mais, com um incapaz de interferir no outro, exceto possivelmente por meio de qualquer identificador de arquivo compartilhado.
exec () substitui o processo atual por um novo. Não tem nada a ver com fork (), exceto que um exec () geralmente segue fork () quando o que se quer é iniciar um processo filho diferente, em vez de substituir o atual.
A fork()chamada do sistema cria um clone do programa em execução no momento. O programa original continua a execução com a próxima linha de código após a chamada da função fork (). O clone também inicia a execução na próxima linha de código. Veja o código a seguir obtido em http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/
#include<stdio.h>#include<unistd.h>int main(int argc,char**argv){
printf("--beginning of program\n");int counter =0;pid_t pid = fork();if(pid ==0){// child processint i =0;for(; i <5;++i){
printf("child process: counter=%d\n",++counter);}}elseif(pid >0){// parent processint j =0;for(; j <5;++j){
printf("parent process: counter=%d\n",++counter);}}else{// fork failed
printf("fork() failed!\n");return1;}
printf("--end of program--\n");return0;}
Este programa declara uma variável de contador, definida como zero, antes de fork()ing. Após a chamada da bifurcação, temos dois processos em execução em paralelo, ambos incrementando sua própria versão do contador. Cada processo será executado para conclusão e saída. Como os processos são executados em paralelo, não temos como saber qual será o primeiro a terminar. A execução deste programa imprimirá algo semelhante ao mostrado abaixo, embora os resultados possam variar de uma execução para a seguinte.
--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3--end of program--
child process: counter=4
child process: counter=5--end of program--
A exec()família de chamadas do sistema substitui o código atualmente em execução de um processo por outro pedaço de código. O processo mantém seu PID, mas se torna um novo programa. Por exemplo, considere o seguinte código:
#include<stdio.h>#include<unistd.h>
main(){char program[80],*args[3];int i;
printf("Ready to exec()...\n");
strcpy(program,"date");
args[0]="date";
args[1]="-u";
args[2]=NULL;
i=execvp(program,args);
printf("i=%d ... did it work?\n",i);}
Este programa chama a execvp()função para substituir seu código pelo programa de data. Se o código estiver armazenado em um arquivo chamado exec1.c, sua execução produzirá a seguinte saída:
Ready to exec()...TueJul1520:17:53 UTC 2008
O programa gera a linha “Pronto para exec (). . . E depois de chamar a função execvp (), substitui seu código pelo programa date. Observe que a linha -. . . funcionou ”não é exibido, porque nesse momento o código foi substituído. Em vez disso, vemos a saída da execução de "data -u".
Ele cria uma cópia do processo em execução. O processo em execução é chamado de processo pai e o processo recém-criado é chamado de processo filho . A maneira de diferenciar os dois é observando o valor retornado:
fork() retorna o identificador do processo (pid) do processo filho no pai
fork() retorna 0 no filho.
exec():
Inicia um novo processo dentro de um processo. Carrega um novo programa no processo atual, substituindo o existente.
fork()+ exec():
Ao iniciar um novo programa é primeiro fork()criar um novo processo e, em seguida exec()(carregar na memória e executar) o programa binário que ele deve executar.
int main(void){int pid = fork();if( pid ==0){
execvp("find", argv );}//Put the parent to sleep for 2 sec,let the child finished executing
wait(2);return0;}
O principal exemplo para entender o conceito fork()e exec()é o shell , o programa interpretador de comandos que os usuários normalmente executam após o logon no sistema.O shell interpreta a primeira palavra da linha de comando como um nome de comando
Para muitos comandos, os garfos do shell e o processo filho executam o comando associado ao nome, tratando as palavras restantes na linha de comando como parâmetros para o comando.
O shell permite três tipos de comandos. Primeiro, um comando pode ser um
arquivo executável que contém o código de objeto produzido pela compilação do código-fonte (um programa C, por exemplo). Segundo, um comando pode ser um arquivo executável que contém uma sequência de linhas de comando do shell. Finalmente, um comando pode ser um comando interno do shell (em vez de um arquivo executável ex-> cd , ls etc.)
fork
é basicamente a clonagem: ORespostas:
O uso
fork
eexec
exemplifica o espírito do UNIX, pois fornece uma maneira muito simples de iniciar novos processos.A
fork
chamada basicamente cria uma duplicata do processo atual, idêntica em quase todos os aspectos. Nem tudo é copiado (por exemplo, limites de recursos em algumas implementações), mas a idéia é criar uma cópia o mais próxima possível.O novo processo (filho) obtém um ID de processo diferente (PID) e possui o PID do processo antigo (pai) como seu PID pai (PPID). Como os dois processos agora estão executando exatamente o mesmo código, eles podem dizer qual é o código de retorno de
fork
- o filho recebe 0, o pai recebe o PID do filho. Isso é tudo, é claro, supondo que afork
chamada funcione - caso contrário, nenhum filho será criado e o pai receberá um código de erro.A
exec
chamada é uma maneira de substituir basicamente todo o processo atual por um novo programa. Ele carrega o programa no espaço de processo atual e o executa a partir do ponto de entrada.Portanto,
fork
eexec
geralmente são usados em sequência para executar um novo programa 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 um programa para
fork
si mesmo, semexec
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 foi usado bastante (e ainda é) para os daemons que simplesmente escutam uma porta TCP efork
uma cópia de si mesmos para processar uma solicitação específica enquanto o pai volta a escutar.Da mesma forma, os programas que sabem que estão concluídos e que desejam executar outro programa não precisam
fork
, emexec
seguida,wait
para a criança. Eles podem simplesmente carregar a criança diretamente em seu espaço de processo.Algumas implementações do UNIX têm um otimizado
fork
que usa o que 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 os programas que usam apenasfork
e nãoexec
porque não precisam copiar um espaço de processo inteiro.Se o
exec
é chamado a seguirfork
(e é o que acontece principalmente), isso causa uma gravação no espaço do processo e é copiada para o processo filho.Observe 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
fork()
divide o processo atual em dois processos. Ou, em outras palavras, seu programa linear fácil de pensar se transforma em dois programas separados, executando um único código:Isso pode meio que explodir sua mente. Agora você tem um pedaço de código com um estado praticamente idêntico sendo executado por dois processos. O processo filho herda todo o código e a memória do processo que o criou, incluindo o início de onde a
fork()
chamada parou. A única diferença é afork()
código de retorno para informar se você é o pai ou a criança. Se você é o pai, o valor de retorno é o ID do filho.exec
é um pouco mais fácil de entender, basta dizerexec
para executar um processo usando o executável de destino e você não tem dois processos executando o mesmo código ou herdando o mesmo estado. Como @Steve Hawkins diz,exec
pode ser usado depois de vocêfork
executar no processo atual o executável de destino.fonte
pid < 0
eafork()
chamada falhouEu acho que alguns conceitos de "Advanced Unix Programming" de Marc Rochkind foram úteis para entender os diferentes papéis de
fork()
/exec()
, especialmente para alguém acostumado aoCreateProcess()
modelo do Windows :.
.
Depois de entender a distinção entre um programa e um processo, o comportamento de
fork()
eexec()
função podem ser resumidos como:fork()
cria uma duplicata do processo atualexec()
substitui o programa no processo atual por outro programa(esta é essencialmente uma versão simplificada 'for dummies' da resposta muito mais detalhada de paxdiablo )
fonte
Fork cria uma cópia de um processo de chamada. geralmente segue a estrutura
(para texto do processo filho (código), dados, a pilha é igual ao processo de chamada) o processo filho executa o código no bloco if.
O EXEC substitui o processo atual pelo código, dados e pilha do novo processo. geralmente segue a estrutura
(após a chamada exec, o unix kernel limpa o texto do processo filho, dados, pilha e preenchimento com texto / dados relacionados ao processo foo), portanto, o processo filho fica com um código diferente (o código foo {não é o mesmo do pai})
fonte
Eles são usados juntos para criar um novo processo filho. Primeiro, a chamada
fork
cria uma cópia do processo atual (o processo filho). Em seguida,exec
é chamado de dentro do processo filho para "substituir" a cópia do processo pai pelo novo processo.O processo é mais ou menos assim:
fonte
fork () cria uma cópia do processo atual, com a execução no novo filho, iniciando logo após a chamada fork (). Após o fork (), eles são idênticos, exceto pelo valor de retorno da função fork (). (RTFM para mais detalhes.) Os dois processos podem divergir ainda mais, com um incapaz de interferir no outro, exceto possivelmente por meio de qualquer identificador de arquivo compartilhado.
exec () substitui o processo atual por um novo. Não tem nada a ver com fork (), exceto que um exec () geralmente segue fork () quando o que se quer é iniciar um processo filho diferente, em vez de substituir o atual.
fonte
A principal diferença entre
fork()
eexec()
é que,A
fork()
chamada do sistema cria um clone do programa em execução no momento. O programa original continua a execução com a próxima linha de código após a chamada da função fork (). O clone também inicia a execução na próxima linha de código. Veja o código a seguir obtido em http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/Este programa declara uma variável de contador, definida como zero, antes de
fork()
ing. Após a chamada da bifurcação, temos dois processos em execução em paralelo, ambos incrementando sua própria versão do contador. Cada processo será executado para conclusão e saída. Como os processos são executados em paralelo, não temos como saber qual será o primeiro a terminar. A execução deste programa imprimirá algo semelhante ao mostrado abaixo, embora os resultados possam variar de uma execução para a seguinte.A
exec()
família de chamadas do sistema substitui o código atualmente em execução de um processo por outro pedaço de código. O processo mantém seu PID, mas se torna um novo programa. Por exemplo, considere o seguinte código:Este programa chama a
execvp()
função para substituir seu código pelo programa de data. Se o código estiver armazenado em um arquivo chamado exec1.c, sua execução produzirá a seguinte saída:O programa gera a linha “Pronto para exec (). . . E depois de chamar a função execvp (), substitui seu código pelo programa date. Observe que a linha -. . . funcionou ”não é exibido, porque nesse momento o código foi substituído. Em vez disso, vemos a saída da execução de "data -u".
fonte
fork()
:Ele cria uma cópia do processo em execução. O processo em execução é chamado de processo pai e o processo recém-criado é chamado de processo filho . A maneira de diferenciar os dois é observando o valor retornado:
fork()
retorna o identificador do processo (pid) do processo filho no paifork()
retorna 0 no filho.exec()
:Inicia um novo processo dentro de um processo. Carrega um novo programa no processo atual, substituindo o existente.
fork()
+exec()
:Ao iniciar um novo programa é primeiro
fork()
criar um novo processo e, em seguidaexec()
(carregar na memória e executar) o programa binário que ele deve executar.fonte
O principal exemplo para entender o conceito
fork()
eexec()
é o shell , o programa interpretador de comandos que os usuários normalmente executam após o logon no sistema.O shell interpreta a primeira palavra da linha de comando como um nome de comandoPara muitos comandos, os garfos do shell e o processo filho executam o comando associado ao nome, tratando as palavras restantes na linha de comando como parâmetros para o comando.
O shell permite três tipos de comandos. Primeiro, um comando pode ser um arquivo executável que contém o código de objeto produzido pela compilação do código-fonte (um programa C, por exemplo). Segundo, um comando pode ser um arquivo executável que contém uma sequência de linhas de comando do shell. Finalmente, um comando pode ser um comando interno do shell (em vez de um arquivo executável ex-> cd , ls etc.)
fonte