Um pequeno aparte
Bem, eu não gosto muito deste guião. A página do manual da system call signal que é apresentada no guião, começa assim:
The behavior of signal() varies across Unix versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.
Depois de ler isto é óbvio porque é que eu prefiro sigaction. Mas a sigaction é ligeiramente mais complicada.
Ignorando a minha opinião negativa quanto a este guião, vou resolvê-lo com recurso a signal, porque aparentemente é o que os professores esperam.
CTRL-C
Espero que toda a gente saiba o que faz o CTRL-C. Se por acaso alguém não souber, aqui fica: CTRL-C serve para terminar o processo actual. Experimentem. Corram a shell do exercício anterior e façam CTRL-C.
Mas não basta saber isto para perceber o que temos de fazer. O que é que acontece por detrás das cortinas quando premimos CTRL-C?
Sinais
Em UNIX os processos podem receber vários sinais, e responder adequamente a cada um. Cada sinal tem um significado associado.
De todos os sinais, o mais conhecido é provavelmente o KILL, que serve para terminar um processo. O sinal TERM (de TERMinate) também termina um processo. A diferença entre o TERM e o KILL é que os processos podem responder ao TERM, mas não ao KILL. Podem ver o TERM como um pedido educado para o processo “limpar a casa e sair”, enquanto que o KILL é como dar um tiro no processo, terminando-o inapelavelmente.
Para enviar um sinal a um processo podemos usar o comando kill. Apesar do nome, kill serve para enviar qualquer sinal. Por omissão é enviado o sinal TERM. Alguns de vós já devem ter usado este comando, provavelmente numa destas formas:
$ kill -KILL pid $ kill –9 pid
Mas como é que o kill envia sinais? Usando a system call kill, que mais uma vez, apesar do nome, serve para enviar qualquer sinal.
Podem ver uma lista de sinais (e os respectivos códigos) correndo este comando:
$ kill –L
Por detrás das cortinas
Quando premimos CTRL-C o que realmente se passa é o envio do sinal INT ao processo actual.
Se consultarem a página do manual signal(7):
$ man 7 signal
… na secção Standard Signals podem ver uma breve descrição dos sinais existentes. Aí podem ver que a acção por omissão do SIGINT é terminar o processo. É por isso que o processo termina quando premimos CTRL-C.
Imunidade
Então como é que vamos tornar a shell imune ao CTRL-C? Vamos simplesmente ignorar o sinal SIGINT.
Na nossa função main vamos usar a system call signal para definir o signal handler para SIGINT como SIG_IGN, que é uma constante pré-definida que faz com que o sinal seja ignorado:
#include <signal.h>
int main(int argc, char** argv)
{
// No início:
signal(SIGINT, SIG_IGN);
// ... Outras cenas
}
E já está.
Ooops
Mas agora surge um problema… Experimentem correr este comando na vossa shell:
> yes
O CTRL-C não funciona! Não pode ser. Só queremos que a shell fique imune e não todos os processos que esta lança! Temos de mudar isto. Ao invés de ignorar o sinal, vamos “reenviá-lo” ao processo actual.
Ah, se ainda estiverem “presos” com o yes, façam CTRL-Z e corram este comando:
$ kill %1
De volta ao problema. Antes de mais é necessário saber o PID do processo actual. Simples. Enquanto esperamos pelo filho com waitpid, guardámos o seu PID numa variável global:
pid_t actual_pid = -1;
// Outras cenas...
actual_pid = pid;
waitpid(pid, &status, 0);
actual_pid = -1;
// Mais cenas...
Vamos agora criar o nosso signal handler que vai tratar de passar o sinal para o filho. Um signal handler é um função que recebe um inteiro (o sinal) e não devolve nada:
void propagate(int signum)
{
if(actual_pid != -1)
kill(actual_pid, signum);
}
Agora, em vez de registar SIG_IGN, registamos esta função:
signal(SIGINT, propagate);
Agora sim, podemos terminar o yes com CTRL-C, mas não a nossa shell.
E aqui está o código completo.
4 commentários:
Porque não puseste a possibilidade de por processos em brackground e foreground ?
Ainda não fiz essa parte :) Tou a tratar disso.
Antes de mais, parabéns pelo excelente blog que aqui tens.
Tenho seguido os teus posts desde o inicio e esclarecido algumas dúvidas que eu tinha em relação aos guiões propostos. Por vezes as explicações do prof. ainda complicam mais do que deviam ajudar.
Gostaria de saber se tens e podes disponibilizar código dos restantes guiões. Não devo precisar de muitas explicações, por vezes o código explica-se a si mesmo. Eu fui fazendo todos os guiões nas aulas, mas ficava mais esclarecido em cada um dos temas se pudesse analisar outros pontos de vista.
Muito obrigado.
@N: Não se se reparaste (e duvido que ainda tenha sido a tempo), mas ainda fiz upload do código dos guiões todos até aos pipes. Pelo que me disseram não se passou disso nas aulas práticas.
Enviar um comentário