O sinal pode ser ignorado (perdido)?

9

Eu tenho um aplicativo que está se comunicando com os trabalhadores por meio de sinais (particularary SIGUSR1 / SIGUSR2 / SIGSTOP).

Posso confiar que, aconteça o que acontecer, todo sinal será entregue e processado pelo manipulador?

O que acontece se os sinais são enviados mais rapidamente do que não é possível para o aplicativo lidar com eles (por exemplo, devido à alta carga do host no momento)?

KaP
fonte

Respostas:

8

Além do problema de "muitos sinais", os sinais podem ser explicitamente ignorados. De man 2 signal:

If the signal signum is delivered to the process, then one of the
following happens:    
  *  If the disposition is set to SIG_IGN, then the signal is ignored.

Os sinais também podem ser bloqueados. De man 7 signal;

A signal may be blocked, which means that it will not be delivered
until it is later unblocked.  Between the time when it is generated
and when it is delivered a signal is said to be pending.

Conjuntos de sinais bloqueados e ignorados são herdados por processos filhos, portanto, o processo pai do aplicativo ignorou ou bloqueou um desses sinais.

O que acontece quando vários sinais são entregues antes que o processo termine de lidar com os anteriores? Isso depende do sistema operacional. A página de signal(2)manual vinculada acima discute:

  • O sistema V redefiniria a disposição do sinal para o padrão. Pior, a entrega rápida de vários sinais resultaria em chamadas recursivas (?).
  • O BSD bloqueará automaticamente o sinal até que o manipulador esteja pronto.
  • No Linux, isso depende dos sinalizadores de compilação definidos para o GNU libc, mas eu esperaria o comportamento do BSD.
muru
fonte
4
A página de manual do Linux para signal(2)sugere enfaticamente que você evite essa confusão usando sigaction(2).
Nate Eldredge
7

Você não pode confiar que todos os sinais enviados serão entregues. Por exemplo, o kernel do linux "coalesce" o SIGCHLD se um processo demorar muito no tratamento do SIGCHLD a partir de um processo filho encerrado.

Para responder a outra parte da sua pergunta, os sinais são "enfileirados" dentro do kernel se um número de sinais diferentes chegar em um intervalo muito curto.

Você deve usar sigaction()para configurar o manipulador de sinal com o sa_sigactionmembro de siginfo_t, configurando o sa_maskmembro do siginfo_targumento com cuidado. Eu acho que isso significa mascarar todos os sinais "assíncronos", pelo menos. De acordo com a página de manual do Linux sigaction(), você também irá mascarar o sinal que está sendo tratado. Acho que você deve definir o sa_flagsmembro como SA_SIGINFO, mas não me lembro por que tenho essa superstição. Acredito que isso fará com que seu processo seja um manipulador de sinal que permaneça definido sem condições de corrida e que não seja interrompido pela maioria dos outros sinais.

Escreva sua função de tratamento de sinal com muito, muito cuidado. Basicamente, apenas defina uma variável global para indicar que um sinal foi capturado e o restante do processo lide com a ação desejada para esse sinal. Os sinais serão mascarados pelo menor tempo possível.

Além disso, você desejará testar seu código de manipulação de sinais com muito cuidado. Coloque-o em um pequeno processo de teste e envie o maior número possível de sinais SIGUSR1 e SIGUSR2, talvez de 2 ou 3 programas de envio de sinal para fins especiais. Misture alguns outros sinais também, depois de ter certeza de que seu código pode lidar com SIGUSR1 e SIGUSR2 de maneira rápida e correta. Prepare-se para depuração difícil.

Se você estiver usando linux e somente linux, pode pensar em usar signalfd()para criar um descritor de arquivo que possa ser select()pesquisado para receber esses sinais. O uso signalfd()pode facilitar a depuração.

Bruce Ediger
fonte
2
Não é apenas o SIGCLD que se une: todos os sinais são potencialmente unidos se forem entregues antes de serem processados.
Gilles 'SO- stop be evil'
Existe alguma medida de quanto tempo é "muito longo" para sinais SIGCHLD? Estou enfrentando esse comportamento no meu programa no momento e meu manipulador de sinais não leva mais do que ~ 100ms, eu diria.
xrisk
@Rishav - que eu saiba, não há como descobrir o que é "muito tempo". Eu esperaria que a carga geral do sistema seja importante. Ou seja, o que outros processos e o kernel estão fazendo afetariam "quanto tempo" entre os sinais para que eles se unissem. Não é uma resposta útil, eu acho.
Bruce Ediger
6

É garantido que um sinal seja entregue, no sentido de que se um processo chamar com êxito kill, o alvo receberá o sinal. Isso é assíncrono: o remetente não tem como saber quando o sinal é recebido ou processado. No entanto, isso não garante que o sinal seja entregue. O alvo pode morrer antes de poder processar o sinal. Se o alvo estiver ignorando o sinal no momento em que é entregue, o sinal não terá efeito. Se o alvo receber várias instâncias do mesmo número de sinal antes de processá-las, os sinais podem (e geralmente são) mesclados: se você enviar o mesmo sinal duas vezes para um processo, não poderá saber se o processo receberá o sinal. uma vez ou duas vezes. Os sinais são projetados principalmente para matar um processo ou, como forma de fazer com que um processo preste atenção, eles não são projetados para comunicação como tal.

Se você precisar de entrega confiável, precisará de um mecanismo de comunicação diferente. Existem dois mecanismos principais de comunicação entre processos: um tubo permite a comunicação unidirecional; um soquete permite comunicação bidirecional e várias conexões com o mesmo servidor. Se você precisar que o destino processe quantas notificações forem enviadas, envie bytes por um canal.

Gilles 'SO- parar de ser mau'
fonte
4
Você quis escrever "Um sinal é garantido para ser entregue" desde que você descreveu algumas das maneiras pelas quais o sinal não seria entregue (ou seja, o processo morreu antes de ser recebido ou os sinais foram coalescidos)?
Johnny
2

O kernel é livre para unir sinais padrão se mais de um for entregue enquanto bloqueado. Sinais em tempo real, por outro lado, não são igualmente prejudicados.

Na página de manual do sinal (7) :

Os sinais em tempo real são diferenciados pelo seguinte:

  1. Várias instâncias de sinais em tempo real podem ser colocadas na fila. Por outro lado, se várias instâncias de um sinal padrão forem entregues enquanto esse sinal estiver bloqueado no momento, apenas uma instância será colocada na fila.

Tente usar um sinal com um número no intervalo SIGRTMIN a SIGRTMAX.

Graham Rushton
fonte
Há um limite para sinais em tempo real, mas é bastante alto. Um sinal será descartado se o número de sinais pendentes pendentes enviados pelo usuário exceder RLIMIT_SIGPENDING. ulimit -imostra esse valor como 63432 no Ubuntu 18.04.
bain