Desenvolvimento e manuseio de sinais no Linux

6

Atualmente, estou tendo problemas com meus usuários reclamando do término do meu aplicativo. Em algumas condições (aparentemente arbitrárias) e ambientes de área de trabalho, o aplicativo não é finalizado e as configurações não são salvas na reinicialização. Eu perguntei nos canais relevantes do irc e na maioria das vezes eu recebia ordens para lidar corretamente com os sinais. Eu sei de SIGINT para Ctrl-C no terminal e SIGTERM para a terminação "normal". Mas me disseram que o SIGHUP também é importante. Então minha pergunta é:

Que sinais eu tenho que manipular para construir um aplicativo bem comportado?

    
por ManuelSchneid3r 19.05.2016 / 13:34

2 respostas

2

link

tem uma lista de sinais padrão e suas ações padrão. Qualquer processo com privilégios suficientes sempre pode enviar qualquer sinal, mas processses (ou você, via processos) não devem fazer isso.

Você deve apenas kill com e esperar que seus processos sejam kill ed com TERM e um monte de sinais que podem ser gerados pelo seu ambiente (o shell, o driver de terminal), a menos que você saiba que o alvo lida com o sinal especial que você deseja enviar.

Você pode classificar os sinais básicos por suas ações.

Os sinais de ressalto

ABRT TRAP SYS BUS FPE ILL SEGV   XFSZ XCPU QUIT

são gerados por você (TRAP, ABRT, SYS) usando algumas funções especializadas ou entrando em estados de erro difíceis (BUS, FPE, ILL, SEGV). QUIT é gerado por um usuário em um terminal que deseja um dump principal ( C+\ ).

Você pode querer mantê-los em suas disposições padrão.

Fora dos sinais de terminação :

HUP INT PIPE TERM ALRM POLL PROF USR1 USR2

você deve esperar

HUP INT PIPE TERM

do seu ambiente em várias circunstâncias:

HUP -- when the terminal hungs up or you become a stopped orphaned process
INT -- when the user at the terminal interrupts you with C-c
PIPE -- when a PIPE or socket you write into closes, e.g. by
        exiting before you finished writing to it 
       ('yourprogram | (exit)' will give you a 'PIPE' 
        if yourprogram attempts to write to its STDOUT) 
TERM -- when a process ends you a normal termination request

Você deve receber o restante dos sinais de terminação somente se você configurá-lo você mesmo.

KILL e STOP você não pode fazer nada.

Você pode querer interceptar o terminal interrompendo os sinais :

TTIN -- you read from the terminal and you're a backgrounded process
TTOU -- you write the terminal when you're a backgrounded process
        and the terminal is set to put you to sleep when you do
TSTP -- you're being put to sleep with 'C-Z'

mas, na pior das hipóteses, isso apenas interromperá seu processo, sem corromper seu estado.

A menos que você saiba que precisa manipular CHLD , CONT ou URG , não precisa.

TL; DR:

Basicamente, acho que HUP, INT, PIPE e TERM devem ser manipulados (ou ignorados) se você normalmente quer fazer alguma limpeza antes da saída. O resto pode ser deixado sozinho, a menos que seu programa use esses sinais, ou a menos que você absolutamente precise de alguma limpeza em todas as circunstâncias.

No último caso, você pode bloquear todos os sinais não manipulados, mas tenha cuidado para que as máscaras de bloqueio de sinal sejam herdadas entre garfos e chamadas execve e ignorem ou bloqueiem sinais como ILL, se surgirem da execução do processo e não forem enviado para você por kill ou sigqueue lhe dará um comportamento indefinido.

Se você quiser mais informações, explore os manpages e os padrões. Os sinais são um assunto muito importante nos Unixes e lidar com eles pode ser muito complicado.

    
por 30.08.2016 / 14:48
1

Se você está indo nessa direção, provavelmente acabará ignorando um bom número de sinais, simplesmente porque um deles está encerrando seu programa. Como sugerido por siblynx em um comentário, o melhor curso de ação seria analisar esses sinais e entender de onde eles vêm. Afinal, pode haver uma boa razão para o seu programa ser morto.

