Книга: Linux программирование в примерах

8.3.1. Стиль POSIX: statvfs() и fstatvfs()

8.3.1. Стиль POSIX: statvfs() и fstatvfs()

На ранних системах Unix была только одна разновидность файловой системы. Для них было достаточно, если df считывала суперблок каждой смонтированной файловой системы, извлекала значимые сведения и красиво форматировала их для отображения. (Суперблок обычно был вторым блоком в файловой системе; первым был загрузочный блок, содержащий загрузочный код).

Однако в современном мире такой подход был бы непригодным. POSIX предоставляет расширение XSI для получения доступа к этой информации. Главная функция называется statvfs() («vfs» часть происходит от лежащей в основе технологии SunOS, использованной позже в System V Release 4, которая называется виртуальной файловой системой.) Имеется две функции:

#include <sys/types.h> /* XSI */
#include <sys/statvfs.h>
int statvfs(const char *path, struct statvfs *buf);
int fstatvfs(int fd, struct statvfs *buf);
statvfs()
использует для любого файла имя пути; она возвращает сведения о файловой системе, содержащей файл. fstatvfs() принимает в качестве первою аргумента дескриптор открытого файла, здесь также возвращается информация о файловой системе, содержащей открытый файл, struct statvfs содержит следующие члены:

struct statvfs {
 unsigned long int f_bsize;   /* Размер блока */
 unsigned long int f_frsize;
  /* Размер фрагмента («основной размер блока») */
 fsblkcnt_t f_blocks;         /* Общее число блоков */
 fsblkcnt_t f_bfree;          /* Общее число свободных блоков */
 fsblkcnt_t f_bavail;         /* Число доступных блоков (?f_bfree) */
 fsfilcnt_t f_files;          /* Общее число индексов */
 fsfilcnt_t f_ffree;          /* Общее число свободных индексов */
 fsfilcnt_t f_favail;         /* Число доступных индексов (?f_files) */
 unsigned long int f_fsid;    /* ID файловой системы */
 unsigned long int f_flag;    /* Флаги: ST_RDONLY и/или ST_NOSUID */
 unsigned long int f_namemax; /* Максимальная длина имени файла */
};

Сведений, которые в ней содержатся, достаточно для написания df:

unsigned long int f_bsize

Размер блока является предпочтительным размером для осуществления ввода/вывода. Файловая система пытается хранить по крайней мере f_bsize байтов стоящих данных в смежных секторах на диске. (Сектор является наименьшим количеством адресуемых данных на диске. Обычно дисковый сектор равен 512 байтам.)

unsigned long int f_frsize

Некоторые файловые системы (такие, как BSD Fast Filesystem) проводят различие между блоками и фрагментами блоков. Небольшие файлы, общий размер которых меньше размера блока, находятся в некотором числе фрагментов. Это позволяет избежать пустой потери дискового пространства (за счет допустимой цепы большей сложности кода ядра). Размер фрагмента выбирается во время создания файловой системы.

fsblkcnt_t f_blocks

Общее число блоков (в единицах f_bsize) в файловой системе.

fsblkcnt_t f_bfree

Общее число свободных блоков в файловой системе.

fsblkcnt_t f_bavail

Число блоков, которые действительно могут использоваться. Некоторые файловые системы резервируют часть блоков файловой системы для использования суперпользователем при заполнении файловой системы. Современные системы резервируют около 5 процентов, хотя это число может быть изменено администратором. (См. tune2fs(8) на системе GNU/Linux и tunefs(8) на системах Unix.)

fsfilcnt_t f_files

Общее число индексов («порядковых номеров файлов» на языке POSIX) в файловой системе. Это число обычно инициализируется и делается постоянным при создании файловой системы.

fsfilcnt_t f_ffree

Общее число свободных узлов.

fsfilcnt_t f_favail

Число индексов, которые действительно могут быть использованы. Некоторая часть индексов резервируются для суперпользователя, точно так же, как для блоков.

unsigned long int f_fsid

ID файловой системы. POSIX не определяет, что оно представляет, и это под Linux не используется.

unsigned long int f_flag

Флаги, дающие информацию о файловой системе. POSIX определяет два флага: ST_RDONLY для файловых систем только для чтения (таких, как CD-ROM) и ST_NOSUID, который запрещает использование битов setuid и setgid в исполняемых файлах. Системы GNU/Linux предусматривают дополнительные флаги: они перечислены в табл. 8.2.

Таблица 8.2. Значения GLIBC для f_flag

