![](/files/books/160/oblozhka-knigi-linux-programmirovanie-v-primerah-258381.jpg)
Текст книги "Linux программирование в примерах"
Автор книги: Арнольд Роббинс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 20 (всего у книги 55 страниц)
mount
Команда mount
монтирует файловые системы, соединяя их содержимое в системную иерархию файлов в их точках монтирования. Под GNU/Linux это иногда запутано, поскольку приходится иметь дело со всеми известными типами файловых систем и их опциями. Обычно запустить mount
может лишь root
, хотя в некоторых случаях можно сделать исключения, как в случае, обсуждаемом далее в главе.
Файловую систему вы указываете с помощью опции -t
:
mount [опции] устройство точка_монтирования
Например (#
является приглашением для root
):
# mount -t iso9660 /dev/cdrom /mnt/cdrom /* Монтировать CD-ROM */
# mount -t vfat /dev/fd0 /mnt/floppy /* Монтировать гибкий диск MS-DOS */
# mount -t nfs files.example.com:/ /mnt/files /* Монтировать файловую систему NFS */
Можно использовать '-t auto
', чтобы заставить mount
угадать тип файловой системы. Это обычно работает, хотя если вы точно знаете, каков тип файловой системы, опция '-t
' помогает указать тип и избежать вероятности ошибочного определения файловой системы, mount
по умолчанию угадывает нужный тип, поэтому '-t auto
' не является абсолютно необходимым.
Системы GNU/Linux предусматривают специальную разновидность монтирования посредством кольцевого (loopback) устройства. Таким способом образ файловой системы, содержащийся в обычном файле, может быть смонтирован, как если бы это было настоящее дисковое устройство. Эта возможность очень полезна, например, при использовании образов CD-ROM. Она позволяет создать образ и испытать его без необходимости прожига на болванке CD с последующим его монтированием. Следующий пример использует первый образ CD из дистрибутива GNU/Linux RedHat 9:
# ls -l shrike-i386-discl.iso /* Исследование файла образа CD */
-rw-r–r– 1 arnold devel 668991488 Apr 11 05:13 shrike-i386-discl.iso
# mount -t iso9660 -o ro,loop shrike-i386-discl.iso /mnt/cdrom
/* Смонтировать его в /mnt/cdrom */
# cd /mnt/cdrom /* Перейти туда */
# ls /* Посмотреть файлы */
autorun README.it RELEASE-NOTES-fr.html
dosutils README.ja RELEASE-NOTES.html
EULA README.ko RELEASE-NOTES-it.html
GPL README.pt RELEASE-NOTES-ja.html
images README.pt_BR RELEASE-NOTES-ko.html
isolinux README.zh_CN RELEASE-NOTES-pt_BR.html
README README.zh_TW RELEASE-NOTES-pt.html
README-Accessibility RedHat RELEASE-NOTES-zh_CN.html
README.de RELEASE-NOTES RELEASE-NOTES-zh_TW.html
README.es RELEASE-NOTES-de.html RPM-GPG-KEY
README.fr RELEASE-NOTES-es.html TRANS.TBL
# cd /* Сменить */
# umount /mnt/cdrom /* Демонтировать */
Возможность монтирования таким способом образа ISO 9660 особенно полезна при тестировании сценариев, создающих образы CD. Вы можете создать образ в обычном файле, смонтировать его и проверить, что он подготовлен правильно. Затем, убедившись, что все в порядке, можно скопировать образ на записываемый CD («прожечь» CD). Возможность кольцевого устройства полезна также для монтирования образов гибких дисков
umount
Команда umount
демонтирует файловую систему, удаляя ее содержимое из системной иерархии файлов. Использование следующее:
umount файл-или-устройство
Демонтируемая файловая система не должна быть занята. Это означает, что нет процессов с открытыми в файловой системе файлами и что ни у одного процесса текущий рабочий каталог не находится в этой файловой системе:
$ mount /* Показать, что смонтировано */
/dev/hda2 on / type ext3 (rw) /* / находится на настоящем устройстве */
none on /proc type proc (rw)
usbdevfs on /proc/bus/usb type usbdevfs (rw)
/dev/hda5 on /d type ext3 (rw) /* To же c /d */
none on /dev/pts type devpts (rw,gid=5,mode=620)
none on /dev/shm type tmpfs (rw)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
$ su /* Переключиться на суперпользователя */
Password: /* Пароль не отображается */
# cd /d /* Сделать /d текущим каталогом */
# umount /d /* Попытка демонтировать /d */
umount: /d: device is busy /* Doesn't work; it's still in use */
# cd / /* Сменить /d */
# umount /d /* Еще одна попытка демонтировать /d */
# /* Молчание золото: umount работает */
Файл /etc/fstab
[81]81
Ha GNU/Linux и большинстве систем Solaris и некоторые системы на основе System V Release 4 используют /etc/vfstab
, возможно, с другим форматом – Примеч. автора.
[Закрыть] перечисляет файловые системы, которые могут быть смонтированы. Большинство из них монтируются автоматически, когда система загружается. Формат следующий:
устройство точка-монтирования тип-фс опции dump-freq fsck-pass
(dump-freq
и fsck-pass
являются административными особенностями, не относящимися к настоящему обсуждению). Например, на нашей системе файл выглядит следующим образом:
$ cat /etc/fstab
# device mount-point type options freq passno
/dev/hda3 / ext3 defaults 1 1 /* Корневая файловая система */
/dev/hda5 /d ext3 defaults 1 2
none /dev/pts devpts gid=5,mode=620 0 0
none /proc proc defaults 0 0
none /dev/shm tmpfs defaults 0 0
# Windows partition:
/dev/hda1 /win vfat noauto,defaults,user,uid=2076,gid=10 0 0
/dev/hda3 swap swap defaults 0 0
/dev/cdrom /mnt/cdrom iso9660 noauto,owner,ro 0 0 /* Монтируемый */
/dev/fd0 /mnt/floppy auto noauto,owner 0 0/* Гибкий диск, то же самое */
Разрешены комментарии, начинающиеся с #
. Вскоре, в разделе 8.2 1 «Использование опций монтирования», будет обсуждение различных опций.
Тот же самый формат файла используется для /etc/mtab
, куда mount
записывает информацию о файловых системах, когда они смонтированы; umount
удаляет информацию из этого файла, когда файловая система демонтирована:
$ cat /etc/mtab
/dev/hda2 / ext3 rw 0 0
none /proc proc rw 0 0
usbdevfs /proc/bus/usb usbdevfs rw 0 0
/dev/hda5 /d ext3 rw 0 0
none /dev/pts devpts rw,gid=5,mode=620 0 0
none /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/dev/hda1 /win vfat rw,noexec,nosuid,nodev,uid=2076,gid=10,user=arnold 0 0
Ядро делает доступным (почти) те же самые сведения в /proc/mounts
, в том же формате:
$ cat /proc/mounts
rootfs / rootfs rw 0 0
/dev/root / ext3 rw 0 0
/proc /proc proc rw 0 0
usbdevfs /proc/bus/usb usbdevfs rw 0 0
/dev/hda5 /d ext3 rw 0 0
none /dev/pts devpts rw 0 0
none /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/dev/hda1 /win vfat rw,nosuid,nodev,noexec 0 0
Обратите внимание, что в /etc/mtab
есть некоторые сведения, которые отсутствуют в /proc/mounts
. (Например, см. строку для точки монтирования /win
.) С другой стороны, возможно (используя 'mount -f
') помещать в /etc/mtab
элементы, которые не являются настоящими (эта практика имеет свое применение, см. mount(8)). Подводя итог, /proc/mounts
всегда описывает, что смонтировано в действительности; однако, /etc/mtab
содержит сведения об опциях mount
, которых нет в /proc/mounts
. Поэтому, чтобы получить полную картину, вам может понадобиться прочесть оба файла
Команда mount
поддерживает опции, которые контролируют то, какие операции ядро будет позволять или не позволять применять к файловой системе. Их приличное количество. Лишь две из них полезны на самом деле в командной строке:
ro
Монтирует файловую систему только для чтения. Это необходимо для устройств только для чтения, таких, как CD-ROM и DVD.
loop
Использует кольцевое устройство для интерпретации обычного файла в качестве файловой системы. Пример этого мы показали ранее (см. раздел 8.1.3 «Монтирование файловых систем: mount
»).
Опции передаются с опцией командной строки -о и могут группироваться, отделяясь запятыми. Например, вот использованная ранее командная строка:
mount -t iso9660 -о ro,loop shrike-i386-discl.iso /mnt/cdrom
Оставшиеся опции предназначены для использования в /etc/fstab
(хотя они могут использоваться также и в командной строке). Следующий список представляет те опции, которые, как мы полагаем, наиболее важны в повседневном использовании.
auto
, noauto
Файловые системы, помеченные auto
, должны монтироваться при загрузке системы через 'mount -а
' (монтировать все файловые системы). Файловые системы noauto
должны монтироваться вручную. Они по-прежнему находятся в /etc/fstab
вместе с другими файловыми системами. (См., например, элемент /win
для показанного ранее файла /etc/fstab
).
defaults
Использует по умолчанию опции rw
, suid
, dev
, exec
, auto
, nouser
и async
. (async
является продвинутой опцией, повышающей производительность ввода/вывода).
dev
, nodev
Позволяет (не позволяет) использовать в файловой системе файлы символьных или блочных устройств.
exec
, noexec
Позволяет (не позволяет) запускать в файловой системе двоичные исполняемые файлы.
user
, nouser
Позволяет (не позволяет) монтировать данную файловую систему любому пользователю. Это полезно для CD-ROM; даже если вы находитесь на рабочей станции с одним пользователем, удобно не переключаться на root
просто для монтирования CD. Демонтировать файловую систему может лишь пользователь, который смонтировал ее. user
предполагает наличие опций noexec
, nosuid
и nodev
.
suid
, nosuid
Поддерживает (не поддерживает) биты setuid
и setgid
исполняемых файлов в файловой системе.
rw
Монтирует файловую систему в режиме для чтения/записи.
Опции nodev
, noexec
и nosuid
особенно ценны для безопасности на файловых системах гибких дисков и CD-ROM. Рассмотрите студенческую среду, в которой студентам разрешено монтировать собственные гибкие диски или CD. Тривиально смастерить для жесткого диска файловую систему с оболочкой setuid-root или с файлом устройства с разрешением записи для всех, которая позволила бы предприимчивому пользователю изменить права доступа к системным файлам.
У каждой файловой системы есть специфичные для нее опции. Важной опцией для ext2
и ext3
является grpid
. Мы отложим обсуждение этой опции до раздела 11.5.1 «Группа по умолчанию для новых файлов и каталогов». Подробности для всех поддерживаемых файловых систем можно найти в справочной странице mount(8).
В качестве конкретного примера вернемся к строчке для раздела Windows в нашей системе:
# device mount-point type options freq passno
/dev/hda1 /win vfat noauto,defaults,user,uid=2076,gid=10 0 0
Опция noauto
предотвращает монтирование раздела Windows при загрузке. Опция defaults
та же самая, что rw
, suid
, dev
, exec
, async
. Опция user
позволяет нам монтировать файловую систему, не будучи root
. Опции uid=
и gid=
делает файлы в /win
принадлежащими нам, так что нам не нужно иметь права root
при работе с этим разделом.
getmntent()
Любой из файлов /etc/fstab
, /etc/mtab
и /proc/mounts
может быть прочитан программное использованием набора процедур getmntent()
:
#include
#include
FILE *setmntent(const char *filename, const char *type);
struct mntent *getmntent(FILE *filep);
int addmntent(FILE *filep, const struct mntent *mnt);
int endmntent(FILE *filep);
char *hasmntopt(const struct mntent *mnt, const char *opt);
setmntent()
открывает файл, содержащий элементы точек монтирования. Аргумент filename
является файлом, который должен быть открыт. Аргумент type
похож на второй аргумент fopen()
, указывая доступ для чтения, записи или чтения/записи. (Рассмотрите команду mount
, которой приходится добавлять элемент к /etc/mtab
для каждой файловой системы, которую она монтирует, и umount
, которая должна удалять этот элемент.) Затем возвращаемое значение типа FILE*
используется с оставшимися процедурами.
getmntent()
читает файл, возвращая указатель на static struct mntent
, которая заполнена соответствующими значениями. Это статическое хранилище переписывается при каждом вызове. Когда больше не осталось элементов, она возвращает NULL
. (Это сходно с процедурами для чтения файлов паролей и групп; см раздел 6.3 «Имена пользователей и групп».)
addmntent()
вызывается для добавления сведений в конец открытого файла, она предназначена для использования функцией mount
.
endmntent()
закрывает открытый файл; вызывайте ее после завершения обработки. Не вызывайте просто fclose()
; может потребоваться очистить другие внутренние структуры данных, связанные с переменной FILE*
.
hasmntopt()
является более специализированной функцией. Она просматривает struct mntent
, переданную в качестве первого параметра, в поисках опции mount
, совпадающей со вторым аргументом. Если опция найдена, она возвращает адрес совпадающей подстроки. В противном случае возвращается NULL
.
Поля в struct mntent
непосредственно соответствуют полям в файле /etc/fstab
. Структура выглядит следующим образом:
struct mntent {
char *mnt_fsname; /* Устройство или сервер для файл. С-мы. */
char *mnt_dir; /* Каталог для монтирования. */
char *mnt_type; /* Тип файловой системы: ufs, nfs и т.д. */
char *mnt_opts; /* Отделяемые запятыми опции для fs. */
int mnt_freq; /* Частота дампа (в днях). */
int mnt_passno; /* Номер для 'fsck'. */
};
Обычным принципом работы со смонтированными файловыми системами является создание внешнего цикла, читающего /etc/mtab
, обрабатывая по одной struct mntent
за раз. Наш первый пример, ch08-mounted.c
, делает именно это:
1 /* ch08-mounted.с – вывод списка смонтированных файловых
2 систем */
3 /* ЗАМЕЧАНИЕ: специфично для GNU/Linux! */
4
5 #include
6 #include
7 #include
8 #include
9
10 void process(const char *filename);
11 void print_mount(const struct mntent *fs);
12
13 char *myname;
14
15 /* main – обработка опций */
16
17 int main(int argc, char **argv)
18 {
19 int c;
20 char *file = "/etc/mtab"; /* файл по умолчанию для чтения */
21
22 myname = argv[0];
23 while ((c = getopt(argc, argv, "f:")) != -1) {
24 switch (c) {
25 case 'f':
26 file = optarg;
27 break;
28 default:
29 fprintf(stderr, "usage: %s [-f fstab-file]n", argv[0]);
30 exit(1);
31 }
32 }
33
34 process(file);
35 return 0;
36 }
37
38 /* process – прочесть структуры struct mntent из файла */
39
40 void process(const char *filename)
41 {
42 FILE *fp;
43 struct mntent *fs;
44
45 fp = setmntent(filename, "r"); /* только для чтения */
46 if (fp == NULL) {
47 fprintf(stderr, "%s: %s: could not open: %sn",
48 myname, filename, strerror(errno));
49 exit(1);
50 }
51
52 while ((fs = getmntent(fp)) != NULL)
53 print_mount(fs);
54
55 endmntent(fp);
56 }
57
58 /* print_mount – вывод одного смонтированного элемента */
59
60 void print_mount(const struct mntent *fs)
61 {
62 printf("%s %s %s %s %d %dn",
63 fs->mnt_fsname,
64 fs->mnt_dir,
65 fs->mnt_type,
66 fs->mnt_opts,
67 fs->mnt_freq,
68 fs->mnt_passno);
69 }
В отличие от большинства программ, которые мы до сих пор видели, эта специфична для Linux. Во многих Unix-системах есть схожие процедуры, но их идентичность не гарантируется.
По умолчанию, ch08-mounted
читает /etc/mtab
, выводя сведения о каждой смонтированной файловой системе. Опция -f
позволяет указать другой файл для чтения, такой, как /proc/mounts
или даже /etc/fstab
.
Функция main()
обрабатывает командную строку (строки 23–32) и вызывает для указанного файла process()
. (Эта программа следует нашему стандартному шаблону.)
process()
, в свою очередь, открывает файл (строка 45) и проходит в цикле через каждую возвращённую файловую систему (строки 52–53). После завершения она закрывает файл (строка 55).
Функция print_mount()
выводит информацию из struct mnent
. Вывод во многом напоминает вывод 'cat /etc/mtab
':
$ ch08-mounted /* Запуск программы */
/dev/hda2 / ext3 rw 0 0
none /proc proc rw 0 0
usbdevfs /proc/bus/usb usbdevfs rw 0 0
/dev/hda5 /d ext3 rw 0 0
none /dev/pts devpts rw,gid=5,mode=620 0 0
none /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/dev/hda1 /win vfat rw,noexec,nosuid,nodev,uid=2076,gid=10,user=arnold 0 0
Вывод сведений о файловой системе, рассмотренный ранее – это хорошо и замечательно, но это не захватывает. Раз мы знаем, что определенная точка монтирования представляет файловую систему, нам нужны сведения о файловой системе. Это даст нам возможность выводить вещи наподобие сведений, полученных с помощью df
и 'df -i
'.
$ df /* Показать свободное/используемое пространство */
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/hda2 6198436 4940316 943248 84% /
/dev/hda5 61431520 27618536 30692360 48% /d
none 256616 0 256616 0% /dev/shm
/dev/hda1 8369532 2784700 5584832 34% /win
$ df -i /* Показать свободные/используемые индексы */
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/hda2 788704 233216 555488 30% /
/dev/hda5 7815168 503243 7311925 7% /d
none 64154 1 64153 1% /dev/shm
/dev/hda1 0 0 0 – /win
statvfs()
и fstatvfs()
На ранних системах Unix была только одна разновидность файловой системы. Для них было достаточно, если df
считывала суперблок каждой смонтированной файловой системы, извлекала значимые сведения и красиво форматировала их для отображения. (Суперблок обычно был вторым блоком в файловой системе; первым был загрузочный блок, содержащий загрузочный код).
Однако в современном мире такой подход был бы непригодным. POSIX предоставляет расширение XSI для получения доступа к этой информации. Главная функция называется statvfs()
(«vfs» часть происходит от лежащей в основе технологии SunOS, использованной позже в System V Release 4, которая называется виртуальной файловой системой.) Имеется две функции:
#include
#include
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
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
определены в
. Они обычно являются unsigned long
, но на современных системах они могут быть даже 64-разрядными, поскольку диски стали очень большими. Следующая программа, ch08-statvfs.c
, показывает, как использовать statvfs()
:
1 /* ch08-statvfs.с – демонстрация statvfs */
2
3 /* ЗАМЕЧАНИЕ: специфично для GNU/Linux! */
4
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
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, как вскоре будет описано), имеют свои собственные системные вызовы, предоставляющие сходную информацию. GNUdf
использует библиотечную процедуру для получения сведений о файловой системе; исходный файл для этой процедуры наполнен#ifdef
для большого числа различных систем. Со временем ситуация с переносимостью должна улучшиться.
Битовые флаги
Обычной методикой, применимой во многих случаях, является использование набора значений флагов; когда флаг установлен (т.е. true), имеет место некоторый факт или применяется некоторое условие. Значения флагов определены либо через именованные константы
#define
, либо через перечисления. В данной главе APInftw()
(описанный далее) также использует флаги. Для поля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]82
См./usr/include/bits/statvfs.h
на системе GNU/Linux – Примеч. автора.
[Закрыть] Если бы вы использовали для каждого флага отдельно полеchar
, это потребовало бы использования 11 байтов вместо четырех, используемыхunsigned long
. Если бы у вас было 32 флага, это были бы 32 байта вместо четырёх!