Novo sinal chega enquanto a execução está em um manipulador de sinal, como decidir qual é o primeiro?

1

Estou trabalhando em uma ferramenta que manipula uma quantidade enorme de sinais (com sinais diferentes ) com sigaction() .

Eu preciso lidar com o caso, se um novo sinal está chegando, enquanto o anterior estava em um manipulador de sinal. Assim, eu preciso ser capaz de lidar com a seguinte "pilha":

  1. fluxo normal do processo
  2. manipulador de signal1
  3. manipulador de signal2
  4. ... possivelmente outros manipuladores de sinal ...

(Afaik não existe uma pilha real, porque manipuladores de sinal correm em seu próprio contexto, mas é assim que eu posso ilustrar minha pergunta.)

Estou usando a API glibc2.

O problema não é impossível (eu posso passar as informações de sinal para estruturas de dados reentrantes no processo principal a serem processadas posteriormente, a partir do fluxo de execução principal), mas eu preciso de uma maneira confiável de descobrir a partir de um manipulador , se é o primeiro na "pilha" ou não.

Os sinais de mascaramento não estão bem, minimizar a perda de sinais (por exemplo, da conflação) é uma das principais prioridades.

Eu preciso do modo confiável . Usar um sigatomic_t global como spinlock também é problemático, porque não posso garantir que um novo sinal não venha logo após o início do manipulador signal1 (ainda antes de tentar adquirir o bloqueio). / p>

Depois de cavar muito em manuais e documentos glibc, eu não encontrei nenhuma maneira confiável para um manipulador de sinal descobrir se ele é o primeiro ou não. É de alguma forma possível?

    
por peterh 04.09.2015 / 21:09

3 respostas

0

Minha melhor ideia atual. Um pouco de pensamento ainda está de volta, mas tenho certeza que funcionará.

O truque é: o atomic_swap e um ponteiro global (chame isso de SigAction* top ). Ele serve como tanto de spinlock quanto como o ponteiro para o último elemento da pilha de sinais.

Assim: * se top == NULL , atomic_swap(top, myPointer) adquire o bloqueio. * se top != NULL , atomic_swap(top, myPointer) colocar o myPointer no topo, enquanto o myPointer terá o elemento superior anterior da pilha. * A pilha é uma lista encadeada, cada myPointer->next contém o próximo elemento.

SigAction* top = NULL;

void handler(SigAction* action) {
  SigAction bkp;

  restart:

  bkp = action;
  atomic_swap(&top, &action);
  if (!action) { // we acquired the lock
    run_handler(action);
    atomic_swap(&top, &action); // release the lock
    if (action!= bkp) { // if there is a new element in the stack
      action = bkp->next;
      goto restart;
    }
  } else { // the lock is not ours
    action->next = bkp;
  }
}
    
por 05.09.2015 / 03:18
2

Configure o seu manipulador de sinais para mascarar os sinais, depois desmascare-os depois de anotar que você recebeu este sinal.

volatile_t sig_atomic_t signal_count;
void mysignalhandler(int signo) {
   sig_atomic_t depth = ++signal_count;
   pending_signals.push(signo)
   if (depth > 1) return;
   sigprocmask(<unblock all signals>)

    while (!pending_signals.empty())
       /* Process pending_signals */
}

Note que há uma pequena condição de corrida entre a última verificação pending_signals.empty () e a iret. Se você tivesse algum check-in no código principal, eu deixaria assim, dado o seu grande número de sinais, ele provavelmente será processado em breve de qualquer maneira. Caso contrário, você pode marcar novamente os sinais no final e verificar se os sinais pendentes ainda estão vazios antes de retornar.

    
por 05.09.2015 / 00:30
1

Aqui está uma abordagem diferente:

Você pode receber muitos sinais diferentes, mas há um conjunto finito de sinais. Além disso, não nos importamos muito com a ordem em que eles chegaram, para que possamos simplesmente contar o número de sinais que recebemos:

long signals[SIGRTMAX];
int signal_handler(int signum) {
    signals[argc]++;

    /* Locklessly process the contents of signals */
}

gcc -O1 -masm=intel transforma em uma única instrução:

add QWORD PTR signals[0+rdi*8], 1

embora possa precisar de um prefixo LOCK caso haja vários núcleos e encadeamentos.

    
por 05.09.2015 / 01:23