Em muitos programas e páginas de manual do Linux, vi código usando fork()
. Por que precisamos usar fork()
e qual é o seu propósito?
Respostas:
fork()
é como você cria novos processos no Unix. Ao ligar fork
, você está criando uma cópia de seu próprio processo que possui seu próprio espaço de endereço . Isso permite que várias tarefas sejam executadas independentemente umas das outras, como se cada uma tivesse toda a memória da máquina para si.
Aqui estão alguns exemplos de uso de fork
:
fork
para executar os programas que você invoca a partir da linha de comando.fork
para criar vários processos de servidor, cada um dos quais lida com solicitações em seu próprio espaço de endereço. Se um morre ou perde memória, os outros não são afetados, por isso funciona como um mecanismo de tolerância a falhas.fork
para lidar com cada página em um processo separado. Isso impedirá que o código do lado do cliente em uma página desative todo o navegador.fork
é usado para gerar processos em alguns programas paralelos (como aqueles escritos usando MPI ). Observe que isso é diferente de usar threads , que não têm seu próprio espaço de endereço e existem dentro de um processo.fork
indiretamente para iniciar processos filho. Por exemplo, toda vez que você usa um comando como subprocess.Popen
no Python, você fork
processa um filho e lê sua saída. Isso permite que os programas funcionem juntos.O uso típico de fork
em um shell pode ter a seguinte aparência:
int child_process_id = fork();
if (child_process_id) {
// Fork returns a valid pid in the parent process. Parent executes this.
// wait for the child process to complete
waitpid(child_process_id, ...); // omitted extra args for brevity
// child process finished!
} else {
// Fork returns 0 in the child process. Child executes this.
// new argv array for the child process
const char *argv[] = {"arg1", "arg2", "arg3", NULL};
// now start executing some other program
exec("/path/to/a/program", argv);
}
O shell gera um processo filho usando exec
e aguarda sua conclusão, então continua com sua própria execução. Observe que você não precisa usar o fork desta forma. Você sempre pode gerar muitos processos filho, como um programa paralelo pode fazer, e cada um pode executar um programa simultaneamente. Basicamente, sempre que você está criando novos processos em um sistema Unix, você está usando fork()
. Para obter o equivalente do Windows, dê uma olhada em CreateProcess
.
Se você quiser mais exemplos e uma explicação mais longa, a Wikipedia tem um resumo decente. E aqui estão alguns slides sobre como processos, threads e simultaneidade funcionam em sistemas operacionais modernos.
fork()
é o caminho para criar um novo processo no UNIX, mas para ser pedante, há pelo menos um outro: posix_spawn()
.
fork () é como o Unix cria novos processos. No ponto em que você chamou fork (), seu processo é clonado e dois processos diferentes continuam a execução a partir daí. Um deles, o filho, fará com que fork () retorne 0. O outro, o pai, fará com que fork () retorne o PID (ID do processo) do filho.
Por exemplo, se você digitar o seguinte em um shell, o programa shell chamará fork () e, em seguida, executará o comando que você passou (telnetd, neste caso) no filho, enquanto o pai exibirá o prompt novamente também como uma mensagem indicando o PID do processo em segundo plano.
$ telnetd &
Quanto ao motivo pelo qual você cria novos processos, é assim que seu sistema operacional pode fazer muitas coisas ao mesmo tempo. É por isso que você pode executar um programa e, enquanto ele está em execução, alternar para outra janela e fazer outra coisa.
fork () é usado para criar o processo filho. Quando uma função fork () é chamada, um novo processo é gerado e a chamada da função fork () retorna um valor diferente para o filho e o pai.
Se o valor de retorno for 0, você sabe que é o processo filho e se o valor de retorno for um número (que é o id do processo filho), você sabe que é o pai. (e se for um número negativo, a bifurcação falhou e nenhum processo filho foi criado)
fork () é basicamente usado para criar um processo filho para o processo no qual você está chamando esta função. Sempre que você chama um fork (), ele retorna um zero para o id filho.
pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process
com isso, você pode fornecer ações diferentes para o pai e a criança e fazer uso do recurso multithreading.
fork () criará um novo processo filho idêntico ao pai. Portanto, tudo o que você executar no código depois disso será executado por ambos os processos - muito útil se você tiver, por exemplo, um servidor e quiser lidar com várias solicitações.
Você provavelmente não precisa usar fork na programação do dia-a-dia se estiver escrevendo aplicativos.
Mesmo se você quiser que seu programa inicie outro programa para fazer alguma tarefa, existem outras interfaces mais simples que usam fork nos bastidores, como "sistema" em C e perl.
Por exemplo, se você quiser que seu aplicativo inicie outro programa, como o bc, para fazer alguns cálculos para você, pode usar 'sistema' para executá-lo. O sistema faz uma 'bifurcação' para criar um novo processo e, em seguida, um 'exec' para transformar esse processo em bc. Assim que o bc for concluído, o sistema retornará o controle ao programa.
Você também pode executar outros programas de forma assíncrona, mas não me lembro como.
Se você estiver escrevendo servidores, shells, vírus ou sistemas operacionais, é mais provável que queira usar o fork.
system()
. Eu estava lendo sobre fork()
porque quero que meu código C execute um script Python.
A chamada do sistema fork () é usada para criar processos. Não leva argumentos e retorna um ID de processo. O objetivo de fork () é criar um novo processo, que se torna o processo filho do chamador. Depois que um novo processo filho é criado, ambos os processos executarão a próxima instrução após a chamada de sistema fork (). Portanto, temos que distinguir o pai do filho. Isso pode ser feito testando o valor retornado de fork ():
Se fork () retornar um valor negativo, a criação de um processo filho não foi bem-sucedida. fork () retorna um zero para o processo filho recém-criado. fork () retorna um valor positivo, o ID do processo filho, para o pai. O ID do processo retornado é do tipo pid_t definido em sys / types.h. Normalmente, o ID do processo é um número inteiro. Além disso, um processo pode usar a função getpid () para recuperar o ID do processo atribuído a este processo. Portanto, após a chamada do sistema para fork (), um teste simples pode dizer qual processo é filho. Observe que o Unix fará uma cópia exata do espaço de endereço do pai e a dará ao filho. Portanto, os processos pai e filho têm espaços de endereço separados.
Vamos entendê-lo com um exemplo para esclarecer os pontos acima. Este exemplo não distingue os processos pai e filho.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_COUNT 200
#define BUF_SIZE 100
void main(void)
{
pid_t pid;
int i;
char buf[BUF_SIZE];
fork();
pid = getpid();
for (i = 1; i <= MAX_COUNT; i++) {
sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
write(1, buf, strlen(buf));
}
}
Suponha que o programa acima seja executado até o ponto da chamada para fork ().
Se a chamada para fork () for executada com sucesso, o Unix fará duas cópias idênticas dos espaços de endereço, uma para o pai e outra para o filho. Ambos os processos iniciarão sua execução na próxima instrução após a chamada fork (). Neste caso, ambos os processos iniciarão sua execução na atribuição
pid = .....;
Ambos os processos iniciam sua execução logo após a chamada do sistema fork (). Como os dois processos têm espaços de endereço idênticos, mas separados, essas variáveis inicializadas antes da chamada fork () têm os mesmos valores em ambos os espaços de endereço. Como cada processo tem seu próprio espaço de endereço, quaisquer modificações serão independentes das outras. Em outras palavras, se o pai alterar o valor de sua variável, a modificação afetará apenas a variável no espaço de endereço do processo pai. Outros espaços de endereço criados por chamadas fork () não serão afetados, embora tenham nomes de variáveis idênticos.
Qual é a razão de usar write em vez de printf? É porque printf () é "armazenado em buffer", o que significa que printf () agrupará a saída de um processo. Ao armazenar em buffer a saída do processo pai, o filho também pode usar printf para imprimir algumas informações, que também serão armazenadas em buffer. Como resultado, como a saída não será enviada para a tela imediatamente, você pode não obter a ordem correta do resultado esperado. Pior, a saída dos dois processos pode ser misturada de maneiras estranhas. Para superar esse problema, você pode considerar o uso de gravação "sem buffer".
Se você executar este programa, poderá ver o seguinte na tela:
................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
................
O ID de processo 3456 pode ser atribuído ao pai ou à criança. Devido ao fato de que esses processos são executados simultaneamente, suas linhas de saída são misturadas de uma forma bastante imprevisível. Além disso, a ordem dessas linhas é determinada pelo escalonador da CPU. Portanto, se você executar este programa novamente, poderá obter um resultado totalmente diferente.
O multiprocessamento é fundamental para a computação. Por exemplo, seu IE ou Firefox pode criar um processo para baixar um arquivo para você enquanto você ainda está navegando na Internet. Ou, enquanto estiver imprimindo um documento em um processador de texto, você ainda pode ver páginas diferentes e ainda fazer algumas edições com elas.
Fork () é usado para criar novos processos à medida que cada corpo escreve.
Aqui está o meu código que cria processos na forma de árvore binária ... Ele pedirá para verificar o número de níveis até os quais você deseja criar processos na árvore binária
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int t1,t2,p,i,n,ab;
p=getpid();
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)
{
t1=fork();
if(t1!=0)
t2=fork();
if(t1!=0 && t2!=0)
break;
printf("child pid %d parent pid %d\n",getpid(),getppid());fflush(stdout);
}
waitpid(t1,&ab,0);
waitpid(t2,&ab,0);
return 0;
}
RESULTADO
enter the number of levels
3
root 20665
child pid 20670 parent pid 20665
child pid 20669 parent pid 20665
child pid 20672 parent pid 20670
child pid 20671 parent pid 20670
child pid 20674 parent pid 20669
child pid 20673 parent pid 20669
Primeiro, é preciso entender o que é chamada de sistema fork (). Deixe-me explicar
A chamada do sistema fork () cria a duplicata exata do processo pai, faz a duplicata da pilha pai, heap, dados inicializados, dados não inicializados e compartilha o código em modo somente leitura com o processo pai.
A chamada do sistema fork copia a memória na base de cópia na gravação, significa que a criança faz na página de memória virtual quando há necessidade de cópia.
Objetivo do fork ():
fork()
é usado para gerar um processo filho. Normalmente é usado em situações semelhantes como threading, mas há diferenças. Ao contrário dos threads, fork()
cria processos separados inteiros, o que significa que o filho e o pai, embora sejam cópias diretas um do outro no ponto em que fork()
é chamado, eles são completamente separados, nenhum pode acessar o espaço de memória do outro (sem ir para os problemas normais você vai acessar a memória de outro programa).
fork()
ainda é usado por alguns aplicativos de servidor, principalmente aqueles executados como root em uma máquina * NIX que elimina as permissões antes de processar as solicitações do usuário. Ainda existem alguns outros casos de uso, mas a maioria das pessoas mudou para multithreading agora.
A razão por trás de fork () versus apenas ter uma função exec () para iniciar um novo processo é explicada em uma resposta a uma pergunta semelhante na troca de pilha do Unix .
Essencialmente, como o fork copia o processo atual, todas as várias opções possíveis para um processo são estabelecidas por padrão, de forma que o programador não as tenha fornecido.
No sistema operacional Windows, por outro lado, os programadores precisam usar a função CreateProcess, que é MUITO mais complicada e requer o preenchimento de uma estrutura multifacetada para definir os parâmetros do novo processo.
Portanto, para resumir, a razão para a bifurcação (versus execução) é a simplicidade na criação de novos processos.
Utilização de chamada de sistema Fork () para criar um processo filho. É uma duplicata exata do processo pai. Fork copia seção de pilha, seção de heap, seção de dados, variável de ambiente, argumentos de linha de comando do pai.
A função fork () é usada para criar um novo processo, duplicando o processo existente a partir do qual é chamado. O processo existente a partir do qual essa função é chamada se torna o processo pai e o processo recém-criado se torna o processo filho. Como já foi dito, o filho é uma cópia duplicada do pai, mas há algumas exceções.
A criança tem um PID exclusivo como qualquer outro processo em execução no sistema operacional.
O filho possui um ID de processo pai que é igual ao PID do
processo que o criou.
A utilização de recursos e os contadores de tempo da CPU são redefinidos para zero no processo filho.
O conjunto de sinais pendentes na criança está vazio.
O filho não herda nenhum cronômetro de seu pai
Exemplo:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void)
{
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) // fork was successful
{
if(childPID == 0) // child process
{
var_lcl++;
var_glb++;
printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
else //Parent process
{
var_lcl = 10;
var_glb = 20;
printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
}
else // fork failed
{
printf("\n Fork failed, quitting!!!!!!\n");
return 1;
}
return 0;
}
Agora, quando o código acima é compilado e executado:
$ ./fork
Parent process :: var_lcl = [10], var_glb[20]
Child Process :: var_lcl = [1], var_glb[1]