A função sigaction() permite configurar tratamentos personalizados para sinais em programas C. Seu protótipo é:
#include <signal.h>
int manipular_sinal(int num_sinal, const struct acao_sinal *nova_acao, struct acao_sinal *acao_antiga);
A estrutura acao_sinal define as propriedades do tratamento:
struct acao_sinal {
void (*tratador_sinal)(int); // Função padrão de tratamento
void (*tratador_avancado)(int, info_sinal_t *, void *); // Usado com o flag SA_INFO_SINAL para informações adicionais
conjunto_sinais_t mascara_sinal; // Sinais bloqueados durante a execução do tratador
int flags_comportamento; // Configurações de comportamento
void (*restaurador)(void); // Uso interno, raramente acessado diretamente
};
O campo flags_comportamento suporta diversos valores, definidos no cabeçalho signal.h. Exemplos incluem:
#define INFO_SINAL 4 // Ativa tratador com três argumentos
#define NAO_BLOQUEAR 0x40000000 // Permite receber o mesmo sinal durante o tratamento
#define REINICIAR_SISCALL 0x10000000 // Reinicia chamadas de sistema interrompidas por sinais
#define REINICIAR_TRATADOR 0x80000000 // Restaura o tratador padrão após a primeira execução
Esses flags podem ser combinados com o operador bitwise OR. Por exemplo, para permitir que o sinal SIGINT seja tratado recursivamente e seja restaurado após o primeiro uso:
flags_comportamento = NAO_BLOQUEAR | REINICIAR_TRATADOR;
Exemplo Prático
Considere um programa que configura um tratador para o sinal SIGALRM, utilizado em temporizações:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void meu_tratador_alarme(int sinal) {
fprintf(stderr, "Alarme recebido! Sinal: %d\n", sinal);
}
int main() {
struct acao_sinal config;
config.tratador_sinal = meu_tratador_alarme;
config.flags_comportamento = 0;
if (manipular_sinal(SIGALRM, &config, NULL) != 0) {
perror("Falha ao configurar tratador");
return 1;
}
alarm(5); // Define um alarme para 5 segundos
while (1) {
pause(); // Aguarda sinais
}
return 0;
}
Comportamentos dos Flagss
A configuração dos flags altera significativamente o fluxo do programa. Considere um tratador para SIGINT:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void processar_sinais(int sinal) {
static int contagem = 0;
printf("Tratador executado %d vezes para o sinal %d\n", ++contagem, sinal);
sleep(3); // Simula processamento demorado
}
int main() {
struct acao_sinal atuacao;
atuacao.tratador_sinal = processar_sinais;
atuacao.flags_comportamento = NAO_BLOQUEAR; // Permitir reentrância
// Configura tratador para SIGINT (Ctrl+C)
manipular_sinal(SIGINT, &atuacao, NULL);
printf("Programa em execução. PID: %d\n", getpid());
while (1) {
sleep(1);
}
return 0;
}
Neste caso, se NAO_BLOQUEAR estiver definido, enviar SIGINT durante a execução do tratador resultará em chamadas recursivas. Caso contrário, o sinal será bloqueado até que o tratador atual finalize.
Controle de Sinais Adicionais
É possível bloquear outros sinais durante o tratamento usando o campo mascara_sinal:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void tratador_com_mascara(int sinal) {
printf("Iniciando tratamento para %d. Sinais adicionais serão bloqueados.\n", sinal);
sleep(2);
printf("Tratamento finalizado.\n");
}
int main() {
struct acao_sinal config_sinal;
config_sinal.tratador_sinal = tratador_com_mascara;
config_sinal.flags_comportamento = 0;
// Bloquear SIGQUIT (Ctrl+\) durante o tratamento de SIGINT
sigemptyset(&config_sinal.mascara_sinal);
sigaddset(&config_sinal.mascara_sinal, SIGQUIT);
manipular_sinal(SIGINT, &config_sinal, NULL);
printf("Aguardando sinais...\n");
while (1) {
pause();
}
return 0;
}
Aqui, SIGQUIT será mantido pendente e só será processado após o término do tratador para SIGINT.
Sinais Personalizados
Sinais como SIGUSR1 podem ser utilizados para comunicação entre processos ou threads:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define SINAL_CUSTOMIZADO SIGUSR1
void* thread_geradora(void* arg) {
sleep(1);
printf("Enviando sinal customizado do thread %ld\n", pthread_self());
raise(SINAL_CUSTOMIZADO);
return NULL;
}
void tratador_customizado(int sinal) {
printf("Sinal %d recebido no thread %ld\n", sinal, pthread_self());
}
int main() {
struct acao_sinal atuacao;
atuacao.tratador_sinal = tratador_customizado;
atuacao.flags_comportamento = 0;
manipular_sinal(SINAL_CUSTOMIZADO, &atuacao, NULL);
pthread_t id_thread;
pthread_create(&id_thread, NULL, thread_geradora, NULL);
pthread_join(id_thread, NULL);
printf("Thread principal aguardando...\n");
pause(); // Aguarda sinais (embora o sinal já tenha sido enviado)
return 0;
}
Note que sinais são entregues ao processo, mas o tratamento ocorre no contexto do thread que o recebeu, a menos que seja especificado com pthread_sigmask.