Desenvolvimento de aplicativos Linux e manipulação de sinais

Desenvolvimento de aplicativos Linux e manipulação de sinais

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

Que sinais devo tratar para construir um aplicativo bem comportado?

Responder1

https://en.wikipedia.org/wiki/Unix_signal

possui uma lista de sinais padrão e suas ações padrão. Qualquer processo suficientemente privilegiado sempre pode enviar qualquer sinal, mas os processos (ou você, por meio de processos) não deveriam fazer isso.

Você só deve kille espera que seus processos sejam killeditados comPRAZOe vários sinais que podem ser gerados pelo seu ambiente (o shell, o driver do 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 coredumping

ABRT TRAP SYS BUS FPE ILL SEGV   XFSZ XCPU QUIT

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

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

Fora desinais de terminação:

HUP INT PIPE TERM ALRM POLL PROF USR1 USR2

você deve esperar

HUP INT PIPE TERM

do seu ambiente em diversas 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ê deverá receber o restante dos sinais de terminação somente se você mesmo configurá-lo.

MATARePARARvocê não pode fazer nada a respeito.

Você pode desejar interceptar o terminal geradosinais de parada:

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á o seu processo, sem corromper o seu estado.

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

DR:

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

No último caso, você pode bloquear totalmente todos os sinais não tratados, mas tome cuidado, pois as máscaras de bloqueio de sinal são herdadas através de bifurcações e chamadas execve e ignoram ou bloqueiam sinais como ILL, se eles surgirem da execução do seu processo e não foram enviados para você por kill ou sigqueue lhe dará um comportamento indefinido.

Se você quiser se aprofundar mais nisso, explore as páginas de manual e os padrões. Sinais são um tópico bastante importante em Unixes e manuseá-los pode ser muito complicado.

Responder2

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

Dito isto, você pode dar uma olhada emsignals(7)para obter uma lista de sinais que podem causar o encerramento do processo:

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

Se o seu aplicativo passa algum tempo conectado a um terminal, o manuseio SIGINTé uma boa ideia ( afinal, Ctrl+ é bastante famoso). também é uma boa ideia porque continua sendo o sinal de terminação básico e é usado pelo kernel (por exemplo, no desligamento do sistema). é provavelmente a versão mais "dura" das duas anteriores, pois causa um core dump. Quando você receber esse, provavelmente será esperado que seu programa trave imediatamente. Ao receber qualquer um dos outros, espera-se que seu programa "limpe a bagunça e saia" (salve as configurações, encerre corretamente).CSIGTERMSIGQUIT

SIGILL, SIGFPEe são sinais relacionados ao mau comportamento do programa SIGSEGV.SIGPIPEfalha de segmentaçãoé provavelmente a situação mais recorrente, mas no final, todos esses casos podem ser tratados como bugs, e não acredito que captar o sinal seja a melhor forma de se recuperar dessas situações.

  • Na maioria das vezes, os compiladores não deixam SIGILLisso acontecer tão facilmente (mais aqui) então acho que você pode deixar isso de lado.
  • Certifique-se de nunca dividir por zero e evitará a maioria dos SIGFPEs. Fora isso, apenas certifique-se de que seu programa seja bom em matemática e não enlouqueça com os números.
  • SIGSEGVpode ser causado por várias coisas (acesso ilegal à memória, desrefeciamento ilegal) e, para isso, sugiro usar gdbpara monitorar seu programa enquanto ele é executado e rastrear a instrução desagradável usando gdba pilha de.
  • Finalmente, SIGPIPEestá principalmente relacionado com fluxos de dados (leitura de arquivos, conexões, ...). Se algum dos fluxos de E/S do seu programa for encerrado durante a execução, você deve certificar-se de que seu programa verifique seus descritores antes de usá-los e lide com todos os eventos de E/S que ocorrem nele (ao usar selectou pollpor exemplo). gdbpode ser útil aqui também.

SIGABRT, SIGALRMe são basicamente sinais do usuário SIGUSR1. SIGUSR2Se você os está recebendo, é provável que você realmente tenha feito isso acontecer.

Isso nos deixa com SIGHUPe SIGKILL, que pode não ser detectado.

  • SIGHUPocorrerá, por exemplo, se você iniciar o processo a partir de um terminal e fechar esse terminal posteriormente sem desanexar o processo ( disownou usando nohup). Se o seu programa for iniciado a partir do terminal e executado como um deamon, não se esqueça de desvinculá-lo do shell, por exemplo, por bifurcação dupla. Este sinal específico é menos relevante se o seu programa puder iniciar a partir de uma GUI ou no momento da inicialização, onde é menos provável que o terminal de controle seja "fechado".

  • SIGKILLcomo você sabe, é o todo-poderoso dos sinais. É a maneira do kernel dizer "merda!". Se você estiver recebendo isso, concentre sua atenção no que está emitindo o sinal, e não em 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 acionado pelo kernel quando seu programa se comportar mal, gdbdeverá ajudá-lo a localizar o problema. Nesse caso, esqueça o sinal e concentre-se no bug sobre o qual ele está falando.

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

informação relacionada