Опубликован: 05.07.2006 | Доступ: свободный | Студентов: 4680 / 885 | Оценка: 4.12 / 3.74 | Длительность: 18:59:00
Лекция 9:

Интерфейс системы UNIX

8.6. Пример - распечатка справочников

Иногда требуется другой вид взаимодействия с системой файлов - определение информации о файле, а не того, что в нем содержится. Примером может служить команда ls ("список справочника") системы UNIX. По этой команде распечатываются имена файлов из справочника и, необязательно, другая информация, такая как размеры, разрешения и т.д.

Поскольку, по крайней мере, на системе UNIX справочник является просто файлом, то в такой команде, как ls нет ничего особенного; она читает файл и выделяет нужные части из находящейся там информации. Однако формат информации определяется системой, так что ls должна знать, в каком виде все представляется в системе.

Мы это частично проиллюстрируем при написании программы fsize. Программа fsize представляет собой специальную форму ls, которая печатает размеры всех файлов, указанных в списке ее аргументов. Если один из файлов является справочником, то для обработки этого справочника программа fsize обращается сама к себе рекурсивно. если же аргументы вообще отсутствуют, то обрабатывается текущий справочник.

Для начала дадим краткий обзор структуры системы файлов. Справочник - это файл, который содержит список имен файлов и некоторое указание о том, где они размещаются. Фактически это указание является индексом для другой таблицы, которую называют "I - узловой таблицей". Для файла I-узел - это то, где содержится вся информация о файле, за исключением его имени. Запись в справочнике состоит только из двух элементов: номера I-узла и имени файла. Точная спецификация поступает при включении файла sys/dir.h, который содержит

#define dirsiz 14 /*max length of file name*/
  struct direct /*structure of directory entry*/
  {
 ino_t& _ino; /*inode number*/
 char & _name[dirsiz]; /*file name*/
  };

"Тип" ino_t - это определяемый посредством typedef тип, который описывает индекс I-узловой таблицы. На PDP-11 UNIX этим типом оказывается unsigned, но это не тот сорт информации, который помещают внутрь программы: на разных системах этот тип может быть различным. Поэтому и следует использовать typedef. Полный набор "системных" типов находится в файле sys/types.h.

функция stat берет имя файла и возвращает всю содержащуюся в I-узле информацию об этом файле (или -1, если имеется ошибка). Таким образом, в результате

struct stat stbuf;
  char *name;
  stat(name,&stbuf);

структура stbuf наполняется информацией из I-узла о файле с именем name. структура, описывающая возвращаемую функцией stat информацию, находится в файле sys/stat.h и выглядит следующим образом:

struct stat /*structure returned by stat*/
  {
    dev_t st_dev;   /* device of inode */
    ino_t st_ino;   /* inode number */
    short st_mode   /* mode bits */
    short st_nlink; / *number of links to file */
    short st_uid;   /* owner's user id */
    short st_gid;   /* owner's group id */
    dev_t st_rdev;  /* for special files */
    off_t st_size;  /* file size in characters */
    time_t st_atime; /* time last accessed */
    time_t st_mtime; /* time last modified */
    time_t st_ctime; /* time originally created */
  }

Большая часть этой информации объясняется в комментариях. Элемент st.mode содержит набор флагов, описывающих файл; для удобства определения флагов также находятся в файле sys/stat.h.

#define s_ifmt    0160000 /* type of file */
 #define s_ifdir   0040000 /* directory */
 #define s_ifchr   0020000 /* character special */
 #define s_ifblk   0060000 /* block special */
 #define s_ifreg   0100000 /* regular */
 #define s_isuid   04000   /* set user id on execution */
 #define s_isgid   02000   /* set group id on execution */
 #define s_isvtx   01000   /*save swapped text after use*/
 #define s_iread   0400    /* read permission */
 #define s_iwrite  0200    /* write permission */
 #define s_iexec   0100    /* execute permission */

Теперь мы в состоянии написать программу fsize. Если полученный от функции stat режим указывает, что файл не является справочником, то его размер уже под рукой и может быть напечатан непосредственно. Если же он оказывается справочником, то мы должны обрабатывать этот справочник отдельно для каждого файла; так как справочник может в свою очередь содержать подсправочники, этот процесс обработки является рекурсивным.

Как обычно, ведущая программа главным образом имеет дело с командной строкой аргументов ; она передает каждый аргумент функции fsize в большой буфер.

#include <stdio.h>
#include <sys/types.h>> /*typedefs*/
#include <sys/dir.h>   /*directory entry structure*/
#include <sys/stat.h>  /*structure returned by stat*/
#define bufsize  256
main(argc,argv) /*fsize:print file sizes*/
char *argv[];
{
  char buf[bufsize];
  if(argc==1) { /*default:current directory*/
 strcpy(buf,".");
 fsize(buf);
  } else
 while(--argc>0) {
    strcpy(buf,*++argv);
    fsize(buf);
 }
}

Функция fsize печатает размер файла. Если однако файл оказывается справочником, то fsize сначала вызывает функцию directory для обработки всех указанных в нем файлов. Обратите внимание на использование имен флагов s_ifmt и _ifdir из файла stat.h.

fsize(name) /*print size for name*/
 char *name;
 {
     struct stat stbuf;
     if(stat(name,&stbuf)== -1) {
 fprintf(stderr,"fsize:can't find %s\n",name);
 return;
 }
 if((stbuf.st_mode & s_ifmt)==s_ifdir)
 directory(name);
 printf("%8ld %s\n",stbuf.st_size,name);
}

Функция directory является самой сложной. Однако значительная ее часть связана с созданием для обрабатываемого в данный момент файла его полного имени, по которому можно восстановить путь в дереве.

directory(name)    /*fsize for all files in name*/
 char *name;
  {
    struct direct dirbuf;
    char *nbp, *nep;
    int i, fd;
    nbp=name+strlen(name);
    *nbp++='/'; /*add slash to directory name*/
    if(nbp+dirsiz+2>=name+bufsize) /*name too long*/
   return;
    if((fd=open(name,0))== -1)
   return;
    while(read(fd,(char *)&dirbuf,sizeof(dirbuf))>0) \(
   if(dirbuf.d_ino==0) /*slot not in use*/
   continue;
   if(strcmp (dirbuf.d_name,".")==0
   || strcmp(dirbuf.d_name,"..")==0
   continue; /*skip self and parent*/
   for (i=0,nep=nbp;i<dirsiz;i++)
   *nep++=dirbuf.d_name[i];
   *nep++='\0';
   fsize(name);
    }
    close(fd);
    *--nbp='\0'; /*restore name*/
  }

Если некоторая дыра в справочнике в настоящее время не используется (потому что файл был удален), то в соответствующее I-узловое число равно нулю, и эта позиция пропускается. Каждый справочник также содержит запись в самом себе, называемую ".", и о своем родителе, ".."; они, очевидно, также должны быть пропущены, а то программа будет работать весьма и весьма долго.

Хотя программа fsize довольно специализированна, она все же демонстрирует пару важных идей. во-первых, многие программы не являются "системными программами "; они только используют информацию, форма или содержание которой определяется операционной системой. Во-вторых, для таких программ существенно, что представление этой информации входит только в стандартные "заголовочные файлы", такие как stat.h и dir.h, и что программы включают эти файлы, а не помещают фактические описания внутрь самих программ.