Programação C

Como usar manipuladores de sinal em linguagem C?

Como usar manipuladores de sinal em linguagem C?
Neste artigo, vamos mostrar como usar manipuladores de sinal no Linux usando a linguagem C. Mas primeiro vamos discutir o que é sinal, como ele vai gerar alguns sinais comuns que você pode usar em seu programa e, em seguida, veremos como vários sinais podem ser tratados por um programa enquanto o programa é executado. Então vamos começar.

Sinal

Um sinal é um evento que é gerado para notificar um processo ou thread de que alguma situação importante chegou. Quando um processo ou thread recebe um sinal, o processo ou thread irá parar o que está fazendo e tomar alguma ação. O sinal pode ser útil para a comunicação entre processos.

Sinais Padrão

Os sinais são definidos no arquivo de cabeçalho sinal.h como uma constante macro. O nome do sinal começou com um “SIG” e seguido por uma breve descrição do sinal. Então, cada sinal tem um valor numérico único. Seu programa deve sempre usar o nome dos sinais, não o número dos sinais. O motivo é que o número do sinal pode variar de acordo com o sistema, mas o significado dos nomes será padrão.

A macro NSIG é o número total de sinais definidos. O valor de NSIG é um maior que o número total de sinais definidos (todos os números de sinais são alocados consecutivamente).

A seguir estão os sinais padrão:

Nome do Sinal Descrição
SIGHUP Desligue o processo. O sinal SIGHUP é usado para relatar a desconexão do terminal do usuário, possivelmente porque uma conexão remota foi perdida ou desligada.
SIGINT Interromper o processo. Quando o usuário digita o caractere INTR (normalmente Ctrl + C), o sinal SIGINT é enviado.
SIGQUIT Saia do processo. Quando o usuário digita o caractere QUIT (normalmente Ctrl + \), o sinal SIGQUIT é enviado.
SIGILL Instrução ilegal. Quando é feita uma tentativa de executar uma instrução de lixo ou privilegiada, o sinal SIGILL é gerado. Além disso, SIGILL pode ser gerado quando a pilha estourar ou quando o sistema tiver problemas para executar um manipulador de sinal.
SIGTRAP Armadilha de rastreamento. Uma instrução de ponto de interrupção e outra instrução de trap gerará o sinal SIGTRAP. O depurador usa este sinal.
SIGABRT Abortar. O sinal SIGABRT é gerado quando a função abort () é chamada. Este sinal indica um erro que é detectado pelo próprio programa e relatado pela chamada de função abort ().
SIGFPE Exceção de ponto flutuante. Quando ocorre um erro aritmético fatal, o sinal SIGFPE é gerado.
SIGUSR1 e SIGUSR2 Os sinais SIGUSR1 e SIGUSR2 podem ser usados ​​como desejar. É útil escrever um manipulador de sinal para eles no programa que recebe o sinal para uma comunicação simples entre processos.

Ação Padrão de Sinais

Cada sinal tem uma ação padrão, uma das seguintes:

Prazo: O processo será encerrado.
Testemunho: O processo será encerrado e produzirá um arquivo de despejo central.
Ign: O processo irá ignorar o sinal.
Pare: O processo vai parar.
Cont: O processo continuará sendo interrompido.

A ação padrão pode ser alterada usando a função de manipulador. A ação padrão de alguns sinais não pode ser alterada. SIGKILL e SIGABRT a ação padrão do sinal não pode ser alterada ou ignorada.

Manuseio de Sinal

Se um processo recebe um sinal, o processo tem uma escolha de ação para esse tipo de sinal. O processo pode ignorar o sinal, pode especificar uma função de manipulador ou aceitar a ação padrão para esse tipo de sinal.

Podemos lidar com o sinal usando sinal ou sigação função. Aqui vemos como o mais simples sinal() função é usada para lidar com sinais.

int signal () (int signum, void (* func) (int))

O sinal() vai chamar o função função se o processo receber um sinal signum. O sinal() retorna um ponteiro para a função função se for bem sucedido ou retorna um erro para errno e -1 caso contrário.

O função ponteiro pode ter três valores:

  1. SIG_DFL: É um indicador para a função padrão do sistema SIG_DFL (), declarado em h arquivo de cabeçalho. É usado para tomar a ação padrão do sinal.
  2. SIG_IGN: É um ponteiro para a função de ignorar do sistema SIG_IGN (),declarado em h arquivo de cabeçalho.
  3. Ponteiro de função de manipulador definido pelo usuário: O tipo de função do manipulador definido pelo usuário é void (*) (int), significa que o tipo de retorno é nulo e um argumento do tipo int.

Exemplo de manipulador de sinal básico

#incluir
#incluir
#incluir
void sig_handler (int signum)
// O tipo de retorno da função do manipulador deve ser nulo
printf ("\ nfunção de tratamento interno \ n");

int main ()
sinal (SIGINT, sig_handler); // Registrar manipulador de sinal
for (int i = 1 ;; i ++) // Loop infinito
printf ("% d: função principal interna \ n", i);
dormir (1); // Atrase por 1 segundo

return 0;

Na captura de tela da saída de Exemplo1.c, podemos ver que na função principal o loop infinito está executando. Quando o usuário digita Ctrl + C, a execução da função principal é interrompida e a função de manipulador do sinal é chamada. Após a conclusão da função de manipulador, a execução da função principal foi retomada. Quando o usuário digita Ctrl + \, o processo é encerrado.

Exemplo de Ignorar Sinais

#incluir
#incluir
#incluir
int main ()
sinal (SIGINT, SIG_IGN); // Registra o manipulador de sinal para ignorar o sinal
for (int i = 1 ;; i ++) // Loop infinito
printf ("% d: função principal interna \ n", i);
dormir (1); // Atrase por 1 segundo

