No final deste guião, deverá ser capaz de:
- Explicar o que é um signal e como se usa para controlar processos do sistema operativo;
- Programar o tratamento de sinais assíncronos.
Um sinal (signal) é uma mensagem assíncrona que pode ser enviada a qualquer momento para um processo ativo. O sistema operativo interrompe o fluxo normal de execução do processo para este tratar o sinal. O objetivo do envio do signal é provocar um comportamento específico, como a sua terminação, por exemplo. O tratamento de um sinal está predefinido, mas pode ser criada uma rotina para tratar este sinal como vamos ver a seguir.
Qualquer chamada a uma função durante a rotina de tratamento do sinal deverá ser segura (Async-Signal-Safe ou abreviadamente AS-Safe).
Por exemplo, a função printf
mantém dados alocados, como ponteiros e indíces para efetuar buffered I/O.
Se, durante a execução do programa, uma chamada à função printf
for interrompida por um sinal, uma nova chamada à função printf
durante a rotina de tratamento do signal resultará em comportamento indefinido, ou seja, poderá criar situações de erro inesperadas.
O envio de sinais pode ser feito com o comando kill
que, por omissão, envia um sinal para pedir ao processo para terminar (daí o nome do comando).
O nome do comando poderia ser algo como send signal, dado que permite enviar qualquer sinal, mas o nome original permanece.
O envio de sinais é apenas permitido ao utilizador dono do processo ou ao super-utilizador.
O envio de sinais pode ser feito com o comando kill
das seguintes formas:
$ kill -s TERM 1234
$ kill -TERM 1234
TERM
corresponde ao sinal que pretendemos enviar.
Pode consultar a lista de possíveis sinais com o comando: kill -L
.
A lista vai variar entre diferentes versões de Unix.
1234
corresponde ao identificador do processo (Process ID ou apenas PID) a que pretendemos enviar o sinal.
Pode consultar a lista de processos ativos com o comando: ps
(ou top
ou pgrep
).
Pergunta: Qual é o sinal enviado por omissão com o comando kill 1234
?
(Sugestão: procure a resposta a página no manual, com o comando: man kill
)
Caso um programa não responda ao sinal de terminação ordeira (SIGTERM
), pode ser usado o sinal para terminação forçada (SIGKILL
), normalmente associado ao comando kill -9
.
- Clone este repositório, usando o git:
git clone https://github.com/tecnico-so/lab_signals.git
- Estude o programa
intquit.c
:- Repare na definição da função
sig_handler()
, que será chamada assincronamente para tratar o sinal, quando o processo o receber; - Repare nos tratamentos do
SIGINT
(linha 16) e doSIGQUIT
(linha 27); - A função
signal()
regista a rotina de tratamento para estes sinais (linhas 37 e 39). Neste caso, a mesma rotinasig_handler()
(nome escolhido pelo programador), vai ser registada para tratar dois sinais diferentes (SIGINT
eSIGQUIT
), mas poderiam ser configuradas rotinas diferentes;
- Repare na definição da função
- Compile o programa com a ferramenta
make
(dentro da pastasrc/
); - Corra o programa (
./intquit
); - Observe que o programa inicia e fica num ciclo infinito ou parado.
- Experimente fazer
CTRL-C
(que enviaSIGINT
) e observe o resultado. - Repita o passo anterior.
- Experimente fazer
CTRL-\
(que enviaSIGQUIT
) e observe o resultado.
- Adicione o tratamento do sinal
SIGTERM
numa nova função chamadaterm_handler()
. - Compile e teste.
Notas: Não é possível enviar o sinal SIGTERM
com um atalho como outros sinais. Para testar o código deveram:
- Parar o processo com o sinal
SIGSTOP
através do atalhoCTRL-Z
. - Inspecionar o
pid
do vosso processo com o comandops
. - Envir o sinal
SIGTERM
com o comandokill pid
. - Regressar ao vosso processo com o comando
fg
.
Neste guião vimos o que são signals e como são úteis para envio de comandos assíncronos para os processos em execução.
O comando kill
permite enviar sinais para processos, sendo o mais frequente o sinal que indica ao programa que deve terminar ordeiramente (SIGTERM
).
Vimos também um exemplo de código com registo e chamada de uma rotina de tratamento de sinais assíncronos.
Os signals são também usados para o controlo de execução de vários programas numa única sessão de shell, o que se designa por job control.
Desta forma é possível ter vários programas a executar, um em primeiro plano (foreground) e outros em segundo plano (background).
Fica a sugestão de explorar estas capacidades com os comandos jobs
, bg
, fg
, e com o operador &
no fim dos comandos que permite executá-los em background.
Contactos para sugestões/correções: LEIC-Alameda, LEIC-Tagus, LETI