2009-07-26 6 views
3

У меня есть небольшое приложение, написанное на C, предназначенное для работы в Linux. Часть приложения принимает пользовательский ввод с клавиатуры и использует неканонический режим терминала, чтобы он мог реагировать на каждое нажатие клавиши.Проблема с терминалом Linux с неканоническим терминальным приложением ввода/вывода

Раздел кода, который принимает входные данные является простой функцией, которая вызывается многократно в цикле:

char get_input() 
{ 
    char c = 0; 
    int res = read(input_terminal, &c, 1); 
    if (res == 0) return 0; 
    if (res == -1) { /* snip error handling */ } 
    return c; 
} 

Это считывает один символ из терминала. Если вход не получен в течение определенного таймфрейма (указанный значением c_cc [VTIME] в termios struct), read() возвращает 0, а get_input() вызывается снова.

Все это отлично работает, за исключением того, что недавно я обнаружил, что если вы запустите это приложение в окне терминала, а затем закройте окно терминала, не прекратив приложения, приложение не выйдет, а запускается в интенсивный цикл с интенсивным использованием процессора, read() непрерывно возвращает 0 без ожидания.

Итак, как я могу закрыть приложение, если оно запущено из окна терминала, а затем окно терминала закрыто? Проблема в том, что read() никогда не возвращает -1, поэтому условие ошибки неотличимо от нормального случая, когда read() возвращает 0. Таким образом, единственное решение, которое я вижу, - это поставить таймер и предположить, что существует условие ошибки, если read возвращает 0 быстрее, чем время, указанное в c_cc [V_TIME]. Но в лучшем случае это решение кажется взломанным, и я надеялся, что есть лучший способ справиться с этой ситуацией.

Любые идеи или предложения?

ответ

1

Вы ловите сигналы и перезагружаете вещи до выхода вашей программы? Я думаю, что SIGHUP - это тот, на который вам нужно сосредоточиться. Возможно, установите переключатель в обработчике сигнала, если переключатель включен при возврате из read() очистки и выхода.

0

Чтение должно возвращать 0 на EOF. То есть он ничего не будет читать. Ваша функция вернет 0 в этом случае!

Что вы должны сделать, это сравнить значение, возвращаемое с чтения с 1 и исключение процесса. I.e. вы попросили одного, но вы его получили?

Возможно, вы захотите обработать errno == EINTR, если возвращается -1.

 
char get_input() 
{ 
     char c = 0; 
     int res = read(input_terminal, &c, 1); 
     switch(res) { 
     case 1: 
       return c; 
     case 0: 
       /* EOF */ 
     case -1: 
       /* error */ 
     } 
} 
1

Вы должны обрабатывать таймаут с помощью select, а не с настройками терминала. Если терминал настроен без тайм-аута, он никогда не вернет 0 на чтение, кроме EOF.

Выбор дает вам тайм-аут, и чтение дает вам 0 при закрытии.

rc = select(...); 
if(rc > 0) { 
     char c = 0; 
     int res = read(input_terminal, &c, 1); 
     if (res == 0) {/* EOF detected, close your app ?*/} 
     if (res == -1) { /* snip error handling */ } 
     return c; 
} else if (rc == 0) { 
    /* timeout */ 
    return 0; 
} else { 
    /* handle select error */ 
}