Сигнал POSIX блокируется в обработчике сигналов, несмотря на то, что он не находится в sa_mask - PullRequest
0 голосов
/ 17 января 2020

Я вчера опубликовал похожий вопрос , но я плохо описал свою проблему, и с тех пор я думаю, что добился прогресса.

Мой минимальный рабочий пример все еще довольно длинный, поэтому Я опубликую соответствующие фрагменты, но полный пример можно найти здесь .

Моя проблема довольно проста, у меня есть две очереди сообщений POSIX, которые созданы асинхронными и обе обрабатываются тот же обработчик в том же потоке. Моя проблема на более фундаментальном уровне в том, что если отдельный поток отправляет в обе очереди последовательно, то обработчик sig запускается только один раз для первой очереди. Это имеет смысл, учитывая, что когда сигнал вызывает обработчик, он автоматически блокируется в соответствии с GNU .

Поэтому при настройке struct sigaction я убрал целевой сигнал (SIGIO) из sigset_t, который я установил как sa_mask. Я предполагал, что затем, используя SA_NODEFER, как объяснено в sigaction (2) , обработчик сигнала сможет вызываться рекурсивно (не уверен, что рекурсивное слово здесь является правильным).

sa_mask определяет маску сигналов, которые должны быть заблокированы (т.е. добавлены к маске сигналов потока, в котором вызывается обработчик сигналов) во время выполнения обработчика сигналов. Кроме того, сигнал, вызвавший обработчик, будет заблокирован, если только не используется флаг SA_NODEFER.

Соответствующий код для присоединения обработчика сигнала к очереди сообщений

assert((conn->fd = mq_open(conn->name, O_CREAT | O_RDONLY | O_NONBLOCK,
               0644, &attr)));

/** Setup handler for SIGIO */
/** sigaction(2) specifies that the triggering signal is blocked in the handler  */
/**     unless SA_NODEFER is specified */
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER; 
sa.sa_sigaction = sigHandler;
/** sa_mask specifies signals that will be blocked in the thread the signal  */
/**     handler executes in */
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGIO);
if (sigaction(SIGIO, &sa, NULL)) {
    printf("Sigaction failed\n");
    goto error;
}

printf("Handler set in PID: %d for TID: %d\n", getpid(), gettid());

/** fcntl(2) - FN_SETOWN_EX is used to target SIGIO and SIGURG signals to a  */
/**     particular thread */
struct f_owner_ex cur_tid = { .type = F_OWNER_TID, .pid = gettid() };
assert(-1 != fcntl(conn->fd, F_SETOWN_EX, &cur_tid));

В качестве проверки работоспособности я проверил маску сигналов внутри обработчика, чтобы проверить, не заблокирован ли SIGIO.

void sigHandler(int signal, siginfo_t *info, void *context)
{
    sigset_t sigs;
    sigemptyset(&sigs);
    pthread_sigmask(0, NULL, &sigs);
    if (sigismember(&sigs, SIGIO)) {
        printf("SIGIO being blocked in handler\n");
        sigaddset(&sigs, SIGIO);
        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
    }
...
}

Но SIGIO, похоже, не заблокирован. Мои рассуждения говорят мне, что следующее должно произойти, учитывая две очереди сообщений MQ1 и MQ2, которые asyn c используют один и тот же обработчик на SIGIO. Учитывая временную привязку двух потоков и задержку сигналов, мне трудно понять. Лучше сказал бы, что мое кое-что образованное предположение будет:

  • mq_send до MQ1, за которым сразу следует mq_send до MQ2 из потока 1
  • Обработчик сигнала MQ1 должен сработать, учитывая SIGIO из MQ1 в потоке 2
  • Обработчик сигнала MQ2 прервет обработчик сигнала MQ1 в потоке 2
  • Обработчик сигнала MQ2 завершится в потоке 2
  • Обработчик сигнала MQ1 завершится в потоке 2

При выполнении примера, который я связал ранее, наблюдается следующее поведение

  • mq_send для MQ1, за которым следуют mq_send для MQ2 из потока 1
  • MQ1 обработчик сигнала запускается и завершается

, что заставляет меня думать, что SIGIO каким-то образом блокируется или игнорируется во время обработки сигнала. Учитывая то, что я прочитал о sa_mask и о моей проверке работоспособности с использованием pthread_sigmask, я не уверен, почему у меня такое поведение, которое я вижу. Я надеюсь, что пропустил какой-то маленький кусочек знания где-то на страницах руководства.

1 Ответ

3 голосов
/ 19 января 2020

Моя проблема на более фундаментальном уровне в том, что если отдельный поток отправляет в обе очереди последовательно, то обработчик sig запускается только один раз для первой очереди ... Что заставляет меня думать, что каким-то образом SIGIO блокируется или игнорируется во время обработки сигнала.

SIGIO - это стандартный сигнал, а не сигнал реального времени. Из POSIX Signal Concepts :

В течение времени между генерацией сигнала и его доставкой или приемом сигнал называется "ожидающим". Обычно этот интервал не может быть обнаружен приложением.

...

Если генерируется последующее вхождение ожидающего сигнала, это определяется реализацией относительно того, доставлен ли сигнал или принимается более одного раза при обстоятельствах, отличных от тех, в которых требуется организация очереди. Порядок, в котором несколько одновременно ожидающих сигналов за пределами диапазона SIGRTMIN до SIGRTMAX доставляются или принимаются процессом, не определен.

При Linux стандартные сигналы не ставятся в очередь, скорее они сбрасываются, когда кто-то уже ожидает. Начиная с Linux man signal(7):

Семантика очереди и доставки для стандартных сигналов

Если несколько стандартных сигналов ожидают В этом случае порядок доставки сигналов не определен.

Стандартные сигналы не ставятся в очередь. Если несколько экземпляров стандартного сигнала генерируются, когда этот сигнал заблокирован, то только один экземпляр сигнала помечается как ожидающий (и сигнал будет доставлен только один раз, когда он разблокирован). В случае, когда стандартный сигнал уже находится на рассмотрении, структура siginfo_t (см. Sigaction (2)), связанная с этим сигналом, не перезаписывается при поступлении последующих экземпляров того же сигнала. Таким образом, процесс получит информацию, связанную с первым экземпляром сигнала.


Одним из способов решения вашей проблемы будет использование SIGEV_THREAD уведомлений вместо SIGEV_SIGNAL, так что ваш обратный вызов вызывается другим потоком. Это также устраняет ограничение обработчиков сигналов, когда вы можете вызывать только asyn c -signal-safe функции .

...