C: Обработка сигналов и семафоры

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

sig_atomic_t running = 1;

while(running == 1) {
    if (sem_wait(server) == -1) {
        //exit and print error A
    }
    if(running == 0) {
        //exit and print error B
    }
    /* do server stuff */

    if (sem_post(client) == -1) {
        //exit and print error
    }
}

server - это имя семафора сервера, client - это имя семафора клиента (что в данном случае не имеет значения). running (которая на самом деле является глобальной) - это переменная, которую я использую в своем обработчике сигналов:

static void init_signalhandler() {
    struct sigaction sa;
    sa.sa_handler = terminate;

    if(sigemptyset(&(sa.sa_mask)) == -1) {
        bail_out(EXIT_FAILURE, "sigemptyset error");
    }

    if(sigaction(SIGINT, &sa, NULL) == -1) {
        bail_out(EXIT_FAILURE, "sigaction1 error");
    } 

    if(sigaction(SIGTERM, &sa, NULL) == -1) {
        bail_out(EXIT_FAILURE, "sigaction2 error");
    }
}

static void terminate(int e) {
    running = 0;
    sem_post(server);
}

Где bail_out - пользовательская функция печати / выхода из ошибки.

В основном, независимо от того, что я делаю, каждый раз, когда я запускаю сервер, он sem_wait(server) часть. Если я пытаюсь убить его, отправляя SIGINT, иногда он печатает ошибку A, а иногда - ошибку B. Это выглядит совершенно случайно. Это как бы вынуждает меня использовать бегущую переменную, хотя иногда семафор пропускается, а в других - нет.

1 ответ

Решение

Как вы не справляетесь с EINTR от sem_wait(3) поведение точно так, как и должно быть.

Например, ты ждешь в sem_wait(server), Вы получаете SIGINT и обращаться с этим правильно. Ваша функция sem_wait вернется -1 и ты спасешься. Что вы должны сделать вместо этого:

    if (sem_wait(s_server) == -1) {
        if(errno == EINTR) continue;
        bail_out(EXIT_FAILURE, "sem_wait(3) failed");
    }

Это отлавливает ошибки из-за сигналов в подпрограмме sem_wait, и вы можете корректно завершить работу вашего сервера, когда переходите к началу цикла, видите, что для выполнения задано значение 0, и пропустите цикл.

Смотрите страницу руководства sem_wait(3), "Возвращаемые значения" для более подробной информации о том, какие ошибки могут быть обработаны.

Другие вопросы по тегам