Керниган и Ритчи - Язык программирования Си (793773), страница 43
Текст из файла (страница 43)
Эта информация сосредоточена взаголовочном файле 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 значение, находится в <sys/stat.h> и выглядит примерно так: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, определены вфайле <sys/types.h>, который тоже нужно включить посредством #include.Элемент st_mode содержит набор флажков, составляющих дополнительную информацию о файле.Определения флажков также содержатся в <sys/stat.h>; нам потребуется только та его часть, котораяимеет дело с типом файла:#define#define#define#define#defineS_IFMTS_IFDIRS_IFCHRS_IFBLKS_IFREG01600000040000002000000600000100000/*/*/*/*/*тип файла */каталог */символьно-ориентированный */блочно-ориентированный */обычный */Теперь мы готовы приступить к написанию программы fsize.
Если режимные биты (st_mode), полученныеот stat, указывают, что файл не является каталогом, то можно взять его размер (st_size) и напечатать.Однако если файл — каталог, то мы должны обработать все его файлы, каждый из которых в свою очередьможет быть каталогом. Обработка каталога — процесс рекурсивный.Программа main просматривает параметры командной строки, передавая каждый аргумент функции fsize.#include#include#include#include#include#include#include<stdio.h><string.h>"syscalls.h"<fcntl.h> /* флажки чтения и записи */<sys/types.h> /* определения типов */<sys/stat.h> /* структура, возвращаемая stat */"dirent.h"void fsize(char *);/* печатает размеры файлов */main(int argc, char **argv){if (argc == 1) /* по умолчанию берется текущий каталог */fsize(".");elsewhile (--argc > 0)fsize(*++argv);return 0;}Функция fsize печатает размер файла.
Однако, если файл — каталог, она сначала вызывает dirwalk, чтобыобработать все его файлы. Обратите внимание на то, как используются имена флажков S_IFMT и S_IFDIRиз <sys/stat.h> при проверке, является ли файл каталогом. Здесь нужны скобки, поскольку приоритетоператора & ниже приоритета оператора ==.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.
Они используют информацию о каталоге,хранящуюся в заголовочном файле <sys/dir.h>, который выглядит следующим образом:#ifndef DIRSIZ#define DIRSIZ 14#endifstruct direct /* элемент каталога */{ino_t d_ino; /* номер inode */char d_name[DIRSIZ]; /* длинное имя не имеет '\0' */};Некоторые версии системы допускают более длинные имена и имеют более сложную структуру каталога.Тип ino_t задан с помощью typedef и описывает индекс списка узлов inode. В системе, которойпользуемся мы, этот тип есть unsigned short, но в других системах он может быть иным, поэтому еголучше определять через typedef. Полный набор "системных" типов находится в <sys/types.h>.Функция 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 <sys/dir.h> /* место расположения структуры каталога *//* 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 — довольно специализированная, она иллюстрирует два важных факта.
Первый:многие программы не являются "системными"; они просто используют информацию, которую хранитоперационная система. Для таких программ существенно то, что представление информации сосредоточеноисключительно в стандартных заголовочных файлах. Программы включают эти файлы, а не держатобъявления в себе. Второе наблюдение заключается в том, что при старании системно-зависимым объектамможно создать интерфейсы, которые сами не будут системно-зависимыми. Хорошие тому примеры —функции стандартной библиотеки.Упражнение 8.5. Модифицируйте fsize таким образом, чтобы можно было печатать остальнуюинформацию, содержащуюся в узле inode.8.7.
Пример. Распределитель памятиВ главе 5 был описан простой распределитель памяти, основанный на принципе стека. Версия, которую мынапишем здесь, не имеет ограничений: вызовы malloc и free могут выполняться в любом порядке;malloc делает запрос в операционную систему на выделение памяти тогда, когда она требуется. Этипрограммы иллюстрируют приемы, позволяющие получать машинно-зависимый код сравнительно машиннонезависимым способом, и, кроме того, они могут служить примером применения таких средств языка, какструктуры, объединения и typedef.Никакого ранее скомпилированного массива фиксированного размера, из которого выделяются куски памяти,не будет. Функция malloc запрашивает память у операционной системы по мере надобности.
Поскольку идругие действия программы могут вызывать запросы памяти, которые удовлетворяются независимо от этогораспределителя памяти, пространство, которым заведует malloc, не обязательно представляет собойсвязный кусок памяти. Поэтому свободная память хранится в виде списка блоков. Каждый блок содержитразмер, указатель на следующий блок и само пространство. Блоки в списке хранятся в порядке возрастанияадресов памяти, при этом последний блок (с самым большим адресом) указывает на первый.При возникновении запроса на память просматривается список свободных блоков, пока не обнаружитсядостаточно большой блок.















