Manusear o SIGABRT Sinal em C++
-
Utilize
sigaction
para registarSIGABRT
Signal Handler -
Utilize a variável
sig_atomic_t
Variable in Signal Handler
Este artigo irá demonstrar múltiplos métodos de como lidar com o sinal SIGABRT
em C++.
Utilize sigaction
para registar SIGABRT
Signal Handler
Os sistemas operacionais baseados em Unix suportam a funcionalidade chamada signal
, um mecanismo pelo qual um programa pode enviar uma mensagem a outro programa de forma assíncrona. Os sinais são normalmente enviados pelo utilizador ou pelos processos do sistema operativo que necessitam de interromper o programa. O manipulador do sinal é a secção de código que é chamada se o programa receber o sinal dado. Existem alguns sinais padrão para tarefas como abortar o programa, parar o programa, e continuar o programa com os seus correspondentes manipuladores de sinal padrão. No entanto, o utilizador pode anular a maioria destes manipuladores de sinais com funções personalizadas, e sigaction
é a função que a regista.
Note-se que SIGABRT
é um dos sinais que resulta na terminação do programa por defeito. No exemplo de código seguinte, anulamos o seu manipulador de sinais e atribuímos a nossa função definida (cleanupRoutine
) para ser chamada assim que o sinal for recebido.
No início, o objecto do tipo struct sigaction
deve ser declarado e inicializado com a chamada de função memset
.
Em seguida, ao seu membro de dados sa_handler
deve ser atribuído o endereço da função que precisa de ser chamada.
Depois disto, precisamos de mascarar outros sinais de interrupção do manipulador SIGABRT
, e a chamada de função sigfillset
consegue isso.
Finalmente, registamos o manipulador de sinais com uma chamada de sigaction
que leva três argumentos: número do sinal, endereço da struct sigaction
, e uma estrutura opcional onde a acção anterior pode ser guardada.
Neste caso, ignoramos o terceiro argumento, mas o nullptr
ainda precisa de ser especificado como um parâmetro. Definimos a função cleanupRoutine
para imprimir a string para a consola e depois sair do programa para verificar facilmente a execução correcta do manipulador. O resto do programa é o loop infinito que precisa de ser interrompido quando o utilizador envia o sinal SIGABRT
. Para testar o programa, execute-o numa janela terminal e envie o sinal da segunda janela executando o seguinte comando kill -SIGABRT pid_num_of_program
.
#include <csignal>
#include <cstring>
#include <iostream>
void cleanupRoutine(int signal_number) {
write(2, "printed from cleanupRoutine\n", 28);
_exit(EXIT_SUCCESS);
}
int main() {
struct sigaction sigabrt_action {};
memset(&sigabrt_action, 0, sizeof(sigabrt_action));
sigabrt_action.sa_handler = &cleanupRoutine;
if (sigfillset(&sigabrt_action.sa_mask) != 0) {
perror("sigfillset");
exit(EXIT_FAILURE);
}
if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0) {
perror("sigaction SIGABRT");
exit(EXIT_FAILURE);
}
int i = 0;
while (true) {
i += 1;
}
exit(EXIT_SUCCESS);
}
Utilize a variável sig_atomic_t
Variable in Signal Handler
Os manipuladores de sinais são tipos especiais de funções que são necessários para ter certas características. Nomeadamente, as únicas variáveis com garantia de funcionamento correcto são as variáveis atómicas. Há um tipo especial sig_atomic_t
, um inteiro que pode ser definido durante a execução do manipulador de sinais. Esta variável é declarada com a palavra-chave volatile
, e qualquer modificação à mesma é vista globalmente. O exemplo seguinte demonstra como podemos incluir esta variável como uma condição para a declaração do laço.
Note-se que o manipulador apenas define a variável de 1 a 0 e não sai do programa se o sinal for recebido. Ou seja, o programa tenta continuar a partir do ponto em que foi interrompido. Neste caso, a iteração do laço é reiniciada, e quando a condição é verificada para ser falsa, salta para fora do laço. Desta forma, o utilizador pode controlar o comportamento do programa utilizando os sinais.
#include <csignal>
#include <cstring>
#include <iostream>
using std::cout;
using std::endl;
volatile sig_atomic_t shutdown_flag = 1;
void cleanupRoutine(int signal_number) { shutdown_flag = 0; }
int main() {
struct sigaction sigabrt_action {};
memset(&sigabrt_action, 0, sizeof(sigabrt_action));
sigabrt_action.sa_handler = &cleanupRoutine;
if (sigfillset(&sigabrt_action.sa_mask) != 0) {
perror("sigfillset");
exit(EXIT_FAILURE);
}
if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0) {
perror("sigaction SIGABRT");
exit(EXIT_FAILURE);
}
int i = 0;
while (shutdown_flag) {
i += 1;
}
cout << "Exiting ..." << endl;
exit(EXIT_SUCCESS);
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook