Путаница в том, как цикл getchar() работает внутренне


Я включил пример программы, использующей getchar() ниже, Для справки (не то, что кто-то, вероятно, нуждается в этом), и не стесняйтесь обращаться к нему, если вы хотите. Но мой вопрос таков:

Что именно происходит, когда программа вызывает getchar()?

Вот мое понимание (пожалуйста, уточните или поправьте меня):

  1. когда вызывается getchar, он проверяет буфер STDIN, чтобы увидеть, есть ли какие-либо входные данные.
  2. Если нет никаких входных данных, getchar сны.
  3. После пробуждения getchar проверяет, есть ли какой-либо вход, и если нет, снова погружает себя в сон.
  4. шаги 2 и 3 повторяйте до тех пор, пока не появится вход.
  5. Как только есть вход (который по соглашению включает 'EOF' в конце), getchar возвращает первый символ этого входа и делает что-то, чтобы указать, что следующий вызов getchar должен вернуть вторую букву из того же буфера? Я не совсем понимаю, что это такое.
  6. когда нет больше символов осталось, кроме EOF, а getchar стирает буфер?
Термины, которые я использовал, вероятно, не совсем корректны.
#include <stdio.h>

int getLine(char buffer[], int maxChars);

#define MAX_LINE_LENGTH 80

int main(void){

    char line[MAX_LINE_LENGTH];
    int errorCode;

    errorCode = getLine(line, sizeof(line));
    if(errorCode == 1)
        printf("Input exceeded maximum line length of %d characters.\n", MAX_LINE_LENGTH);
    printf("%s\n", line);

    return 0;

}

int getLine(char buffer[], int maxChars){
    int c, i = 0;
    while((c = getchar()) != EOF && c != '\n' && i < maxChars - 1)
        buffer[i++] = c;
    buffer[i++] = '\0';
    if(i == maxChars)
        return 1;
    else
        return 0;
}
1   3  

1 ответ:

Шаг 2-4 немного отклонен.

Если в стандартном буфере ввода-вывода нет входных данных, getchar() вызывает функцию для перезагрузки буфера. В Unix-подобной системе это обычно заканчивается вызовом системного вызова read(), и системный вызов read() переводит процесс в спящий режим до тех пор, пока не появится вход, подлежащий обработке, или ядро не будет знать, что вход не будет обработан (EOF). Когда чтение возвращается, код настраивает структуры данных так, чтобы getchar() знал, сколько данных доступно. Вы описание подразумевает опрос; стандартная система ввода-вывода не опрашивает для ввода.

Шаг 5 использует скорректированные указатели для возврата правильных значений.

На самом деле не существует символа EOF; это состояние, а не символ. Даже если вы вводите Control-D или Control-Z, чтобы указать 'EOF', этот символ не вставляется во входной поток. На самом деле, эти символы заставляют систему удалять все типизированные символы, которые все еще ждут операций редактирования строки (например, backspace), чтобы изменить их так, чтобы они были доступны для системного вызова read(). Если таких символов нет, то read() возвращает 0 как количество доступных символов, что означает EOF. Затем getchar() возвращает значение EOF (обычно -1, но гарантированно отрицательное, тогда как допустимые символы гарантированно неотрицательны (нулевые или положительные)).

Таким образом, в основном, вместо опроса, это то, что нажатие Return вызывает определенное прерывание ввода-вывода, а затем, когда ОС получает это, она будит любые процессы, которые спят для ввода-вывода?

Да, нажатие Return вызывает прерывания, и ядро ОС обрабатывает их и пробуждает процессы, ожидающие данных. Драйвер терминала пробуждается ядром при возникновении прерывания и решает, что делать с символами, которые были только что получены. Они могут быть спрятаны для дальнейшей обработки (канонический режим) или сразу же доступны (необработанный режим) и т. д. Предполагая, конечно, что вход-это терминал; если вход - это дисковый файл, это проще во многих отношениях - или если это канал, или ...

Номинально, это не приложение терминала, которое просыпается от прерывания; это ядро, которое просыпается первым, а затем оболочка, запущенная в приложении терминала, которая просыпается, потому что есть данные для чтения, и только когда есть выход, приложение терминала просыпается.

Я говорю "номинально", потому что есть внешний шанс, что на самом деле приложение терминала действительно опосредует ввод-вывод через pty (псевдо-tty), но я думаю, что это происходит на уровне ядра, и терминальное приложение участвует довольно поздно в этом процессе. Существует огромная разница между клавиатурой, на которой вы печатаете, и дисплеем, на котором появляется то, что вы печатаете.

Читайте также против канонических неканонических вход.