Причина получает () для выхода на SIGINT в C

Я пишу простую программу на C, которая читает по одной строке текста за раз с клавиатуры (в цикле), используя функцию gets().

Цикл должен немедленно завершиться, если пользователь нажимает CTRL-C. Я знаю, как написать обработчик sig, который может установить флаг для выхода из цикла... но как мне сделать так, чтобы функция gets() сразу же возвращалась при поступлении этого сигнала?

1 ответ

Решение

Вы должны использовать select() чтобы проверить, доступны ли данные перед чтением из stdin:

void handler (int arg) {
    // handle signal
}

int main (void) {
    char buf[100];
    signal(SIGINT, handler);
    do {
       fd_set f;
       FD_ZERO(&f);
       FD_SET(fileno(stdin), &f);
       int st = select(fileno(stdin)+1, &f, NULL, NULL, NULL);
       if (st == 1) {
          fgets(buf, sizeof(buf), stdin);
          // Handle input
       }
    } while (st >= 0);

    exit(EXIT_SUCCESS);
}

select() вернется с -1 (errno EINTR) после возврата из обработчика сигнала, тогда как read() (вызывается (f) gets) будет автоматически возобновлено.

С Linux есть еще одна возможность использования sigaction():

void handler (int arg) {
    // handle signal
}

int main (void) {
    char buf[100], *p;
    struct sigaction sa = { .sa_handler = handler, .sa_flags = 0, .sa_mask = 0 };
    sigaction(SIGINT, &sa, NULL);

    do {    
            p = fgets(buf, sizeof(buf), stdin);
            // handle input
    } while (p);
    printf("Byebye\n");

    exit(EXIT_SUCCESS);
}

Так как флаг SA_RESTART не указан в sa_flags для обработчика сигнала, read() на медленном устройстве (то есть клеммы, трубы, розетки) вернется с -1 (EINTR)тоже (см. также сигнал (7)).

Хотя это немного проще, поведение может отличаться в зависимости от различных версий Unix (и даже версий ядра), поэтому первая версия является более безопасным и более переносимым / надежным способом.

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