обрабатывать SIGINT с помощью scanf в цикле

Я должен получить ввод от пользователя в цикле while, а затем предпринять какие-то действия. И я также хочу выйти из своего кода при вводе Ctrl + C.

void my_signal_handler(int sig)
{
    running = false;
    signal(sig, SIG_IGN);
}
int main(void)
{
    struct sigaction sa = {{0}};
    sa.sa_handler = &my_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    if (sigaction(SIGINT, &sa, NULL) != 0)
    {
        fprintf(stderr, "sigaction error\n");
        return -1;
    }
    while(running)
    {
        printf("enter number: ");
        scanf("%d", &num);
        // take action based on  number
    }
}

Проблема с этим кодом в том, что после нажатия ctrl+c он не выходит, но ожидает ввода для scanf. Поэтому, как только я нажимаю дополнительную клавишу, программа завершается (вызывается обработчик сигнала).

Как я могу удалить этот дополнительный шаг ввода данных в scanf после нажатия ctrl+c?


person Patrick    schedule 28.06.2016    source источник
comment
Не могли бы вы улучшить свой вопрос, мотивировав его (поэтому, пожалуйста, отредактируйте свой вопрос), пожалуйста?   -  person Basile Starynkevitch    schedule 28.06.2016
comment
Кстати, данный код не компилируется. В scanf(%d", &num); отсутствуют двойные кавычки.   -  person Basile Starynkevitch    schedule 29.06.2016


Ответы (1)


Вы должны установить обработчик сигнала. Как минимум добавить

signal(SIGINT, my_signal_handler);

внутри вашего main перед while(running), но лучше использовать подпись(2).

Вы также должны знать, что stdio выполняет буферизацию; обычно stdout буферизуется строкой, когда это терминал (но см. setvbuf(3 ) и друзья). Поэтому вам следует либо вызвать fflush(3) (вероятно, как fflush(NULL); ) перед scanf внутри цикла или заканчивайте каждую строку управления форматом printf явным \n.

Наконец, scanf(3) может дать сбой и возвращает количество отсканированных элементов, которые вы должны протестировать.

Кстати, ваш  main неверен, его следует определить как int main(void) или, что предпочтительнее, int main(int argc, char**argv).

Однако (при условии, что вы работаете в Linux) очень внимательно прочитайте signal(7) (обратите внимание на то, что сказано об обработчиках сигналов и безопасных функциях асинхронных сигналов) и POSIX signal.h документация и объявите свой флаг running как

volatile sigatomic_t running;

(или, возможно, в C11 как volatile _Atomic bool running;)

Очень важен volatile квалификатор. В противном случае компилятору разрешено оптимизировать (и, например, притвориться, что running всегда верно).

Обратите внимание, что использование signal(2) часто является плохой идеей. Во-первых, если вам действительно нужна обработка сигналов, вам лучше использовать sigaction(2 ). Тогда ваш вызов signal(sig, SIG_IGN); в вашем случае бесполезен (поскольку флаг volatile running был бы изменен в обработчике сигнала). Наконец, для мультиплексирования ввода (и вывода) вы можете использовать poll(2 ), который можно использовать для ожидания и проверки наличия каких-либо доступных входных данных для stdin (на самом деле STDIN_FILENO, что равно 0), и в более общем плане для реализации циклы событий. Вы можете использовать (вместо poll, который я настоятельно рекомендую) старый и почти устаревший select(2), но лучше использовать poll( 2). См. также epoll(7) и inotify(7), но, вероятно, они вам не нужны.

Имейте в виду, что в терминалах stdin часто является tty (проверьте это с помощью isatty( 3)) следуя линейной дисциплине (поэтому происходит некоторая буферизация строк внутри ядра). Прочтите страницу разоблачение tty. Рассмотрите возможность использования библиотеки и функции GNU readline (или, возможно, ncurses), возможно, это то, что вам действительно нужно.

Прочтите также Продвинутое программирование в Linux и возьмите за привычку читать документацию по каждой используемой вами функции.

person Basile Starynkevitch    schedule 28.06.2016
comment
На самом деле в строке //конфигурации обработчика сигналов я добавил правильный код для sigaction. Я еще раз обновлю код, чтобы показать мой актуальный_код. - person Patrick; 28.06.2016
comment
@BS Можете ли вы показать пример кода мультиплексирования ввода (и вывода) этой строки, вы можете использовать опрос (2), который можно использовать для ожидания и проверки наличия какого-либо доступного ввода на стандартный ввод (на самом деле STDIN_FILENO, который равен 0). - person Patrick; 28.06.2016
comment
Это может занять слишком много времени с моей стороны (особенно если вы не очень хорошо знакомы с программированием Linux). Я давал ссылки, особенно книгу ALP - person Basile Starynkevitch; 28.06.2016