Linguagem de Programação IV Carlos Oberdan Rolim Ciência da Computação Sistemas de Informação Criação de processos paralelos *baseado no material do Prof. Edison Ishikawa Como iniciar um processo em C? Similar a rodar um programa na linha de comando Usando a família de chamadas de sistemas exec…(…) exec, execl, execv, execve, … exec…() troca o processo corrente pelo novo que foi especificado Exec.... O processo que chamou exec... É completamente substituído pelo novo programa, e o novo programa inicia sua execução como se fosse a função principal (main). Não existe a criação de um novo processo mas sim a substituição do programa em execução Com fork cria-se novos processos Com exec inicia-se novos programas Exec – Diferentes funções #include <unistd.h> extern char **environ; int execl( const char *path, const char *arg, ...); int execle( const char *path, const char *arg , ..., char* const envp[]); int execlp( const char *file, const char *arg, ...); int execv( const char *path, char *const argv[]); int execvp( const char *file, char *const argv[]); Exec Diferenças os execl(), para o qual o número de argumentos do programa lançado é conhecido; os execv(), para o qual esse número é desconhecido. Em outras palavras, estes grupos de primitivas se diferenciam pelo número de parâmetros passados. Exec Diferenças Modo de passar os argumentos l = lista execl, execlp, execle Requer que cada um dos argumentos da linha de comando passada ao novo programa seja especificada por argumentos em separado. O último argumento é o null pointer - (char *) 0 Ex: char* arg0, char* arg1,....,char *argn, (char *) 0 v = vetor execv, execvp, execve Os argumentos são passados em um array de ponteiros, e o endereço deste array é passado como argumento Exec Propriedades O novo programa herda do processo que o invoca Process ID e parent process ID Real user ID and real group ID Supplementary group IDs Process group ID Session ID Controlling terminal Time left until alarm clock Current working directory Root directory File mode creation mask File locks Process signal mask Resource limits tms_utime, tms_stime, tms_cutime and tms ustime values execv() - exemplo int execv(const char *path, char *const argv[]) path - command path argv - argumentos (por um null no final) Exemplo char* prog[3]={“/usr/bin/ps”, “-a”, NULL}; execv(prog[0], prog); Sugestão consulte o man execv – exemplo //gcc -o execv execv.c //Existem diversas chamadas execXXX #include <stdio.h> #include <stdlib.h> int main(){ char *my_program[3] = {"/bin/ls", "-l“,NULL}; execv(my_program[0],my_program); printf("Cannot execute the command.\n"); return 0; } Como executar vários processo em um único programa C? System call fork() quando um processo é “forkeado”, um novo processo é criado o segmento de dados e códigos do novo processo é o mesmo do original uma entrada na tabela de processos é criada para o novo processo Fork Processo 1 Processo 1 PAI fork FILHO Processo 2 shell shell shell fork shell exec ps System call fork Copia o processo corrente e o executa valor de retorno ZERO no processo filho O Id do processo filho (PID) no processo pai use o valor de retorno para identificar aonde o programa está Exemplo de fork #include < stdio.h> #include <sys/types.h> #include <unistd.h> int main(){ if (fork()==0) { printf(“Eu sou o filho\n”); Cria o processo filho Código executado pelo filho } else { printf(“Eu sou o pai\n”); } } Código executado pelo pai Comportamento fork Comportamento do fork Sequencia fork() + execve() Fork vs Execve() O que acontece com o processo filho quando ele morre antes que o pai? Zombie Quando o processo filho termina, ele tem que avisar o pai antes de se matar Se o pai não tomar conhecimento da morte do filho, o filho se tornará um Zombie O filho ficará no estado Zombie até que o pai tome conhecimento Zombie Os recursos usados pelo Zombie não são liberados! Os processos zombie são rotulados como <defunct> na listagem do comando ps -u username Se o seu programa(pai) rodar por muito tempo, criando zombies, ele irá esgotar a memória É obrigação do programador (sua) evitar isso Exemplo de zombie # ./zombie I am parent. I loop here. My child pid is [2966] #include <stdio.h> #include <stdlib.h> int main(){ int pid; pid = fork(); I am child. I become a zombie now. Saída do ps -ax 2965 pts/0 2966 pts/0 Z+ R+ 0:24 ./zombie 0:00 [zombie] <defunct> if (pid==0){ printf(“I am child. I become a zombie now.\n”); exit (0); child process terminate here } else { printf(“I am parent. I loop here. My child pid is [%d]\n”, pid); while(1); parent process continue to run } return 0; } Como evitar o Zombie Usando a system call wait e waitpid no processo pai para tomar conhecimento da morte do filho A função wait suspende a execução do processo até a morte de seu filho. Se o filho já estiver morto no instante da chamada da primitiva (caso de um processo zumbi), a função retorna imediatamente. A função waitpid suspende a execução do processo até que o filho especificado pelo argumento pid tenha morrido. Se ele já estiver morto no momento da chamada, o comportamento é idêntico ao descrito anteriormente. Esperar pela terminação Chamado também de join Wait: cenários possíveis Wait e Waitpid #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status) /* espera a morte de um filho */ pid_t waitpid(pid_t pid, int *status, int options) int *status /* status descrevendo a morte do filho */ Valor de retorno: identificador do processo morto ou -1 em caso de erro. Wait e Waitpid O valor do argumento pid pode ser: < -1 : significando que o pai espera a morte de qualquer filho cujo o ID do grupo é igual so valor de pid; -1 : significando que o pai espera a morte de qualquer filho; 0 : significando que o pai espera a morte de qualquer processo filho cujo ID do grupo é igual ao do processo chamado; > 0 : significando que o pai espera a morte de um processo filho com um valor de ID exatamente igual a pid. Wait e Waitpid Se status é não nulo (NULL), wait e waitpid armazena a informação relativa a razão da morte do processo filho, sendo apontada pelo ponteiro status. Este valor pode ser avaliado com diversas macros que são listadas com o comando shell # man 2 wait. O código de retorno via status indica a morte do processo que pode ser devido uma: uma chamada exit(), e neste caso, o byte à direita de status vale 0, e o byte à esquerda é o parâmetro passado a exit pelo filho; uma recepção de um sinal fatal, e neste caso, o byte à direita de status é não nulo. Os sete primeiros bits deste byte contém o número do sinal que matou o filho. Exemplo St é um ponteiro global que recebe o retorno de wait Exemplo Exemplo # ./zombie2 #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> int main(){ int pid; I am parent. My child pid is [2982] I am child. Saída do ps -ax 2981 pts/0 R+ 0:04 ./zombie2 pid = fork(); if (pid==0){ printf(“I am child.\n”); exit (0); o processo filho termina aqui } else { printf(“I am parent. My child pid is [%d]\n”, pid); wait(NULL); espera o filho aqui while(1); } return 0; } #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> int main(void){ pid_t child_pid, wpid; int status = 0; int i; int a[3] = {1, 2, 1}; printf("parent_pid = %d\n", getpid()); for (i = 0; i < 3; i++) { printf("i = %d\n", i); if ((child_pid = fork()) == 0) { printf("In child process (pid = %d)\n", getpid()); if (a[i] < 2) { printf("Should be accept\n"); exit(1); } else{ printf("Should be reject\n"); exit(0); } /*NOTREACHED*/ } } while ((wpid = wait(&status)) > 0) { printf("Exit status of %d was %d (%s)\n", (int)wpid, status, (status > 0) ? "accept" : "reject"); } return 0; } Saída: parent_pid = 15820 i=0 i=1 In child process (pid = 15821) Should be accept i=2 In child process (pid = 15822) Should be reject In child process (pid = 15823) Should be accept Exit status of 15823 was 256 (accept) Exit status of 15822 was 0 (reject) Exit status of 15821 was 256 (accept) Criando dados compartilhados - IPC shmget vs mmap Bibliografia Operating Systems, 4th Ed, William Stallings, Prentice Hall Advanced Programming in the UNIX Environment, W. R. Stevens, Addison-Wesley Programming with GNU Software, M. Loukides e A. Oram, O'Reilly Managing Projects with make, A. Oram e S. Talbott, O'Reilly