Проблема с системным вызовом Sigsuspend

sigfillset(&set);
sigdelset(&set, SIGUSR2);

sigsuspend(&set);

// signal handler sets a flag

if(flag == 1)
    //do something

В этом случае мой поток просыпается только при доставке SIGUSR2. Однако кажется, что что-то еще заставляет sigsuspend вернуться, но я не знаю что, поскольку все остальные сигналы маскируются. Есть ли проблема в этом фрагменте кода?

Большое тебе спасибо


person riri jiji    schedule 26.06.2015    source источник
comment
Покажите нам MCVE.   -  person pilcrow    schedule 26.06.2015


Ответы (1)


Трудно сказать, что именно не так, не видя остальной части кода, но в коде, который вы показываете, есть по крайней мере одна проблема:

Перед возвратом sigsuspend(2) восстанавливает маску сигнала, которая была активной при вызове. Если вы не изменили маску сигнала процесса перед вызовом sigsuspend(2), то между возвратом из sigsuspend(2) и проверкой значения flag есть промежуток времени, в течение которого могут быть доставлены другие отложенные сигналы (или даже может случиться так, что новые сигналы создаются и доставляются). Если эти сигналы перехватываются и обработчик меняет значение flag на другое значение, вы «теряете» обновление, которое сделал обработчик SIGUSR2.

Например, рассмотрим эту программу:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

volatile sig_atomic_t flag;

void handler1(int signo) {
    printf("In handler1\n");
    flag = 1;
}

void handler2(int signo) {
    printf("In handler2\n");
    flag = 2;
}

int main(void) {
    struct sigaction sigact;
    sigact.sa_handler = handler1;
    sigact.sa_flags = 0;
    sigfillset(&sigact.sa_mask);

    if (sigaction(SIGUSR1, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(EXIT_FAILURE);
    }

    sigact.sa_handler = handler2;

    if (sigaction(SIGUSR2, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(EXIT_FAILURE);
    }

    sigset_t mask;
    sigfillset(&mask);
    sigdelset(&mask, SIGUSR1);

    sigsuspend(&mask);

    printf("%d\n", flag);

    return 0;
}

Он ловит SIGUSR1 и SIGUSR2, блокирует все сигналы, кроме SIGUSR1, а затем вызывает sigsuspend(2).

Запустите его, а затем отправьте SIGUSR2, а затем SIGUSR1. Это гарантирует наличие ожидающего сигнала до того, как SIGUSR1 будет замечено и обработано. Это также приводит к доставке SIGUSR2, как только SIGUSR2 разблокируется, что происходит после возврата sigsuspend(2) (поскольку мы не коснулись маски сигнала процесса):

filipe@filipe-Kubuntu:~/dev$ kill -SIGUSR2 29755
filipe@filipe-Kubuntu:~/dev$ kill -SIGUSR1 29755

Выход:

filipe@filipe-Kubuntu:~/dev$ ./a.out 
In handler1
In handler2
2

Как это исправить? Просто убедитесь, что маска сигнала процесса установлена ​​на ту же маску, которую вы используете в sigsuspend(2), чтобы по ошибке не восстановить маску. Это так же просто, как добавить это перед вызовом sigsuspend(2):

if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0) {
    perror("sigprocmask()");
    exit(EXIT_FAILURE);
}

Итак, обновленная программа:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

volatile sig_atomic_t flag;

void handler1(int signo) {
    printf("In handler1\n");
    flag = 1;
}

void handler2(int signo) {
    printf("In handler2\n");
    flag = 2;
}

int main(void) {
    struct sigaction sigact;
    sigact.sa_handler = handler1;
    sigact.sa_flags = 0;
    sigfillset(&sigact.sa_mask);

    if (sigaction(SIGUSR1, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(EXIT_FAILURE);
    }

    sigact.sa_handler = handler2;

    if (sigaction(SIGUSR2, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(EXIT_FAILURE);
    }

    sigset_t mask;
    sigfillset(&mask);
    sigdelset(&mask, SIGUSR1);

    if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0) {
        perror("sigprocmask()");
        exit(EXIT_FAILURE);
    }

    sigsuspend(&mask);

    printf("%d\n", flag);

    return 0;
}

Это работает так, как ожидалось: гарантируется, что никакие другие обработчики сигналов не будут выполняться между окном времени после возврата sigsuspend(2) и проверкой flag.

Еще одно важное замечание: я не знаю, как вы настраиваете обработчик сигнала, но если вы используете signal(2), то не делайте этого: его семантика зависит от платформы, и он может вести себя не так, как вы хотите. Вы должны использовать sigaction(2) точно так, как я показываю в примере программы. Строка sigfillset(&sigact.sa_mask) необходима для того, чтобы убедиться, что во время работы обработчика сигнала все остальные сигналы по-прежнему заблокированы, иначе доставка сигнала в разгар выполнения обработчика SIGUSR1 может вызвать другой обработчик, который изменил флаг.

person Filipe Gonçalves    schedule 27.06.2015