Главная страница
Навигация по странице:

  • Упражнение 8.1.

  • 8.4. Произвольный доступ (lseek)

  • 8.5. Пример. Реализация функций fopen и getc

  • 8.6. Пример. Печать каталогов

  • Язык программирования Си Брайан Керниган, Деннис Ритчи 3е издание Версия 1 Table of Contents


    Скачать 2.33 Mb.
    НазваниеЯзык программирования Си Брайан Керниган, Деннис Ритчи 3е издание Версия 1 Table of Contents
    Дата18.09.2022
    Размер2.33 Mb.
    Формат файлаpdf
    Имя файлаBrian_Kernighan_Dennis_Ritchie-The_C_Programming_Language-RU.pdf
    ТипДокументы
    #683263
    страница20 из 31
    1   ...   16   17   18   19   20   21   22   23   ...   31
    8.3. Системные вызовы open, creat, close, unlink
    В отличие от стандартных файлов ввода, вывода и ошибок, которые открыты по умолчанию, остальные файлы нужно открывать явно. Для этого есть два системных вызова: open и сreat
    Функция open почти совпадает с fopen
    , рассмотренной в главе 7. Разница между ними в том, что первая возвращает не файловый указатель, а дескриптор файла типа int
    . При любой ошибке open возвращает -1.
    #include int fd; int open(char *name, int flags, int perms); fd = open(name, flags, perms);
    Как и в fopen
    , аргумент name
    — это строка, содержащая имя файла. Второй аргумент, flags
    , имеет тип int и специфицирует, каким образом должен быть открыт файл. Его основными значениями являются:
    O_RDONLY
    — открыть только на чтение;
    O_WRONLY
    — открыть только на запись;
    O_RDWR
    — открыть и на чтение, и на запись.
    В System V UNIX эти константы определены в

    , а в версиях Berkeley (BSD) — в

    Чтобы открыть существующий файл на чтение, можно написать fd = open(name, O_RDONLY, 0);

    Далее везде, где мы пользуемся функцией open
    , ее аргумент perms равен нулю.
    Попытка открыть несуществующий файл является ошибкой. Создание нового файла или перезапись старого обеспечивается системным вызовом creat
    . Например int creat(char *name, int perms); fd = сreat(name, perms);
    Функция creat возвращает дескриптор файла, если файл создан, и -1, если по каким-либо причинам файл создать не удалось. Если файл уже существует, creat
    "обрежет" его до нулевой длины, что равносильно выбрасыванию предыдущего содержимого данного файла; создание уже существующего файла не является ошибкой.
    Если строится действительно новый файл, то creat его создаст с правами доступа, специфицированными в аргументе реrms
    . В системе UNIX с каждым файлом ассоциированы девять битов, содержащие информацию о правах пользоваться этим файлом для чтения, записи и исполнения лицам трех категорий: собственнику файла, определенной им группе лиц и всем остальным. Таким образом, права доступа удобно специфицировать с помощью трех восьмеричных цифр. Например,
    0755
    специфицирует чтение, запись и право исполнения собственнику файла, а также чтение и право исполнения группе и всем остальным.
    Для иллюстрации приведем упрощенную версию программы ср системы UNIX, которая копирует один файл в другой. В нашей версии копируется только один файл, не позволяется во втором аргументе указывать директорий (каталог), и права доступа не копируются, а задаются константой.
    #include
    #include
    #include "syscalls.h"
    #define PERMS 0666 /* RW для собственника, группы и остальных */ void error(char *, ...);
    /* ср: копирование f1 в f2 */ main(int argc, char *argv[])
    { int f1, f2, n; char buf[BUFSIZ]; if (argc != 3) еrrоr("Обращение: ср откуда куда"); if ((f1 = open(argv[1], 0_RDONLY, 0)) == -1) error ("ср: не могу открыть файл %s", argv[1]); if ((f2 = creat(argv[2], PERMS)) == -1) error ("ср: не могу создать файл %s, режим %03о", argv[2], PERMS); while ((n = read(f1, buf, BUFSIZ)) > 0) if (write(f2, buf, n) != n) error ("ср: ошибка при записи в файл %s", argv[2]); return 0;
    }
    Данная программа создает файл вывода с фиксированными правами доступа, определяемыми кодом
    0666
    С помощью системного вызова stat
    , который будет описан в параграфе 8.6, мы можем определить режим использования существующего файла и задать тот же режим для копии.

    Заметим, что функция error
    , вызываемая с различным числом аргументов, во многом похожа на printf
    Реализация error иллюстрирует, как пользоваться другими программами семейства printf. Библиотечная функция vprintf аналогична printf
    , с той лишь оговоркой, что переменная часть списка аргументов заменена в ней одним аргументом, который инициализируется макросом va_start
    . Подобным же образом соотносятся функции vfprintf с fprintf и vsprintf с sprintf
    #include
    #include
    /* error: печатает сообщение об ошибке и умирает */ void error(char *fmt, ...)
    { va_list args; va_start(args, fmt); fprintf(stderr, "ошибка: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(1);
    }
    На количество одновременно открытых в программе файлов имеется ограничение (обычно их число колеблется около 20). Поэтому любая программа, которая намеревается работать с большим количеством файлов, должна быть готова повторно использовать их дескрипторы. Функция close(int fd)
    разрывает связь между файловым дескриптором и открытым файлом и освобождает дескриптор для его применения с другим файлом. Она аналогична библиотечной функции fclose с тем лишь различием, что никакой очистки буфера не делает. Завершение программы с помощью exit или return в главной программе закрывает все открытые файлы.
    Функция unlink(char *name)
    удаляет имя файла из файловой системы. Она соответствует функции remove стандартной библиотеки.
    Упражнение 8.1. Перепишите программу cat из главы 7, используя функции read
    , write
    , open и close
    Замените ими соответствующие функции стандартной библиотеки. Поэкспериментируйте, чтобы сравнить быстродействие двух версий.
    8.4. Произвольный доступ (lseek)
    Ввод-вывод обычно бывает последовательным, т. е. каждая новая операция чтения-записи имеет дело с позицией файла, следующей за той, что была в предыдущей операции (чтения-записи). При желании, однако, файл можно читать или производить запись в него в произвольном порядке. Системный вызов lseek предоставляет способ передвигаться по файлу, не читая и не записывая данные. Так, функция long lseek(int fd, long offset, int origin); в файле с дескриптором fd устанавливает текущую позицию, смещая ее на величину offset относительно места, задаваемого значением origin
    . Значения параметра origin
    0, 1 или 2 означают, что на величину offset отступают соответственно от начала, от текущей позиции или от конца файла. Например, если требуется добавить что-либо в файл (когда в командном интерпретаторе shell системы UNIX ввод перенаправлен оператором
    >>
    в файл или когда в fopen задан аргумент "а"
    ), то прежде чем что-либо записывать, необходимо найти конец файла с помощью вызова функции lseek(fd, 0L, 2);

    Чтобы вернуться назад, в начало файла, надо выполнить lseek(fd, 0L, 0);
    Следует обратить внимание на аргумент
    0L
    : вместо
    0L
    можно было бы написать
    (long) 0
    или, если функция lseek должным образом объявлена, просто 0.
    Благодаря lseek с файлами можно работать так, как будто это большие массивы, правда, с замедленным доступом. Например, следующая функция читает любое число байтов из любого места файла. Она возвращает число прочитанных байтов или -1 в случае ошибки.
    #include "syscalls.h"
    /* get: читает n байт из позиции роз */ int get(int fd, long pos, char *buf, int n)
    { if (lseek(fd, pos, 0) >= 0) /* установка позиции */ return read(fd, buf, n); else return -1;
    }
    Возвращаемое функцией lseek значение имеет тип long и является новой позицией в файле или, в случае ошибки, равно -1. Функция fseek из стандартной библиотеки аналогична lseek
    ; от последней она отличается тем, что в случае ошибки возвращает некоторое ненулевое значение, а ее первый аргумент имеет тип
    FILE*
    8.5. Пример. Реализация функций fopen и getc
    Теперь на примере функций fopen и getc из стандартной библиотеки покажем, как описанные выше части согласуются друг с другом.
    Напомним, что файлы в стандартной библиотеке описываются файловыми указателями, а не дескрипторами.
    Указатель файла — это указатель на структуру, содержащую информацию о файле: указатель на буфер, позволяющий читать файл большими кусками; число незанятых байтов буфера; указатель на следующую позицию в буфере; дескриптор файла; флажки, описывающие режим (чтение/запись), ошибочные состояния и т. д.
    Структура данных, описывающая файл, содержится в

    , который необходимо включать (с помощью
    #include
    ) в любой исходный файл, если в том осуществляется стандартный ввод-вывод. Этот же заголовочный файл включен и в исходные тексты библиотеки ввода-вывода.
    В следующем фрагменте, типичном для файла

    , имена, используемые только в библиотечных функциях, начинаются с подчеркивания. Это сделано для того, чтобы они случайно не совпали с именами, фигурирующими в программе пользователя. Такое соглашение соблюдается во всех программах стандартной библиотеки.
    #define NULL О
    #define EOF (-1)
    #define BUFSIZ 1024
    #define OPEN_MAX 20 /* max число одновременно открытых файлов */ typedef struct _iobuf { int cnt; /* количество оставшихся символов */ char *ptr; /* позиция следующего символа */
    char *base; /* адрес буфера */ int flag; /* режим доступа */ int fd; /* дескриптор файла */
    } FILE; extern FILE _iob[OPEN_MAX];
    #define stdin (&iob[0])
    #define stdout (&_iob[1])
    #define stderr (&_iob[2]) enum _flags {
    _READ =01, /* файл открыт на чтение */
    _WRITE = 02, /* файл открыт на запись */
    _UNBUF = 04, /* файл не буферизуется */
    _EOF = 010, /* в данном файле встретился EOF */
    _ERR = 020 /* в данном файле встретилась ошибка */
    }; int _fillbuf(FILE *); int _flushbuf(int, FILE *);
    #define feof(p) (((p)->flag & _EOF) != 0)
    #define ferror(p) (((p)->flag & _ERR) != 0)
    #define fileno(p) ((p)->fd)
    #define getc(p) (--(p)->cnt >= 0 \
    ? (unsigned char) *(p)->ptr++ : _fillbuf(p))
    #define putc(x.p) (--(p)->cnt >= 0 \
    ? *(p)->ptr++ = (x) : _flushbuf((x),p))
    #define getchar() getc(stdin)
    #define putchar(x) putc((x), stdout)
    Макрос getc обычно уменьшает счетчик числа символов, находящихся в буфере, и возвращает символ, после чего приращивает указатель на единицу. (Напомним, что длинные
    #define с помощью обратной наклонной черты можно продолжить на следующих строках.) Когда значение счетчика становится отрицательным, getc вызывает
    _fillbuf
    , чтобы снова заполнить буфер, инициализировать содержимое структуры и выдать символ. Типы возвращаемых символов приводятся к unsigned
    ; это гарантирует, что все они будут положительными.
    Хотя в деталях ввод-вывод здесь не рассматривается, мы все же привели полное определение putc
    . Сделано это, чтобы показать, что она действует во многом так же, как и getc
    , вызывая функцию
    _flushbuf
    , когда буфер полон. В тексте имеются макросы, позволяющие получать доступ к флажкам ошибки и конца файла, а также к его дескриптору.
    Теперь можно написать функцию fopen
    . Большая часть инструкций fopen относится к открытию файла, к соответствующему его позиционированию и к установке флажковых битов, предназначенных для индикации текущего состояния. Сама fopen не отводит места для буфера; это делает
    _fillbuf при первом чтении файла.
    #include
    #include "syscalls.h"
    #define PERMS 0666 /* RW для собственника, группы и проч. */

    /* fopen: открывает файл, возвращает файловый указатель */
    FILE *fopen(char *name, char *mode)
    { int fd;
    FILE *fp; if (*mode != 'r' && *mode != 'w' && *mode != 'a') return NULL; for (fp = _iob; fp < _iob + OPEN_MAX; fp++) if ((fp->flag & (_READ | _WRITE)) == 0) break; /* найдена свободная позиция*/ if (fp >= _iob + OPEN_MAX) /* нет свободной позиции */ return NULL; if (*mode == 'w' ) fd = creat(name, PERMS); else if (*mode == 'a' ) { if ((fd = open(name, O_WRONLY, 0)) == -1) fd = creat(name, PERMS); lseek(fd, 0L, 2);
    } else fd = open(name, O_RDONLY, 0); if (fd == -1) /* невозможен доступ по имени name */ return NULL; fp->fd = fd; fp->cnt = 0; fp->base = NULL; fp->flag = (*mode == 'r' ) ? _READ : _WRITE; return fp;
    }
    Приведенная здесь версия fopen реализует не все режимы доступа, оговоренные стандартом; но, мы думаем, их реализация в полном объеме ненамного увеличит длину программы. Наша fopen не распознает буквы b
    , сигнализирующей о бинарном вводе-выводе (поскольку в системах UNIX это не имеет смысла), и знака
    +
    , указывающего на возможность одновременно читать и писать.
    Для любого файла в момент первого обращения к нему с помощью макровызова getc счетчик cnt равен нулю. Следствием этого будет вызов
    _fillbuf
    . Если выяснится, что файл на чтение не открыт, то функция
    _fillbuf немедленно возвратит
    EOF
    . В противном случае она попытается запросить память для буфера
    (если чтение должно быть с буферизацией).
    После получения области памяти для буфера
    _fillbuf обращается к read
    , чтобы его наполнить, устанавливает счетчик и указатели и возвращает первый символ из буфера. В следующих обращениях
    _fillbuf обнаружит, что память для буфера уже выделена.
    #include "syscalls.h"
    /* _fillbuf: запрос памяти и заполнение буфера */ int _fillbuf(FILE *fp)
    { int bufsize; if ((fp->flag&(_READ | _EOF | _ERR)) != _READ) return EOF; bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZ;
    if (fp->base == NULL) /* буфера еще нет */ if ((fp->base = (char *) malloc(bufsize)) == NULL) return EOF; /* нельзя получить буфер */ fp->ptr = fp->base; fp->cnt = read(fp->fd, fp->ptr, bufsize); if (--fp->cnt < 0) { if (fp->cnt == -1) fp->flag |= _EOF; else fp->flag |= _ERR; fp->cnt = 0; return EOF;
    } return (unsigned char) *fp->ptr++;
    }
    Единственное, что осталось невыясненным, — это каким образом организовать начало счета. Массив
    _iob следует определить и инициализировать так, чтобы перед тем как программа начнет работать, в нем уже была информация о файлах stdin
    , stdout и stderr
    FILE _iob[OPEN_MAX] = { /* stdin, stdout, stderr: */
    { 0, (char *) 0, (char *) 0, _READ, 0 },
    { 0, (char *) 0, (char *) 0, _WRITE, 1 },
    { 0, (char *) 0, (char *) 0, _WRITE | _UNNBUF, 2 }
    };
    Инициализация flag как части структуры показывает, что stdin открыт на чтение, stdout
    — на запись, a stderr
    — на запись без буферизации.
    Упражнение 8.2. Перепишите функции fopen и
    _fillbuf
    , работая с флажками как с полями, а не с помощью явных побитовых операций. Сравните размеры и скорости двух вариантов программ.
    Упражнение 8.3. Разработайте и напишите функции
    _flushbuf
    , fflush и fclose
    Упражнение 8.4. Функция стандартной библиотеки int fseek(FILE *fp, long offset, int origin) идентична функции lseek с теми, однако, отличиями, что fp
    — это файловый указатель, а не дескриптор, и возвращает она значение int
    , означающее состояние файла, а не позицию в нем. Напишите свою версию fseek
    . Обеспечьте, чтобы работа вашей fseek по буферизации была согласована с буферизацией, используемой другими функциями библиотеки.
    8.6. Пример. Печать каталогов
    При разного рода взаимодействиях с файловой системой иногда требуется получить только информацию о файле, а не его содержимое. Такая потребность возникает, например, в программе печати каталога файлов, работающей аналогично команде ls системы UNIX. Она печатает имена файлов каталога и по желанию пользователя другую дополнительную информацию (размеры, права доступа и т. д.). Аналогичной командой в MS-DOS является dir
    Так как в системе UNIX каталог — это тоже файл, функции ls
    , чтобы добраться до имен файлов, нужно только его прочитать. Но чтобы получить другую информацию о файле (например узнать его размер), необходимо выполнить системный вызов. В других системах (в MS-DOS, например) системным вызовом приходится пользоваться даже для получения доступа к именам файлов. Наша цель — обеспечить доступ к информации
    по возможности системно-независимым способом несмотря на то, что реализация может быть существенно системно-зависима.
    Проиллюстрируем сказанное написанием программы fsize
    . Функция fsize
    — частный случай программы ls
    ; она печатает размеры всех файлов, перечисленных в командной строке. Если какой-либо из файлов сам является каталогом, то, чтобы получить информацию о нем, fsize обращается сама к себе. Если аргументов в командной строке нет, то обрабатывается текущий каталог.
    Для начала вспомним структуру файловой системы в UNIXe. Каталог — это файл, содержащий список имен файлов и некоторую информацию о том, где они расположены. "Место расположения" — это индекс, обеспечивающий доступ в другую таблицу, называемую "списком узлов inode". Для каждого файла имеется свой inode, где собрана вся информация о файле, за исключением его имени. Каждый элемент каталога состоит из двух частей: из имени файла и номера узла inode.
    К сожалению, формат и точное содержимое каталога не одинаковы в разных версиях системы. Поэтому, чтобы переносимую компоненту отделить от непереносимой, разобьем нашу задачу на две. Внешний уровень определяет структуру, названную
    Dirent
    , и три подпрограммы opendir
    , readdir и closedir
    ; в результате обеспечивается системно-независимый доступ к имени и номеру узла inode каждого элемента каталога. Мы будем писать программу fsize
    , рассчитывая на такой интерфейс, а затем покажем, как реализовать указанные функции для систем, использующих ту же структуру каталога, что и Version 7 и System
    V UNIX. Другие варианты оставим для упражнений.
    Структура
    Dirent содержит номер узла inode и имя. Максимальная длина имени файла равна
    NAME_MAX
    — это значение системно-зависимо. Функция opendir возвращает указатель на структуру, названную
    DIR
    (по аналогии с
    FILE
    ), которая используется функциями readdir и closedir
    . Эта информация сосредоточена в заголовочном файле dirent.h
    #define NAME_MAX 14 /* максимальная длина имени файла; */
    /* системно-зависимая величина */ typedef struct { /* универс. структура элемента каталога: */ long ino; /* номер inode */ char name[NAME_MAX+1]; /* имя + завершающий '\0' */
    } Dirent; typedef struct { /* минимальный DIR: без буферизации и т.д. */ int fd; /* файловый дескриптор каталога */
    Dirent d; /* элемент каталога */
    } DIR;
    DIR *opendir(char *dirname);
    Dirent *readdir(DIR *dfd); void closedir(DIR *dfd);
    Системный вызов stat получает имя файла и возвращает полную о нем информацию, содержащуюся в узле inode, или -1 в случае ошибки. Так, char *name; struct stat stbuf; int stat(char *, struct stat *); stat(name, &stbuf);
    заполняет структуру stbuf информацией из узла inode о файле с именем name
    . Структура, описывающая возвращаемое функцией stat значение, находится в

    и выглядит примерно так: struct stat { /* информация из inode, возвращаемая stat */ dev_t st_dev; /* устройство */ ino_t st_ino; /* номер inode */ short st_mode; /* режимные биты */ short st_nlink; /* число связей с файлом */ short st_uid; /* имя пользователя-собственника */ short st_gid; /* имя группы собственника */ dev_t st_rdev; /* для специальных файлов */ off_t st_size; /* размер файла в символах */ time_t st_atime; /* время последнего использования */ time_t st_mtime; /* время последней модификации */ time_t st_ctime; /* время последнего изменения inode */
    };
    Большинство этих значений объясняется в комментариях. Типы, подобные dev_t и ino_t
    , определены в файле

    , который тоже нужно включить посредством
    #include
    Элемент st_mode содержит набор флажков, составляющих дополнительную информацию о файле.
    Определения флажков также содержатся в

    ; нам потребуется только та его часть, которая имеет дело с типом файла:
    #define S_IFMT 0160000 /* тип файла */
    #define S_IFDIR 0040000 /* каталог */
    #define S_IFCHR 0020000 /* символьно-ориентированный */
    #define S_IFBLK 0060000 /* блочно-ориентированный */
    #define S_IFREG 0100000 /* обычный */
    Теперь мы готовы приступить к написанию программы fsize
    . Если режимные биты (
    st_mode
    ), полученные от stat
    , указывают, что файл не является каталогом, то можно взять его размер (
    st_size
    ) и напечатать.
    Однако если файл — каталог, то мы должны обработать все его файлы, каждый из которых в свою очередь может быть каталогом. Обработка каталога — процесс рекурсивный.
    Программа main просматривает параметры командной строки, передавая каждый аргумент функции fsize
    #include
    #include
    #include "syscalls.h"
    #include /* флажки чтения и записи */
    #include /* определения типов */
    #include /* структура, возвращаемая stat */
    #include "dirent.h" void fsize(char *);
    /* печатает размеры файлов */ main(int argc, char **argv)
    { if (argc == 1) /* по умолчанию берется текущий каталог */ fsize("."); else while (--argc > 0)
    fsize(*++argv); return 0;
    }
    Функция fsize печатает размер файла. Однако, если файл — каталог, она сначала вызывает dirwalk
    , чтобы обработать все его файлы. Обратите внимание на то, как используются имена флажков
    S_IFMT
    и
    S_IFDIR
    из

    при проверке, является ли файл каталогом. Здесь нужны скобки, поскольку приоритет оператора
    &
    ниже приоритета оператора
    ==
    int stat(char *, struct stat *); void dirwalk(char *, void (*fcn)(char *));
    /* fsize: печатает размер файла "name" */ void fsize(char *name)
    { struct stat stbuf; if (stat(name, Sstbuf) == -1) { fprintf(stderr, "fsize: нет доступа к %s\n", name); return;
    } if ((stbuf.stjnode & S_IFMT) == S_IFDIR) dirwalk(name, fsize); printf("%81d %s\n", stbuf.st_size, name);
    }
    Функция dirwalk
    — это универсальная программа, применяющая некоторую функцию к каждому файлу каталога. Она открывает каталог, с помощью цикла перебирает содержащиеся в нем файлы, применяя к каждому из них указанную функцию, затем закрывает каталог и осуществляет возврат. Так как fsize вызывает dirwalk на каждом каталоге, в этих двух функциях заложена косвенная рекурсия.
    #define MAX_PATH 1024
    /* dirwalk: применяет fcn ко всем файлам из dir */ void dirwalk(char *dir, void (*fcn)(char *))
    { char name[MAX_PATH];
    Dirent *dp;
    DIR *dfd; if ((dfd = opendir(dir)) == NULL) { fprintf(stderr, "dirwalk: не могу открыть %s\n", dir); return;
    } while ((dp = readdir(dfd)) != NULL) { if (strcmp(dp->name, ".") == 0
    || strcmp(dp->name, "..") == 0) continue; /* пропустить себя и родителя */ if (strlen(dir)+strlen(dp->name)+2 > sizeof(name)) fprintf (stderr, "dirwalk: слишком длинное имя %s/%s\n", dir, dp->name); else { sprintf(name, "%s/%s", dir, dp->name);
    (*fcn)(name);
    }

    } closedir(dfd);
    }
    Каждый вызов readdir возвращает указатель на информацию о следующем файле или
    NULL
    , если все файлы обработаны. Любой каталог всегда хранит в себе информацию о себе самом в файле под именем "
    " и о своем родителе в файле под именем "
    "; их нужно пропустить, иначе программа зациклится. Обратите внимание: код программы этого уровня не зависит от того, как форматированы каталоги. Следующий шаг — представить минимальные версии opendir
    , readdir и closedir для некоторой конкретной системы.
    Здесь приведены программы для систем Version 7 и System V UNIX. Они используют информацию о каталоге, хранящуюся в заголовочном файле

    , который выглядит следующим образом:
    #ifndef DIRSIZ
    #define DIRSIZ 14
    #endif struct direct /* элемент каталога */
    { ino_t d_ino; /* номер inode */ char d_name[DIRSIZ]; /* длинное имя не имеет '\0' */
    };
    Некоторые версии системы допускают более длинные имена и имеют более сложную структуру каталога.
    Тип ino_t задан с помощью typedef и описывает индекс списка узлов inode. В системе, которой пользуемся мы, этот тип есть unsigned short
    , но в других системах он может быть иным, поэтому его лучше определять через typedef
    . Полный набор "системных" типов находится в

    Функция opendir открывает каталог, проверяет, является ли он действительно каталогом (в данном случае это делается с помощью системного вызова fstat
    , который аналогичен stat
    , но применяется к дескриптору файла), запрашивает пространство для структуры каталога и записывает информацию. int fstat(int fd, struct stat *);
    /* opendir: открывает каталог для вызовов readdir */
    DIR *opendir(char *dirname)
    { int fd; struct stat stbuf;
    DIR *dp; if ((fd = open(dirname, 0_RDONLY, 0)) == -1
    || fstat(fd, Sstbuf) == -1
    || (stbuf.stjnode & S_IFMT) != S_IFDIR
    || (dp = (DIR *) malloc(sizeof(DIR))) == NULL) return NULL; dp->fd = fd; return dp;
    }
    Функция closedir закрывает каталог и освобождает пространство.
    /* closedir: закрывает каталог, открытый opendir */ void closedir(DIR *dp)
    {
    if (dp) { close(dp->fd); free(dp);
    }
    }
    Наконец, readdir с помощью read читает каждый элемент каталога. Если некий элемент каталога в данный момент не используется (соответствующий ему файл был удален), то номер узла inode у него равен нулю, и данная позиция пропускается. В противном случае номер inode и имя размещаются в статической (
    static
    ) структуре, и указатель на нее выдается в качестве результата. При каждом следующем обращении новая информация занимает место предыдущей.
    #include /* место расположения структуры каталога */
    /* readdir: последовательно читает элементы каталога */
    Dirent *readdir(DIR *dp)
    { struct direct dirbuf; /* структура каталога на данной системе */ static Dirent d; /* возвращает унифицированную структуру */ while (read(dp->fd, (char *) &dirbuf, sizeof (dirbuf ))
    == sizeof (dirbuf)) { if (dirbuf.d_ino == 0) /* пустой элемент, не используется */ continue; d.ino = dirbuf.d_ino; strncpy(d.name, dirbuf.d_name, DIRSIZ); d.name[DIRSIZ] = '\0'; /* завершающий символ '\0' */ return &d;
    } return NULL;
    }
    Хотя программа fsize
    — довольно специализированная, она иллюстрирует два важных факта. Первый: многие программы не являются "системными"; они просто используют информацию, которую хранит операционная система. Для таких программ существенно то, что представление информации сосредоточено исключительно в стандартных заголовочных файлах. Программы включают эти файлы, а не держат объявления в себе. Второе наблюдение заключается в том, что при старании системно-зависимым объектам можно создать интерфейсы, которые сами не будут системно-зависимыми. Хорошие тому примеры — функции стандартной библиотеки.
    1   ...   16   17   18   19   20   21   22   23   ...   31


    написать администратору сайта