Системное программирование в UNIX средствами Free Pascal

       

Системный вызов fdread


При использовании файла терминального устройства вместо обычного дискового файла изменения больше всего затрагивают работу системного вызова read. Это особенно проявляется, если терминал находится в каноническом режиме, устанавливаемом по умолчанию для обычного интерактивного использования. В этом режиме основной единицей ввода становится строка. Следовательно, программа не может считать символы из строки, пока пользователь не нажмет на клавишу Return, которая интерпретируется системой как переход на новую строку. Важно также, что после ввода новой строки всегда происходит возврат из вызова fdread, даже если число символов в строке меньше, чем число символов, запрошенное вызовом fdread. Если вводится только Return и системе посылается пустая строка, то соответствующий вызов

fdread вернет значение 1, так как сам символ новой строки тоже доступен программе. Поэтому нулевое значение, как и обычно, может использоваться для определения конца файла (то есть ввода символа еof).

Использование вызова fdread для чтения из терминала в программе io уже рассматривалось в главе 2. Тем не менее эта тема нуждается в более подробном объяснении, поэтому рассмотрим следующий вызов:

nread := fdread(0, buffer, 256);

При извлечении стандартного ввода процесса из обычного файла вызов интерпретируется просто: если в файле более 256 символов, то вызов fdread вернет в точности 256 символов в массиве buffer. Чтение из терминала происходит в несколько этапов – чтобы продемонстрировать это, обратимся к рис. 9.3, который показывает взаимодействие между программой и пользователем в процессе вызова fdread.

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

1

Строка ввода >

echo



(Вызов read)

Буфер чтения >

2

echo q <erase>

3

echo hello

4

echo hello <nl>

v (Данные перемещаются в буфер чтения)

5

(Возврат из вызова read)

echo hello <nl>

6

(Теперь буфер пуст)

<


Рис. 9.3. Этапы чтения из терминала в каноническом режиме

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

На шаге 2 пользователь напечатал q, затем передумал и набрал символ erase для удаления символа из строки ввода. Эта часть схемы подчеркивает, что редактирование строки ввода может выполняться, не затрагивая программу, выполняющую вызов read.

На шаге 3 строка ввода завершена, только еще не содержит символ перехода на новую строку. Часть схемы, обозначенная как шаг 4, показывает состояние в момент ввода символа перехода на новую строку, при этом драйвер терминала передает строку ввода, включая символ перевода строки, в буфер чтения. Это приводит к шагу 5, на котором вся строка ввода становится доступной для чтения. В процессе, выполнившем вызов read, происходит возврат из этого вызова, при этом число введенных символов nread равно 11. Шаг 6 показывает ситуацию сразу же после такого завершения вызова, при этом и строка ввода, и буфер чтения снова временно пусты.

Следующий пример подкрепляет эти рассуждения. Он основан на простой программе read_demo, которая имеет лишь одну особенность: она использует небольшой размер буфера для приема байтов из стандартного ввода.

(* Программа read_demo - вызов fdread для терминала *)

uses linux;

const

  SMALLSZ=10;

var

  nread:longint;

  smallbuf:array [0..SMALLSZ] of char;

begin

  nread := fdread (0, smallbuf, SMALLSZ);

  while nread > 0 do

  begin

    smallbuf[nread] := #0;

    writeln('nread: ',nread,' ', smallbuf);

    nread := fdread (0, smallbuf, SMALLSZ);

  end;

end.

Если подать на вход программы следующий терминальный ввод

1

1234

Это более длинная строка

<EOF>

то получится такой диалог:

1

nread: 2 1

1234

nread: 5 1234

Это более длинная строка

nread: 10 Это более

nread: 10 длинная с

nread: 6 трока

Обратите внимание, что для чтения самой длинной строки требуется несколько последовательных операций чтения. Заметим также, что значения счетчика nread включают также символ перехода на новую строку. Это не показано для простоты изложения.

Что происходит, если терминал не находится в каноническом режиме? В таком случае для полного управления процессом ввода программа должна устанавливать дополнительные параметры состояния терминала. Это осуществляется при помощи семейства системных вызовов, которые будут описаны позже.

Упражнение 9.1. Попробуйте запустить программу read_demo, перенаправив ее ввод на чтение файла.


Содержание раздела