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:
- 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.
- SIG_IGN : É um ponteiro para a função de ignorar do sistema SIG_IGN () , declarado em h arquivo de cabeçalho.
- 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.