Флаг POSIX Значение
ST_MANDLOCK Осуществляет принудительное блокирование (см. раздел 14.2).
ST_NOATIME Не обновлять при каждом доступе время доступа
ST_NODEV Запрещает доступ через файлы устройств
ST_NODIRATIME Не обновлять поле времени доступе каталогов
ST_NOEXEC Запрещает исполнение двоичных файлов
ST_NOSUID ? Файловая система запрещает использование битов setuid и setgid.
ST_RDONLY ? Файловая система только для чтения.
ST_SYNCHRONOUS Любая запись осуществляется синхронно (см. раздел 4.6.3).

unsigned long int f_namemax

Максимальная длина имени файла. Это относится к каждому отдельному компоненту в имени пути; другими словами, максимальная длина для элемента каталога

Типы fsblkcnt_t и fsfilcnt_t определены в <sys/types.h>. Они обычно являются unsigned long, но на современных системах они могут быть даже 64-разрядными, поскольку диски стали очень большими. Следующая программа, ch08-statvfs.c, показывает, как использовать statvfs():

1  /* ch08-statvfs.с --- демонстрация statvfs */
2
3  /* ЗАМЕЧАНИЕ: специфично для GNU/Linux! */
4
5  #include <stdio.h>
6  #include <errno.h>
7  #include <mntent.h> /* для getmntent(), et al. */
8  #include <unistd.h> /* для getopt() */
9  #include <sys/types.h>
10 #include <sys/statvfs.h>
11

12 void process(const char *filename);
13 void do_statvfs(const struct mntent *fs);
14
15 int errors = 0;
16 char *myname;
17

18 /* main --- обработка опций */
19

20 int main(int argc, char **argv)
21 {
22  int c;
23  char *file = "/etc/mtab"; /* файл для чтения по умолчанию */
24
25  myname = argv[0];
26  while ((c = getopt(argc, argv, "f:")) != -1) {
27   switch (c) {
28   case 'f':
29    file = optarg;
30    break;
31   default:
32    fprintf(stderr, "usage: %s [-f fstab-file]n", argv[0]);
33    exit(1);
34   }
35  }
36
37  process(file);
38  return (errors != 0);
39 }
40
41 /* process --- чтение структур struct mntent из файла */
42
43 void process(const char *filename)
44 {
45  FILE* fp;
46  struct mntent *fs;
47
48  fp = setmntent(filename, "r"); /* только для чтения */
49  if (fp == NULL) {
50   fprintf(stderr, "%s: %s: could not open: %sn",
51    myname, filename, strerror(errno));
52   exit(1);
53  }
54
55  while ((fs = getmntent(fp)) != NULL)
56   do_statvfs(fs);
57
58  endmntent(fp);
59 }

Строки 1–59 в сущности те же самые, как и для ch08-mounted.c. main() обрабатывает командную стоку, a process() просматривает в цикле каждую смонтированную файловую систему. do_statvfs() осуществляет действительную работу, выводя для каждой интересующей файловой системы struct statvfs.

61  /* do_statvfs --- Использовать statvfs и вывести сведения */
62
63  void do_statvfs(const struct mntent *fs)
64  {
65   struct statvfs vfs;
66
67   if (fs->mnt_fsname[0] != '/') /* пропустить ненастоящие файловые системы */
68    return;
69
70   if (statvfs(fs->mnt_dir, &vfs) != 0) {
71    fprintf(stderr, "%s: %s: statvfs failed: %sn",
72     myname, fs->mnt_dir, strerror(errno));
73    errors++;
74    return;
75   }
76
77   printf("%s, mounted on %s:n", fs->mnt_dir, fs->mnt_fsname);
78   printf("tf_bsize: %ldn", (long)vfs.f_bsize);
79   printf("tf_frsize: %ldn", (long)vfs.f_frsize);
80   printf("tf_blocks: %lun", (unsigned long)vfs.f_blocks);
81   printf("tf_bfree: %lun", (unsigned long)vfs.f_bfree);
82   printf("tf_bavail: %lun", (unsigned long)vfs.f_bavail);
83   printf("tf_files: %lun", (unsigned long)vfs.f_files);
84   printf("tf_ffree: %lun", (unsigned long)vfs.f_ffree);
85   printf("tf_favail: %lun", (unsigned long)vfs.f_favail);
86   printf("tf_fsid: %#lxn", (unsigned long)vfs.f_fsid);
87
88   printf("tf_flag: ");
89   if (vfs.f_flag == 0)
90    printf("(none)n");
91   else {
92    if ((vfs.f_flag & ST_RDONLY) != 0)
93     printf("ST_RDONLY ");
94    if ((vfs.f_flag & ST_NOSUID) != 0)
95     printf("ST_NOSUID");
96    printf("n");
97   }
98
99   printf("tf_namemax: %#ldn", (long)vfs.f_namemax);
100 }