return 0;

Aqui, a função do manipulador é registrada para SIG_IGN () função para ignorar a ação do sinal. Então, quando o usuário digitou Ctrl + C,  SIGINT o sinal está sendo gerado, mas a ação é ignorada.

Exemplo de manipulador de sinal novamente

#incluir
#incluir
#incluir
void sig_handler (int signum)
printf ("\ nFunção de tratamento interno \ n");
sinal (SIGINT, SIG_DFL); // Registre novamente o manipulador de sinal para a ação padrão

int main ()
sinal (SIGINT, sig_handler); // Registrar manipulador de sinal
for (int i = 1 ;; i ++) // Loop infinito
printf ("% d: função principal interna \ n", i);
dormir (1); // Atrase por 1 segundo

return 0;

Na captura de tela da saída de Example3.c, podemos ver que quando o usuário digitou pela primeira vez Ctrl + C, a função de manipulador invocada. Na função de manipulador, o manipulador de sinal se registra novamente para SIG_DFL para ação padrão do sinal. Quando o usuário digita Ctrl + C pela segunda vez, o processo é encerrado, o que é a ação padrão do SIGINT sinal.

Enviando sinais:

Um processo também pode enviar explicitamente sinais para si mesmo ou para outro processo. As funções raise () e kill () podem ser usadas para enviar sinais. Ambas as funções são declaradas no sinal.h arquivo de cabeçalho.

int raise (int signum)

A função raise () usada para enviar sinal signum para o processo de chamada (em si). Ele retorna zero se for bem-sucedido e um valor diferente de zero se falhar.

int kill (pid_t pid, int signum)

A função kill usada para enviar um sinal signum a um processo ou grupo de processos especificado por pid.

Exemplo de manipulador de sinal SIGUSR1

#incluir
#incluir
void sig_handler (int signum)
printf ("Função de manipulador interna \ n");

int main ()
sinal (SIGUSR1, sig_handler); // Registrar manipulador de sinal
printf ("Dentro da função principal \ n");
aumentar (SIGUSR1);
printf ("Função principal interna \ n");
return 0;

Aqui, o processo envia o sinal SIGUSR1 para si mesmo usando a função raise ().

Aumente com Programa de Exemplo de Eliminação

#incluir
#incluir
#incluir
void sig_handler (int signum)
printf ("Função de manipulador interna \ n");

int main ()
pid_t pid;
sinal (SIGUSR1, sig_handler); // Registrar manipulador de sinal
printf ("Dentro da função principal \ n");
pid = getpid (); // Processo de identificação por si mesmo
matar (pid, SIGUSR1); // Envie SIGUSR1 para si mesmo
printf ("Função principal interna \ n");
return 0;

Aqui, o processo envia SIGUSR1 sinalizar para si mesmo usando mate() função. getpid () é usado para obter o próprio ID do processo.

No próximo exemplo, veremos como os processos pai e filho se comunicam (comunicação entre processos) usando mate() e função de sinal.

Comunicação entre pais e filhos com sinais

#incluir
#incluir
#incluir
#incluir
void sig_handler_parent (int signum)
printf ("Pai: recebeu um sinal de resposta da criança \ n");

void sig_handler_child (int signum)
printf ("Criança: recebeu um sinal do pai \ n");
dormir (1);
matar (getppid (), SIGUSR1);

int main ()
pid_t pid;
if ((pid = fork ())<0)
printf ("Bifurcação falhou \ n");
saída (1);

/ * Processo filho * /
else if (pid == 0)
sinal (SIGUSR1, sig_handler_child); // Registrar manipulador de sinal
printf ("Criança: aguardando sinal \ n");
pausa();

/ * Processo Pai * /
senão
sinal (SIGUSR1, sig_handler_parent); // Registrar manipulador de sinal
dormir (1);
printf ("Pai: enviando sinal para filho \ n");
matar (pid, SIGUSR1);
printf ("Pai: aguardando resposta \ n");
pausa();

return 0;

Aqui, garfo() função cria o processo filho e retorna zero para o processo filho e o ID do processo filho para o processo pai. Então, pid foi verificado para decidir o processo pai e filho. No processo pai, ele é adormecido por 1 segundo para que o processo filho possa registrar a função de manipulador de sinal e esperar pelo sinal do pai. Após 1 segundo processo pai enviar SIGUSR1 sinalizar para o processo filho e aguardar o sinal de resposta do filho. No processo filho, primeiro ele está esperando pelo sinal do pai e quando o sinal é recebido, a função do manipulador é chamada. A partir da função de manipulador, o processo filho envia outro SIGUSR1 sinalizar para os pais. Aqui getppid () função é usada para obter o ID do processo pai.

Conclusão

Sinal no Linux é um grande tópico. Neste artigo, vimos como lidar com o sinal desde o básico e também obter um conhecimento de como o sinal é gerado, como um processo pode enviar sinal para si mesmo e para outro processo, como o sinal pode ser usado para comunicação entre processos.

Como alterar as configurações do mouse e touchpad usando Xinput no Linux
A maioria das distribuições Linux vem com a biblioteca “libinput” por padrão para lidar com eventos de entrada em um sistema. Ele pode processar event...
Remapeie os botões do mouse de maneira diferente para software diferente com o controle de botão do mouse X
Talvez você precise de uma ferramenta que possa fazer o controle do seu mouse mudar com cada aplicativo que você usa. Se for esse o caso, você pode ex...
Análise do mouse sem fio Microsoft Sculpt Touch
Recentemente li sobre o Microsoft Sculpt Touch mouse sem fio e decidi comprá-lo. Depois de usá-lo por um tempo, decidi compartilhar minha experiência ...