Dito isto, você pode dar uma olhada em signals(7) para obter uma lista de sinais que podem causar a finalização do processo:

SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1 and SIGUSR2.

Se seu aplicativo gasta algum tempo anexado a um terminal, manipular SIGINT é uma boa idéia ( Ctrl + C é bem famoso, afinal de contas). SIGTERM também é uma boa idéia, porque continua sendo o sinal de terminação básico e é usado pelo kernel (por exemplo, após o desligamento do sistema). SIGQUIT é provavelmente a versão "mais dura" dos dois anteriores, pois causa um dump principal. Quando você receber esse, provavelmente será esperado que seu programa caia imediatamente. Ao receber qualquer um dos outros, seu programa deve "limpar sua bagunça e sair" (salvar configurações, finalizar corretamente).

SIGILL , SIGFPE , SIGSEGV e SIGPIPE são sinais relacionados ao mau comportamento do programa. Falha de segmentação é provavelmente a situação mais recorrente, mas no final, todos esses casos podem ser tratados como erros, e eu não acredito que capturar o sinal seja a melhor maneira de se recuperar dessas situações.

  • Na maioria das vezes, os compiladores não permitem que SIGILL aconteça tão facilmente ( mais aqui ) então eu acho que você pode deixar isso de lado.
  • Certifique-se de nunca dividir por zero e você evitará a maioria dos SIGFPE s. Além disso, apenas certifique-se de que seu programa é bom em matemática e não enlouqueça com os números.
  • SIGSEGV pode ser causado por várias coisas (acesso ilegal à memória, desregulação ilegal) e, para isso, sugiro usar gdb para monitorar seu programa enquanto ele é executado e rastrear a instrução desagradável usando gdb ' s pilha.
  • Finalmente, SIGPIPE está principalmente relacionado a fluxos de dados (leitura de arquivos, conexões, ...). Se qualquer um dos seus fluxos de E / S do programa for terminado enquanto é executado, você deve verificar se o programa verifica seus descritores antes de usá-los e lida com todos os eventos de E / S que ocorrem nele (quando usar select ou poll por exemplo). gdb também pode ser útil aqui.

SIGABRT , SIGALRM , SIGUSR1 e SIGUSR2 são basicamente sinais do usuário. Se você está recebendo, é provável que você tenha feito isso acontecer.

Isso nos deixa com SIGHUP e SIGKILL , que podem não ser capturados.

  • SIGHUP ocorrerá por exemplo se você iniciar o processo a partir de um terminal e fechar esse terminal posteriormente sem desanexar o processo ( disown ou usando nohup ). Se o seu programa é iniciado a partir do terminal e é executado como um deamon, não se esqueça de tê-lo desconectado do shell, por exemplo, por double-forking. Este sinal em particular é menos relevante se o seu programa puder iniciar a partir de uma GUI, ou no momento da inicialização, onde o terminal de controle é menos provável de ser "fechado".

  • SIGKILL como você sabe, é o todo-poderoso de sinais. É a maneira do kernel dizer "sh * t up!" Se você está recebendo isso, então você deve focar sua atenção no que está emitindo o sinal, ao invés de como lidar com isso em seu programa.

No final, analisar o sinal e usar ferramentas de depuração é provavelmente a abordagem mais simples para esses problemas. Se um sinal for disparado pelo kernel quando seu programa se comportar mal, gdb deve ajudá-lo a localizar o problema. Nesse caso, esqueça o sinal e concentre-se no bug que está lhe dizendo.

Se o sinal vem de "fora", encontrar o emissor é provavelmente o melhor curso de ação (se você realmente não consegue encontrá-lo, rastreio a chamada de sistema kill(2) pode ser um bom último recurso ) . Então você pode decidir se deseja ignorá-lo ( SIG_IGN ) ou levá-lo em conta. Silenciar o programa emissor também pode ser uma alternativa decente ...

    
por 30.08.2016 / 13:48

Tags