Строки 67–68 пропускают файловые системы, которые не основываются на реальных дисковых устройствах. Это означает, что файловые системы типа /proc или /dev/pts игнорируются. (Правда, эта проверка эвристическая, но она работает: в /etc/mtab смонтированные устройства перечислены по полному пути устройства: например, /dev/hda1.) Строка 70 вызывает statvfs() с соответствующей проверкой ошибок, а строки 77-99 выводят сведения.

Строки 89–96 имеют дело с флагами: отдельные биты информации, которые присутствуют или не присутствуют. Обсуждение того, как биты флагов используются в коде С, см. во врезке. Вот вывод ch08-statvfs:

$ ch08-statvfs /* Запуск программы */
/, mounted on /dev/hda2: /* Результаты для файловой системы ext2 */
f_bsize: 4096
f_frsize: 4096
f_blocks: 1549609
f_bfree: 316663
f_bavail: 237945
f_files: 788704
f_ffree: 555482
f_favail: 555482
f_fsid: 0
f_flag: (none)
f_namemax: 255
...
/win, mounted on /dev/hda1: /* Результаты для файл. системы vfat */
f_bsize: 4096
f_frsize: 4096
f_blocks: 2092383
f_bfree: 1391952
f_bavail: 1391952
f_files: 0
f_ffree: 0
f_favail: 0
f_fsid: 0
f_flag: ST_NOSUID
f_namemax: 260

Во время написания этого, для GLIBC 2.3.2 и ранее, GNU df не использует statvfs(). Это потому, что код читает /etc/mtab и вызывает stat() для каждой смонтированной файловой системы, чтобы найти ту, номер устройства которой совпадает с соответствующим аргументом для файла (или дескриптора файла). Для того, чтобы прочесть опции монтирования, коду нужно найти файловую систему, поэтому он может установить биты f_flag. Проблема в том, что stat() на смонтированной удаленной файловой системе, сервер которой недоступен, может висеть неопределенно долго, вызвав также зависание df. С тех пор эта проблема в GLIBC была исправлена, но df не будет изменяться в течение некоторого времени, так что она сможет продолжать работать на более старых системах.

ЗАМЕЧАНИЕ. Хотя POSIX определяет statvfs() и fstatvfs(), не все системы их поддерживают или поддерживают корректно. Многие системы (включая Linux, как вскоре будет описано), имеют свои собственные системные вызовы, предоставляющие сходную информацию. GNU df использует библиотечную процедуру для получения сведений о файловой системе; исходный файл для этой процедуры наполнен #ifdef для большого числа различных систем. Со временем ситуация с переносимостью должна улучшиться.

Битовые флаги

Обычной методикой, применимой во многих случаях, является использование набора значений флагов; когда флаг установлен (т.е. true), имеет место некоторый факт или применяется некоторое условие. Значения флагов определены либо через именованные константы #define, либо через перечисления. В данной главе API nftw() (описанный далее) также использует флаги. Для поля f_flag структуры struct statvfs есть только два флага:

#define ST_RDONLY 1 /* файловая система только для чтения */
#define ST_NOSUID 2 /* setuid/setgid не разрешены */

Физически каждая именованная константа представляет различные позиции битов в значении f_flag. Логически каждое значение представляет отдельный бит информации о состоянии; т.е. некоторый факт или условие, которое является или не является истинным для данного конкретного экземпляра struct statvfs.

Флаги устанавливаются, проверяются и очищаются с помощью побитовых операторов С. Например, statvfs() устанавливает эти флаги, используя побитовый оператор ИЛИ:

int statvfs(const char *path, struct statvfs *vfs) {
 /* заполнить большую часть *vfs */
 vfs->f_flag = 0; /* Убедиться, что начинается с нуля */
 if (файловая система только для чтения)
  vfs->f_flag |= ST_RDONLY; /* Добавить флаг ST_RDONLY */
 if (файловая система запрещает setuid)
  vfs->f_flag |= ST_NOSUID; /* Добавить флаг ST_NOSUID */
 /* оставшаяся часть процедуры */
}

Побитовый оператор И проверяет, установлен ли флаг, а сочетание побитовых операторов И и дополнения очищает флаг:

if ((vfs.f_flag & ST_RDONLY) != 0) /* True, если флаг ST_RDONLY */
 vfs.f_flag &= ~(ST_RDONLY|ST_NOSUID); /* Очистить оба флага */

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

Причина использования флагов кроется в том, что они обеспечивают значительную экономию пространства данных. Одно поле unsigned long дает возможность хранить по меньшей мере 32 отдельных бита информации. GLIBC (на момент написания) определяет 11 различных флагов для поля f_flag.[82] Если бы вы использовали для каждого флага отдельно поле char, это потребовало бы использования 11 байтов вместо четырех, используемых unsigned long. Если бы у вас было 32 флага, это были бы 32 байта вместо четырёх!

Оглавление книги


Генерация: 2.025. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз