DELETE, и как такую ситуацию должным образом обработать в программе.Как и в гл. 6, во многих примерах приводятся полезные программы, не вошедшие в седьмую версию. Даже если они не помогут вам непосредственно, знакомство с ними может навести вас на мысль о сходных средствах, которые целесообразно создать и для вашей системы.
Подробное описание системных вызовов вы можете найти во втором разделе справочного руководства по UNIX, так как здесь освещаются лишь основные вопросы.
7.1 Ввод-вывод низкого уровня
Низкий уровень ввода-вывода представляет собой непосредственный вход в операционную систему Ваши программы читают или пишут файлы порциями некоторого подходящего размера. Ядро буферизует данные порциями, которые подходят для периферийных устройств, и планирует операции для устройств так, чтобы их производительность была оптимальной для всех пользователей.
Дескрипторы файловВвод и вывод обычно осуществляются посредством чтения или записи файлов, так как все периферийные устройства, и даже ваш терминал, представлены как файлы в файловой системе. Таким образом, единый интерфейс обеспечивает связь между программой и периферийными устройствами.
В самом общем случае, прежде чем читать или писать файл, необходимо сообщить системе о вашем намерении открыть файл. Если вы собираетесь писать в файл, может быть, его необходимо и создать. Система проверяет ваше право сделать это (существует ли файл и есть ли у вас разрешение на доступ к нему?), и при положительном результате проверки возвращает неотрицательное целое, называемое дескриптором файла. Всякий раз, когда нужно выполнить ввод-вывод через файл, для его идентификации вместо имени используется дескриптор файла. Вся информация об открытом файле поддерживается системой. Ваша программа ссылается на файл только через дескриптор файла. Указатель на
FILE
, как отмечалось в гл. 6, является ссылкой на структуру, которая наряду с прочим содержит дескриптор файла: макрокоманда fileno(fp)
, определенная в
, возвращает дескриптор файла.Для обеспечения удобства ввода и вывода на терминал предусмотрены специальные меры. Когда программа запускается из
shell
, она получает три открытых файла с дескрипторами 0, 1 и 2 стандартный входной поток, стандартный выходной поток и стандартный файл диагностики. Всем им по умолчанию поставлен в соответствие терминал, поэтому если программа читает только через дескриптор файла 0 и пишет через дескрипторы файлов 1 и 2, она может выполнять ввод-вывод без открывания файлов. Если же программа открывает любые другие файлы, они будут иметь дескрипторы 3, 4 и т.д.При переключении ввода-вывода на файлы или программные каналы (к ним или от них)
shell
изменяет назначение терминала по умолчанию для дескрипторов файлов 0 и 1. Обычно дескриптор файла 2 закрепляется за терминалом, так что сообщения об ошибках могут поступать на него. Использование символики shell
, такой, как 2>filename
и 2>&1
, вызовет переназначение файла, присвоенного по умолчанию, но при этом присвоение файлов меняется в shell
, а не программой. (Программа сама может переназначить их впоследствии, если потребуется, что, правда, бывает редко.)Файловый ввод-вывод: read
и write
Весь ввод и вывод обеспечиваются двумя системными вызовами,
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
в
). Для иллюстрации изложенного здесь приведена программа копирования входного потока в выходной. Так как входной и выходной потоки могут переключаться на любой файл или устройство, она действительно скопирует что-нибудь куда-либо: это "скелетная" реализация cat
./* cat: minimal version */
#define SIZE 512 /* arbitrary */
main() {
char buf[SIZE];
int n;
while ((n = read(0, buf, sizeof buf)) > 0)
write(1, buf, n);
exit(0);
}
Если размер файла не кратен числу
SIZE
, некоторый вызов read
вернет меньшее число байтов, которые должны быть записаны с помощью write
; следующий затем вызов read
вернет нуль.Чтение и запись порциями, подходящими для диска, будут наиболее эффективными, но даже ввод-вывод по одному символу за раз осуществим для умеренных объемов буферизуются ядром. Дороже всего обходятся обращения к системе. Программа
ed
, например, использует однобайтовый способ, чтобы читать стандартный входной поток. Мы хронометрировали работу данной версии cat для файла в 54 000 байт при шести значениях SIZE
:Время (пользователь+система, в сек.)
Размер PDP-11/40 VAX-11/750 1 271.0 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
Размер блока на диске для системы на PDP-11 составляет 512 байт и 1024 байта — для VAX.
Доступ нескольких процессоров к одному и тому же файлу в одно и то же время является совершенно законным: в самом деле, один процесс может писать, в то время как другой читает. Это обескураживает, если не входит в ваши планы, но иногда оказывается полезным. Даже несмотря на то, что при одном обращении к функции
read
возвращается 0, который сигнализирует о конце файла, следующий вызов read
обнаружит наличие некоторого количества байтов, если еще данные пишутся в файл. Это соображение лежит в основе программы readslow
, которая продолжает читать свой входной поток вне зависимости от того, достигла она конца файла или нет. Программа readslow
удобна для наблюдения за работой программы.$ slowprog >temp &
5213
идентификатор процесса$ readslow
Иными словами, медленная программа выполняет вывод в файл;
readslow
, возможно, совместно с некоторыми другими программами "наблюдает", как накапливаются данные.По составу
readslow
идентична cat, за исключением того, что она зацикливается, а не завершается, когда встречает конец входного потока. Программа readslow
должна использовать ввод-вывод низкого уровня, так как стандартная библиотечная функция продолжает выдавать EOF
после первого конца файла./* readslow: keep reading, waiting for more */
#define SIZE 512 /* arbitrary */
main() {
char buf[SIZE];
int n;
for (;;) {
while ((n = read(0, buf, sizeof buf))