Utilizando a função sigaction() para gerenciamento de sinais em C

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.

Tags: sinais POSIX linguagem C programação de sistemas manipulação de sinais chamadas de sistema Linux

Publicado em 7-26 05:44