domingo, 24 de agosto de 2014

Fork em C

A função fork , da lib unistd.h , é responsavel por criar novos processos, se a operação de criar um novo processo for executada com sucesso, ele retorna dois valores , o PID do novo processo criado isso quando ainda estamos no processo principal, e zero , o zero significa que o processo filho (child) foi criado e é atraves dele que vamos executar funções em outro PID , caso -1 seja retornado significa que a criação de um processo filho (child) deu errado.

Para armazenamos os PID's dos processos criados iremos utilizar o tipo de data pid_t , essa estrutura de dado armazena o PID dos novos processos criados, utilizaremos também as funções getpid() e getppid().

getpid(): retorna o PID do processo atual.
getppid(): retorna o PID do processo pai (parent).

E agora o primeiro exemplo:
#include <unistd.h> // pid_t , getpid() , fork() , sleep
#include <stdio.h> // printf


int main(void){
	pid_t child_pid;

	printf("Parent PID: %d\n",getpid());

	child_pid = fork();
	if(child_pid){ // Ainda estamos no processo principal , podemos pegar o valor retornado pelo fork
		printf("Novo PID criado: %d\n",child_pid);
	} else if(pid == 0){ // Ja estamos em um outro processo (child)
		while(1){
			printf("Eu sou o processo filho %d , meu pai e %d , nao precisa de DNA\n",getpid(),getppid());
			sleep(5);
		}
		return(0);
	} else {
		// Fork Failed
		printf("Fork error\n");
	}

	while(1){
		sleep(3);
		printf("Dois loops infinitos rodando juntos ? fork() power\n");
	}


}

Executando:
$ gcc exemplo1.c -o exemplo1
$ ./exemplo1
Parent PID: 1788
Novo PID criado: 1789
Eu sou o processo filho 1789 , meu pai e 1788 , nao precisa de DNA
Dois loops infinitos rodando juntos ? fork() power
Eu sou o processo filho 1789 , meu pai e 1788 , nao precisa de DNA
Dois loops infinitos rodando juntos ? fork() power
Dois loops infinitos rodando juntos ? fork() power
Eu sou o processo filho 1789 , meu pai e 1788 , nao precisa de DNA
Dois loops infinitos rodando juntos ? fork() power
Eu sou o processo filho 1789 , meu pai e 1788 , nao precisa de DNA
Dois loops infinitos rodando juntos ? fork() power
Dois loops infinitos rodando juntos ? fork() power
Eu sou o processo filho 1789 , meu pai e 1788 , nao precisa de DNA
^C

Esperando Seus Filhos.

Depois de criar um novo processo , o processo principal ainda continua sendo executado , independentemente dos processo criados, caso você precise esperar a conclusão do processo para prosseguir com outras operações , ou algo do tipo , você pode usar duas funções, waitpid e wait , essas duas funções retornam o código de saída dos processos filhos (childs).

Função wait:
pid_t wait (int *status_ptr)
A função wait é uma versão simplificada do função waitpid , é usada para esperar a execução de qualquer processo filho.

Exemplo:
#include <unistd.h>
#include <stdio.h>

int fork_function(void){
	printf("Me espere ...\n");
	sleep(5);
	return(0);
}

int main(void){
	if(fork() == 0){
		fork_function();
		return;
	}

	int status;
	wait(&status);
	printf("Processo filho terminado , status: %d\n",status);
	return(0);
}


O processo principal irar esperar por 5 segundos, até encerrar sua execução completamente. Poderia também ter usado somente:
wait(NULL);
Caso o status de saída do processo filho não seja necessário.

Função waitpid:
pid_t waitpid (pid_t pid, int *status_ptr, int options)
Tem mais parâmetros do que a função wait , como foi dito antes, a função wait nada mais é que uma versão simplificada de waitpid , a função wait é equivalente a isso:
waitpid(-1, &status, 0);
waitpid(-1,NULL,0);
Ou usando o macro WAIT_ANY:
waitpid(WAIT_ANY, &status, 0);
waitpid(WAIT_ANY,NULL,0);
não vou me aprofundar muito nessa função , vou deixar só um exemplo , onde um PID especifico é esperado , e outro não:
#include <unistd.h>
#include <stdio.h>

int fork_function(void){
	printf("Sou o processo filho %d , Me espere ...\n",getpid());
	sleep(5);
	return(0);
}

int other_fork_function(void){
	printf("Sou o processo filho %d , meu pai vai se esquecer de mim\n",getpid());
	sleep(10);
	printf("Processo filho %d terminado, eu nao disse que meu pai ia me esquecer ? =/\n",getpid());
}

int main(void){
	pid_t childs[2];

	childs[0] = fork();
	if(childs[0] == 0){
		fork_function();
		return;
	}

	childs[1] = fork();
	if(childs[1] == 0){
		other_fork_function();
		return;
	}


	int status;
	waitpid(childs[0],NULL,0);
	printf("Processo filho %d terminado\n",childs[0]);
	return(0);
}

Executando:
$ gcc exemplo3.c -o exemplo3
$ ./exemplo3
Sou o processo filho 1922 , meu pai vai se esquecer de mim
Sou o processo filho 1921 , Me espere ...
Processo filho 1921 terminado
$ Processo filho 1922 terminado, eu nao disse que meu pai ia me esquecer ? =/

No exemplo o processo 1921 é esperado, mas o 1921 não , o processo principal é finalizado , mas o processo filho ainda continua rodando e 5 segundos depois a mensagem é enviada pro terminal.

Matando Processos.

Para matarmos processos utilizamos a função kill , mas porque matar processos ? Em certas situações pode ser útil , imagine: você deixa um processo em um loop infinito pra ficar criando arquivo de log , ou algo do tipo , quando você sair do programa o processo ainda continua rodando , para evitar isso você pode matar ele, usaremos a lib signal.h .
kill(pid_t pid,int signal);
Para ver a lista de sinais disponíveis:
$ cat /usr/include/bits/signum.h

exemplo de utilização da função kill:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int kill_me(void){
	while(1){
		printf("Voce vai me matar PAI ?\n");
		sleep(1);
	}
	return(0);
}

int main(void){
	pid_t pid = fork();
	if(pid ==0){
		kill_me();
		return;
	}

	printf("Aperte enter para matar o seu filho\n");
	getchar();

	kill(pid,SIGTERM);
	wait(NULL);
	printf("Parabens filho morto com sucesso\n");
	printf("Agora fuja para o paraguai\n");
	return(0);
}
Executando:
$ ./exemplo4
Aperte enter para matar o processo filho
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?
Voce vai me matar PAI ?

Parabéns filho morto com sucesso
Agora fuja para o Paraguai
No lugar de SIGTERM eu poderia colocar 15 ou 0xF, seria a mesma coisa.

Um exemplo de programa que usa fork,waitpid e kill :D :

((( Brute-force SSH )))

Referencias:

http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_23.html
http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html
http://www.thegeekstuff.com/2012/05/c-fork-function/
http://www.gnu.org/software/libc/manual/html_node/Kill-Example.html

Nenhum comentário:

Postar um comentário