Главная » Программирование для UNIX » Файловый ввод−вывод – read и write

0

Весь ввод и вывод  осуществляется двумя системными вызовами – read и write, которые доступны из языка Си через  функции с теми  же именами. В обеих  функциях первый  аргумент –  это  дескриптор файла. Второй аргумент – массив байт, служащий источником или  приемником данных. Третий аргумент задает количество передаваемых байт.

int  fd,  n,  nread,  nwritten; char  buf[SIZE];

nread  =  read(fd,  buf,  n); nwritten  =  write(fd,  buf,  n);

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

Хотя   количество байт  для   чтения и  записи  не  ограничивается, на практике чаще всего задается значение 1, то есть посимвольная передача  данных (небуферизованный ввод-вывод), и значение, равное раз меру  дискового блока, чаще всего  512  или  1024. (Это значение содержится в константе BUFSIZ в файле stdio.h.)

Проиллюстрируем вышесказанное при помощи программы, копирующей свой ввод в вывод. Так как ввод и вывод могут быть перенаправлены в любой  файл или  устройство, эта программа копирует что угодно  куда  угодно: это схематичная реализация команды cat.

/*  cat:  минимальная  версия */

#define SIZE  512 /*  произвольное значение */

main()

{

char  buf[SIZE]; int n;

while  ((n =  read(0, buf,  sizeof buf))  >  0) write(1,  buf, n);

exit(0);

}

Если  размер файла не  кратен SIZE,  то  очередной вызов read  вернет меньшее значение  для  записи, а  следующий после  этого  вызов  read вернет значение 0.

Наиболее эффективно чтение и запись данных выполняются порциями,  равными размеру дискового блока, хотя и посимвольный ввод-вывод годится для небольших объемов данных, так как ядро само выполняет буферизацию и основные затраты приходятся на системные вызовы. Редактор ed, к примеру, использует побайтное чтение из стандартного   ввода.  Мы  измерили  производительность этой   версии cat  на файле длиной 54 000 байт для шести значений SIZE:

Время (пользовательское+системное, сек)

SIZE

1

PDP-11/70 271,0

VAX-11/750 188,8

10

29,9

19,3

100

3,8

2,6

512

1,3

1,0

1024

1,2

0,6

5120

1,0

0,6

Размер блока составляет 512  байт  в системе PDP-11 и 1024  в системе VAX.

Совершенно нормальна ситуация, когда несколько процессов одновременно обращаются к одному  файлу; например, один процесс записывает  данные, а другой в это время их  читает. Такое поведение может сбивать с толку, но иногда бывает и полезным. Даже если  вызов read вернул 0,  следующий  вызов может обнаружить новые  данные, если они  в это время были  записаны. Этот эффект  положен в основу  программы readslow, которая продолжает чтение ввода  независимо от того, достигнут ли конец файла. Программа readslow удобна  для  наблю дения за процессом выполнения программ:

$ slowprog  >temp &

$ readslow  <temp | grep  something

Другими словами, медленная программа выполняет вывод  в файл, а программа readslow,  возможно, взаимодействуя с какой-либо  другой программой, следит за накоплением данных.

Программа readslow  идентична программе cat  за  исключением того, что она продолжает выполнять цикл после  достижения конца файла. Здесь  обращение к низкоуровневому вводу-выводу неизбежно, так как функции стандартной библиотеки продолжают возвращать EOF  после  первого достижения конца файла.

/*  readslow:  непрерывное  чтение в ожидании  данных  */

#define SIZE          512         /*  произвольное значение */

main()

{

char  buf[SIZE]; int n;

for  (;;) {

while ((n  =  read(0, buf,  sizeof buf))  >  0) write(1,  buf,  n);

sleep(10);

}

}

Функция sleep приостанавливает выполнение программы на заданное число  секунд; это описано в sleep(3). Не нужно, чтобы  readslow тормо шила файл, постоянно обращаясь за новыми данными; это потребует слишком много процессорного времени. Таким образом, эта версия re– adslow копирует свой ввод, достигает конца файла и прерывается нена долго, затем пытается повторить ввод.  Если  за время бездействия поступили новые  данные, они будут считаны при следующем проходе.

Упражнение 7.1.  Добавьте в программу readslow параметр –n так, чтобы функция sleep останавливала выполнение на  n секунд. В некоторых  системах команда tail имеет параметр –f (forever, то есть  бесконечно), который включает в команде tail режим, подобный readslow. Прокомментируйте это решение. ~

Упражнение 7.2.  Что  произойдет с программой readslow, если  размер читаемого файла уменьшится? Как  это исправить? Подсказка: прочитайте об fstat в разделе 7.3. ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии