Read() из stdin

Рассмотрим следующую строку кода:

while((n = read(STDIN_FILENO, buff, BUFSIZ)) > 0)

В соответствии с моим пониманием read/write функции являются частью небуферизованного ввода-вывода. Значит ли это, что функция read() будет читать только один символ за звонок из stdio? Или, другими словами, значение n будет

    -1  in case of error
n =  0  in case of EOF
     1  otherwise

Если это не так, когда вернется предыдущая функция read() и почему?

Примечание. Я также думал, что read() будет ждать, пока он успешно прочитает BUFSIZ количество символов из stdin. Но что происходит в случае, количество доступных для чтения символов меньше BUFSIZ? Будет ли читать навсегда или до тех пор, пока EOF не придет (Ctrl + D в unix или Ctrl + Z на окнах)?

Кроме того, скажем BUFSIZ = 100 и stdin = ACtrl+D (например, EOF сразу после одного символа). Теперь сколько раз while loop будет повторяться?

Ответы

Ответ 1

Поведение read() зависит от того, что читается. Для обычных файлов, если вы запрашиваете N символов, вы получаете N символов, если они доступны, меньше N, если вмешивается конец файла.

Если read() читает с терминала в каноническом/готовом режиме, драйвер tty предоставляет данные по одной строке за раз. Поэтому, если вы скажете read(), чтобы получить 3 символа или 300, read будет зависать до тех пор, пока драйвер tty не увидит символ новой строки или не определит терминал ключом EOF, а затем read() вернется либо с количеством символов в строке, либо с количество символов, которое вы запросили, в зависимости от того, что меньше.

Если read() читает с терминала в неканоническом/необработанном режиме, read сразу же получит доступ к нажатию клавиш. Если вы попросите read() получить 3 символа, он может вернуть от 0 до 3 символов в зависимости от времени ввода и конфигурации терминала.

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

read() будет вести себя иначе, если дескриптор был настроен для неблокирующего ввода-вывода. read() вернет -1 с errno, установленным в EAGAIN или EWOULDBLOCK, если вход не был немедленно доступен. Это относится к розеткам.

Итак, как вы можете видеть, вы должны быть готовы к неожиданностям, когда вызываете read(). Вы не всегда получите количество запрошенных вами символов, и вы можете получить нефатальные ошибки, такие как EINTR, что означает, что вы должны повторить read().

Ответ 2

Ваш код гласит:

while((n = read(0, buff, BUFSIZ) != 0))

Это неверно - скобки означают, что он интерпретируется как:

while ((n = (read(0, buff, BUFSIZ) != 0)) != 0)

где логическое условие оценивается перед назначением, поэтому n будет получать только значения 0 (условие не верно) и 1 (условие истинно).

Вы должны написать:

while ((n = read(0, buff, BUFSIZ)) > 0)

Это останавливается на EOF или ошибке чтения, а n позволяет узнать, с каким состоянием вы столкнулись.


По-видимому, вышеприведенный код был опечаткой в ​​вопросе.

Небуферизованный ввод-вывод будет считывать число символов, которые вы читаете (но не более). Он может читать меньше за счет EOF или ошибки. Он также может читать меньше, потому что меньше времени доступно во время разговора. Рассмотрим терминал; как правило, это будет читать только до конца строки, потому что нет более доступного, чем это. Рассмотрим трубу; если процесс подачи генерировал 128 непрочитанных байтов, тогда, если BUFSIZ равен 4096, вы получите только 128 байтов от чтения. Неблокирующий файловый дескриптор может вернуться, потому что ничего не доступно; сокет может вернуть меньшее количество байтов, поскольку пока еще нет информации; чтение диска может возвращать меньшее количество байтов, потому что в процессе чтения выполняется меньше запрашиваемого количества байтов, оставшихся в файле.

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

Ответ 3

Как говорится в read() manpage:

Возвращаемое значение

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

Итак, каждый read() будет считывать количество указанных байтов; но он может читать меньше. "Без буферизации" означает, что если вы укажете read(fd, bar, 1), чтение будет читать только один байт. Буферизованный IO пытается читать в квантах BUFSIZ, даже если вам нужен только один символ. Это может показаться расточительным, но это позволяет избежать накладных расходов на выполнение системных вызовов, что делает его быстрым.

Ответ 4

  • прочитайте попытки получить все запрошенные символы.
  • если EOF происходит до того, как все запрошенные символы могут быть возвращены, он возвращает то, что получил после этого следующее чтение возвращает -1, чтобы сообщить вам о завершении файла.

Что происходит, когда он пытается читать, и в нем нет ничего, что связано с блокировкой. Вы можете вызвать open, чтобы прочитать блокировку или неблокирование файла. "блокировка" означает ждать, пока что-то вернется.

Это то, что вы видите в оболочке, ожидающей ввода. Он сидит там. Пока вы не вернетесь.

Неблокирование означает, что чтение не будет возвращать байты данных, если их нет. В зависимости от множества других факторов, которые могли бы сделать совершенно правильный ответ непригодным для вас, read установит errno на что-то вроде EWOULDBLOCK, которое позволит вам узнать, почему ваше чтение вернуло нулевые байты. Это не обязательно фатальная ошибка.

Ваш код может проверить минус, чтобы найти EOF или ошибки

Ответ 5

Когда мы говорим, что read небуферизован, это означает, что на уровне вашего процесса не происходит буферизация после того, как данные будут удалены из описания открытого файла, который является потенциально разделяемым ресурсом. Если stdin является терминалом, в игре, вероятно, есть хотя бы 2 дополнительных буфера:

  • Терминальный буфер, который, вероятно, может содержать 1-4 тыс. данных от линии до.
  • Буфер для ядра/канонического режима для ввода/редактирования строки на терминале, который позволяет пользователю выполнять примитивное редактирование (backspace, backword, erase line и т.д.) на линии до его отправки (в буфер, описанный выше) нажав enter.

read вытащит все, что уже было отправлено, вплоть до максимальной длины чтения, которую вы передали, но не может извлечь что-либо из буфера редактирования строк. Если вы хотите отключить этот дополнительный уровень буферизации, вам нужно найти способ отключения готового/канонического режима для терминала с помощью tcsetattr и т.д.