Книга: 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 байта вместо четырёх!
- 14.2.2. Блокировка POSIX: fcntl() и lockf()
- Стиль написания исходного кода
- 9.1.6.1. Использование функций POSIX: wait() и waitpid()
- Posix
- Стиль и позиция
- 12.6.2. Функции POSIX: random() и srandom()
- Один стиль для всех страниц сайта
- Стиль жизни
- Восстановление в POSIX
- 2.1.1. Соглашения POSIX
- 2.3.3.2. Длинные опции в стиле POSIX
- 4.3.2. Стиль сообщения об ошибках