|
| 1 | +# 16 - Enviando sinais |
| 2 | + |
| 3 | +!!! pdf |
| 4 | +  |
| 5 | + |
| 6 | +<br> |
| 7 | + |
| 8 | +Na aula de hoje falamos sobre sinais e vimos que grande parte deles indica que algo excepcional aconteceu. Veremos neste handout as chamadas usadas para consultar o status de um processo quando ele acaba com erro e como enviar sinais para outros processos. |
| 9 | + |
| 10 | +## Recuperando informações de erros usando `wait` |
| 11 | + |
| 12 | +Anteriormente vimos que ao chamar `wait(&status);` guardamos informações sobre o fim do processo filho na variável `status`. Nos outros exercícios olhamos para os casos em que `WIFEXITED(status) == 1`. |
| 13 | + |
| 14 | +```C |
| 15 | +int wstatus; |
| 16 | + |
| 17 | +wait(&wstatus); |
| 18 | +printf("Filho acabou\n"); |
| 19 | + |
| 20 | +// Utiliza macro para ler um "pedaço" (um ou mais bits) de wstatus |
| 21 | +printf("Terminou normal?: %d\n", WIFEXITED(wstatus)); // lê 0 ou 1 |
| 22 | + |
| 23 | +// se terminou normalmente |
| 24 | +if (WIFEXITED(wstatus)) { |
| 25 | + // lê o byte menos significativo do return do filho |
| 26 | + printf("Valor de retorno: %d\n", WEXITSTATUS(wstatus)); |
| 27 | +} |
| 28 | +``` |
| 29 | +
|
| 30 | +Todo término inesperado de um programa em execução (processo) é feito usando um sinal. Ao acessar informações em um local de memória não mapeado para o nosso processo ele recebe o sinal `SIGSEGV`. Ao executar uma divisão por zero ele receberá o sinal `SIGFPE`. Logo, nestes casos é verdade que `WIFSIGNALED(status) == 1`, então podemos pegar o número do sinal usando a macro `WTERMSIG(status)`. |
| 31 | +
|
| 32 | +!!! exercise text short |
| 33 | + Leia o arquivo *parte1.c*. O que ele faz? Quantos processos são criados (conte o original)? |
| 34 | +
|
| 35 | + !!! answer "Resposta" |
| 36 | + O programa original cria um processo filho, que faz uma divisão por zero. São dois processos, portanto. |
| 37 | +
|
| 38 | +!!! example |
| 39 | + Modifique *parte1.c* para o processo pai esperar o fim do filho e mostrar uma mensagem com o `pid` do filho que acabou (pegue isto via `wait`). |
| 40 | +
|
| 41 | +!!! example |
| 42 | + No proceso pai, após o `wait` mostre no terminal as seguintes expressões: |
| 43 | +
|
| 44 | + * `WIFEXITED(status)` |
| 45 | + * `WIFSIGNALED(status)` |
| 46 | + * `WTERMSIG(status)` |
| 47 | +
|
| 48 | +
|
| 49 | +!!! example "To-Do" |
| 50 | + Mostrar o número do sinal não é muito útil. Pesquise sobre a chamada `strsignal` (`man strsignal`) e use-a para mostrar uma mensagem descritiva de qual sinal foi recebido no exercício acima. |
| 51 | +
|
| 52 | +!!! tip |
| 53 | + Após cada modificação no código, compile e execute para conferir os resultados! |
| 54 | +
|
| 55 | +## Envio de sinais via terminal |
| 56 | +
|
| 57 | +Além de erros e exceções, sinais também são usados para avisar de mudanças no sistema, sejam elas iniciadas pelo usuário ou por outros processos. A sequência de exercícios abaixo é um experimento de envio de sinais. |
| 58 | +
|
| 59 | +
|
| 60 | +!!! example |
| 61 | + Faça uma cópia do arquivo *parte1.c* e salve como *parte2.c*. No novo arquivo, altere para que o processo filho mostre seu pid e entre em loop infinito. |
| 62 | +
|
| 63 | +Claramente **nem o pai nem o filho terminam** no exemplo acima, para ver isso use no terminal o comando `ps -C parte2`, ele mostrará os processos em execução. Porém, se o filho terminar o pai termina também! O sinal **SIGKILL** é usado para terminar forçadamente um processo e ele pode ser enviado por qualquer outro processo do mesmo usuário (ou o *root*, que pode mandar sinais para qualquer processo do sistema). |
| 64 | +
|
| 65 | +O envio de sinais é feito usando a chamada `kill`. Assim como outras chamadas de sistema, `kill` possui também um programa de linha de comando. |
| 66 | +
|
| 67 | +!!! example |
| 68 | + Abra dois terminais e coloque um ao lado do outro. No primeiro, execute o programa *parte2.c*. |
| 69 | +
|
| 70 | +!!! example |
| 71 | + No segundo terminal, use a ferramenta de linha de comando `kill` para enviar o sinal **SIGKILL** para o processo filho. Se precisar, consulte a documentação usando `man 1 kill`. |
| 72 | +
|
| 73 | +!!! exercise text short |
| 74 | + Use `man 7 signal` para ver a lista de sinais disponíveis e seus números. Qual seria o número para o sinal `SIGINT`? |
| 75 | +
|
| 76 | + !!! answer "Resposta" |
| 77 | + Execute `man 7 signal`e procure por **Signal numbering for standard signals** |
| 78 | +
|
| 79 | +Isto deve ter feito o pai finalizar e mostrar a informação de que o processo filho foi finalizado. Note que o pai tem direito a saber o sinal usado para finalizar um filho: |
| 80 | +
|
| 81 | +!!! example |
| 82 | + Envie o sinal `SIGINT` para seu processo filho e verifique que o processo pai mostra o número correto. |
| 83 | +
|
| 84 | +## Envio de sinais em um programa |
| 85 | +
|
| 86 | +O programa `kill` é apenas um casquinha em volta de sua chamada de sistema. |
| 87 | +
|
| 88 | +!!! example |
| 89 | + Veja a documentação da chamada de sistema (em C) no manual `man 2 kill` |
| 90 | +
|
| 91 | +!!! example |
| 92 | + Faça uma cópia do arquivo *parte2.c* e salve como *parte3.c*. Modifique o novo arquivo para que o processo pai espere 10 segundos e envie um **SIGKILL** para o filho. |
| 93 | +
|
| 94 | +!!! exercise text short |
| 95 | + Agora os dois processos acabam? As informações de finalização no pai condizem com o sinal enviado? |
| 96 | +
|
| 97 | + !!! answer "Resposta" |
| 98 | + Se restarem dúvidas, confira com o professor! |
| 99 | +
|
| 100 | +### Extra |
| 101 | +
|
| 102 | +Até o momento usamos a chamada `wait` para esperar um processo filho acabar. Porém, não sabemos ainda checar **se** um processo filho acabou (um processo filho específico, sem necessariamente deixar a chamada `wait` travada)! Podemos fazer isso com a função `waitpid`, que recebe o `pid_t` do processo filho, e permite esperar ou não pela finalização dele. |
| 103 | +
|
| 104 | +!!! example |
| 105 | + Pesquise no manual pela flag `WNOHANG` da chamada `waitpid`. |
| 106 | +
|
| 107 | +!!! example |
| 108 | + Faça uma cópia do arquivo *parte3.c* e salve como *parte4.c*. Modifique o novo arquivo para que o processo pai só envie o sinal se o processo filho ainda estiver executando. Salve como *parte4.c* |
| 109 | +
|
0 commit comments