Como usar manipuladores de sinal em linguagem C?

How Use Signal Handlers C Language



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 gerado para notificar um processo ou thread de que alguma situação importante chegou. Quando um processo ou encadeamento recebe um sinal, o processo ou encadeamento interrompe o que está fazendo e executa alguma ação. O sinal pode ser útil para 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. Portanto, cada sinal possui 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 Interrompa 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.
FOCA Instrução ilegal. Quando é feita uma tentativa de executar uma instrução '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 esse 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.
Essencial: O processo será encerrado e produzirá um arquivo de dump principal.
Ign: O processo irá ignorar o sinal.
Pare: O processo vai parar.
Conta: O processo continuará após ser 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.

  • Se a ação especificada para o sinal for ignorada, o sinal será descartado imediatamente.
  • O programa pode registrar uma função de manipulador usando funções como sinal ou sigação . Isso é chamado de manipulador capta o sinal.
  • Se o sinal não foi tratado nem ignorado, sua ação padrão ocorre.

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

intsinal() (intsinal, vazio (*função)(int))

o sinal() vai chamar o função função se o processo receber um sinal sinal . 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 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 executar 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
vaziosig_handler(intsinal){

// O tipo de retorno da função do manipulador deve ser nulo
printf (' nFunção de manipulador interna n');
}

inta Principal(){
sinal(SIGINT,sig_handler); // Registrar manipulador de sinal
para(inteu=1;;eu++){ //Loop infinito
printf ('% d: Função principal interna n',eu);
dormir(1); // Atrase por 1 segundo
}
Retorna 0;
}

Na captura de tela da saída de Example1.c, podemos ver que o loop infinito da função principal está em execução. Quando o usuário digita Ctrl + C, a execução da função principal é interrompida e a função manipuladora do sinal é chamada. Após a conclusão da função do 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
inta Principal(){
sinal(SIGINT,SIG_IGN); // Registra o manipulador de sinal para ignorar o sinal

para(inteu=1;;eu++){ //Loop infinito
printf ('% d: Função principal interna n',eu);
dormir(1); // Atrase por 1 segundo
}
Retorna 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

vaziosig_handler(intsinal){
printf (' nFunção de manipulador interna n');
sinal(SIGINT,SIG_DFL); // Registre novamente o manipulador de sinal para a ação padrão
}

inta Principal(){
sinal(SIGINT,sig_handler); // Registrar manipulador de sinal
para(inteu=1;;eu++){ //Loop infinito
printf ('% d: Função principal interna n',eu);
dormir(1); // Atrase por 1 segundo
}
Retorna 0;
}

Na captura de tela da saída de Example3.c, podemos ver que, quando o usuário digitou Ctrl + C pela primeira vez, a função do manipulador foi chamada. 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 arquivo de cabeçalho signal.h.

int levantar (intsinal)

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

intmatar(pid_t pid, intsinal)

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

Exemplo de manipulador de sinal SIGUSR1

#incluir
#incluir

vaziosig_handler(intsinal){
printf ('Função de manipulador interna n');
}

inta Principal(){
sinal(SIGUSR1,sig_handler); // Registrar manipulador de sinal
printf ('Função principal interna n');
levantar (SIGUSR1);
printf ('Função principal interna n');
Retorna 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
vaziosig_handler(intsinal){
printf ('Função de manipulador interna n');
}

inta Principal(){
pid_t pid;
sinal(SIGUSR1,sig_handler); // Registrar manipulador de sinal
printf ('Função principal interna n');
pid=getpid(); // Processo de identificação de si mesmo
matar(pid,SIGUSR1); // Envie SIGUSR1 para si mesmo
printf ('Função principal interna n');
Retorna 0;
}

Aqui, o processo envia SIGUSR1 sinalizar para si mesmo usando matar() 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 matar() e função de sinal.

Comunicação entre pais e filhos com sinais

#incluir
#incluir
#incluir
#incluir
vaziosig_handler_parent(intsinal){
printf ('Pai: Recebeu um sinal de resposta da criança n');
}

vaziosig_handler_child(intsinal){
printf ('Criança: Recebeu um sinal do pai n');
dormir(1);
matar(getppid(),SIGUSR1);
}

inta Principal(){
pid_t pid;
E se((pid=garfo())<0){
printf ('Fork Failed n');
saída (1);
}
/ * Processo filho * /
outro E se(pid==0){
sinal(SIGUSR1,sig_handler_child); // Registrar manipulador de sinal
printf ('Criança: esperando sinal n');
pausa();
}
/ * Processo Pai * /
outro{
sinal(SIGUSR1,sig_handler_parent); // Registrar manipulador de sinal
dormir(1);
printf ('Pai: enviando sinal para criança n');
matar(pid,SIGUSR1);
printf ('Pai: aguardando resposta n');
pausa();
}
Retorna 0;
}

Aqui, garfo() A função cria o processo filho e retorna zero para o processo filho e o ID do processo filho para o processo pai. Portanto, 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

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.