Ctrl-C съеденный getchar () - PullRequest
       29

Ctrl-C съеденный getchar ()

4 голосов
/ 02 ноября 2011

Я давно ищу решение своей проблемы, поэтому обращаюсь к вам:

Рассмотрим этот кусок кода:

static char done = 0;
static void sigHandler(void)
{
    done = 1;
}

int user_input()
{
    return (getchar() == 'q') ? 0 : 1;
}

int main(void)
{
    signal(SIGTERM, sigHandler);
    signal(SIGINT, sigHandler);
    while (user_input() != 0 && !done)
        usleep(1000);
    printf("exiting\n");
    return 0;
}

Ожидаемое поведение: Программа завершается, когда пользователь вводит q, затем вводит. Если нажата CTRL + C , она отлавливается функцией sigHandler, которая устанавливает флаг 'done' в 1 и выходит из программы.

Наблюдаемое поведение: Символ CTRL + C съедается вызовом getchar (), а функция sigHandler никогда не выполняется. При нажатии CTRL + C и последующем вводе вызывается функция sigHandler, и программа завершается.

Может ли кто-нибудь с большим опытом и знаниями помочь мне в этом?

Спасибо за ваш вклад:)

Ответы [ 3 ]

6 голосов
/ 02 ноября 2011

Символ Ctrl-C съедается вызовом getchar(), а функция sigHandler никогда не выполняется.

Ctrl-C не съедается getchar;это приводит к доставке сигнала и запуску sigHandler.Это устанавливает done и возвращает. Только тогда вызывается getchar, который съедает символ новой строки, а после этого проверяется , done, поэтому программа завершается.

Кстати, обработчик сигналапринимает аргумент int, а не void.

6 голосов
/ 02 ноября 2011

Код на самом деле работает должным образом - вы не проверяете флаг done до тех пор, пока не вернетесь из user_input(), поэтому вам нужно ввести дополнительный символ после элемента управления-C.

Если вы хотите прервать вызов на getchar, когда получите элемент управления C, вам, вероятно, придется сделать что-то ужасное, например. используйте setjmp / longjmp.

4 голосов
/ 02 ноября 2011

Существует способ прервать вызов, не прибегая к безобразным хакерским атакам (в противоположность тому, что сказал Пол Р.).Вам следует использовать sigaction() с sa_flags, установленным на 0 вместо signal().

Кроме того, в руководстве к сигналу (2) сказано:

Избегатьего использование : вместо этого используйте sigaction (2).

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

static char done = 0;
static void sigHandler(int signum)
{
    done = 1;
}

int user_input()
{
    return (getchar() == 'q') ? 0 : 1;
}

int main(void)
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = sigHandler;
    sa.sa_flags = 0;// not SA_RESTART!;

    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);

    while (user_input() != 0 && !done)
        usleep(1000);
    printf("exiting\n");
    return 0;
}

Обычно, после перехвата и обработки сигнала, большинство (я не уверен, что не все) системные вызовы будут перезапущены.Таким образом, после обработки сигнала sigint ваша функция getchar будет продолжаться, как будто ничего не произошло.Вы можете изменить это поведение, вызвав sigaction с помощью sa_flags=0.

. Таким образом, после обработки SIGINT, getchar вернет -1 и для errno будет установлено значение «Прерванный системный вызов» (я незапомните имя константы прямо сейчас.

...