A função sigaction é utilizada para registrar uma rotina de tratamento de sinais no sistema Linux.
Assinatura da função:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Parâmetros:
- signum: o sinal que será capturado
- act: parâmetro de entrada, define o novo comportamento para o sinal
- oldact: parâmetro de saída, armazena o comportamento anterior do sinal
A estrutura sigaction é definida da seguinte forma:
struct sigaction {
void (*sa_handler)(int); // Função de tratamento do sinal
void (*sa_sigaction)(int, siginfo_t *, void *); // Função alternativa de tratamento
sigset_t sa_mask; // Sinais a serem bloqueados durante o tratamento
int sa_flags; // Sinalizadores de comportamento
void (*sa_restorer)(void); // Função de restauração (obsoleta)
};
Exemplo de implementação
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
void manipulador_sinal(int sinal) {
printf("Sinal recebido: [%d]\n", sinal);
sleep(3);
}
int principal() {
struct sigacao acao;
// Define a função de tratamento
acao.sa_handler = manipulador_sinal;
// Inicializa o conjunto de sinais bloqueados (nenhum bloqueado)
sigemptyset(&acao.sa_mask);
// Define flags padrão
acao.sa_flags = 0;
// Registra o tratamento para SIGINT (Ctrl+C)
if (sigaction(SIGINT, &acao, NULL) == -1) {
perror("Falha ao registrar sigaction");
return 1;
}
// Loop infinito para manter o programa executando
while(1) {
sleep(1);
}
return 0;
}
Comportamento observado: Durante a execução da função de tratamento de um sinal, se o mesmo sinal for gerado múltiplas vezes, eles entram no conjunto de sinais pendentes (bloqueados). Após a conclusão do tratamento, apenas uma instância do sinal será processada, pois os sinais não são enfileirados.
Bloqueio de sinais: Se outros sinais forem especificados em sa_mask, eles também serão bloqueados durante a execução da função de tratamento. Esses sinais pendentes serão processados apenas após a conclusão do tratamento atual.
Sinal SIGCHLD
Condições que geram o sinal SIGCHLD:
- Quando um processo filho termina
- Quando um processo filho recebe o sinal SIGSTOP
- Quando um processo filho parado recebe o sinal SIGCONT
Função do sinal SIGCHLD:
O kernel envia o sinal SIGCHLD ao processo pai quando um processo filho termina. Ao receber este sinal, o processo pai pode utilizar as funções wait() ou waitpid() para recuperar os recursos do processo filho.
Exemplo: Processo pai gerenciando múltiplos processos filhos
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
void manipulador_sigchld(int sinal) {
pid_t pid_filho;
// Loop contínuo para evitar僵尸进程 (processos zumbi)
while(1) {
// Espera por qualquer processo filho com WNOHANG (não bloqueante)
pid_filho = waitpid(-1, NULL, WNOHANG);
if (pid_filho > 0) {
printf("Processo filho [%d] encerrado\n", pid_filho);
}
else if (pid_filho == 0) {
// Ainda há processos filhos em execução
break;
}
else {
// Não há mais processos filhos
printf("Todos os processos filhos foram encerrados\n");
break;
}
}
}
int principal() {
sigset_t conjunto_sinais;
// Bloqueia SIGCHLD temporariamente
sigemptyset(&conjunto_sinais);
sigaddset(&conjunto_sinais, SIGCHLD);
sigprocmask(SIG_BLOCK, &conjunto_sinais, NULL);
// Cria dois processos filhos
for (int i = 0; i < 2; i++) {
pid_t pid = fork();
if (pid < 0) {
perror("Falha ao criar processo filho");
return 1;
}
else if (pid == 0) {
// Processo filho
printf("Processo filho [%d] criado (PID: %d, PID pai: %d)\n",
i, getpid(), getppid());
exit(0);
}
}
// Configura o manipulador de SIGCHLD
struct sigacao acao;
acao.sa_handler = manipulador_sigchld;
sigemptyset(&acao.sa_mask);
acao.sa_flags = 0;
sigaction(SIGCHLD, &acao, NULL);
// Desbloqueia SIGCHLD
sigprocmask(SIG_UNBLOCK, &conjunto_sinais, NULL);
// Mantém o processo pai executando
while(1) {
sleep(1);
}
return 0;
}
Considerações importantes:
- É possível que os processos filhos terminem antes que o manipulador de sinais seja registrado. A solução é bloquear o sinal SIGCHLD antes de criar os processos filhos e desbloqueá-lo após registrar o manipulador.
- Se um novo sinal SIGCHLD for gerado durante o tratamento do sinal atual, ele será bloqueado. Como os sinais não são enfileiraods, múltiplas omissões podem resultar em僵尸进程. A solução é usar um loop contínuo na função de tratamento para recuperar todos os processos filhos que podem ter terminado.
Comunicação entre processos pai e filho via sinais
Exemplo de implementação de uma contagem alternada entre processos pai e filho usando sinais:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int contador = 0; // Variável global para contagem
int controle = 0; // Variável de controle
void manipulador_pai(int sinal) {
printf("Pai: [%d]\n", contador);
contador += 2;
controle = 0;
sleep(1);
}
void manipulador_filho(int sinal) {
printf("Filho: [%d]\n", contador);
contador += 2;
controle = 0;
sleep(1);
}
int principal() {
pid_t pid = fork();
if (pid < 0) {
perror("Falha ao criar processo filho");
return 1;
}
else if (pid > 0) {
// Processo pai
signal(SIGUSR1, manipulador_pai);
controle = 1; // Inicia com controle para o filho enviar primeiro
while(1) {
if (controle == 0) {
kill(pid, SIGUSR2);
controle = 1;
}
}
}
else {
// Processo filho
contador = 1; // Filho começa com 1
signal(SIGUSR2, manipulador_filho);
while(1) {
if (controle == 0) {
kill(getppid(), SIGUSR1);
controle = 1;
}
}
}
}
Neste exemplo, os processos pai e filho alternam a execução enviando sinais um ao outro, criando um efeito de contagem alternada entre os dois processos.