$ ls -l /dev/null /* Показать сведения также для /dev/null */
crw-rw-rw- 1 root root 1, 3 Aug 31 2002 /dev/null
Вместо размера файла
ls
отображает старший и младший номера. В случае жесткого диска
/dev/hda
представляет диск в целом,
/dev/hda1
,
/dev/hda2
и т.д. представляют разделы внутри диска. У них у всех общий старший номер устройства (3), но различные младшие номера устройств.
Обратите внимание, что дисковые устройства являются блочными устройствами, тогда как
/dev/null
является символьным устройством. Блочные и символьные устройства являются отдельными сущностями; даже если символьное устройство и блочное устройство имеют один и тот же старший номер устройства, они необязательно связаны
Старший и младший номера устройства можно извлечь из значения
dev_t
с помощью функций
major
и
minor
, определенных в
<sys/sysmacros.h>
:
#include <sys/types.h> /* Обычный */
#include <sys/sysmacros.h>
int major(dev_t dev); /* Старший номер устройства */
int minor(dev_t dev); /* Младший номер устройства */
dev_t makedev(int major, int minor); /* Создать значение dev_t */
(Некоторые системы реализуют их в виде макросов.)
Функция
makedev
идет другим путем; она принимает отдельные значения старшего и младшего номеров и кодирует их в значении
dev_t
. В других отношениях ее использование выходит за рамки данной книги; патологически любопытные должны посмотреть mknod(2).
fprintf(stderr, "%s is not a block or character device\n",
argv[1]);
exit(1);
}
printf("%s: major: %d, minor: %d\n", devtype,
major(sbuf.st_rdev), minor(sbuf.st_rdev));
exit(0);
}
Вот
что происходит при запуске программы:
$ ch05-devnum /tmp /* Попробовать не устройство */
/tmp is not a block or character device
$ ch05-devnum /dev/null /* Символьное устройство */
char: major: 1, minor: 3
$ ch05-devnum /dev/hda2 /* Блочное устройство */
block: major: 3, minor: 2
К счастью, вывод согласуется с выводом
ls
, давая нам уверенность [59] , что мы в самом деле написали правильный код.
Воспроизведение вывода ls замечательно и хорошо, но действительно ли это полезно? Ответ — да. Любое приложение, работающее с иерархиями файлов, должно быть способно различать различные типы файлов. Подумайте об архиваторе, таком как
tar
или
cpio
. Было бы пагубно, если бы такая программа рассматривала файл дискового устройства как обычный файл, пытаясь прочесть его и сохранить его содержимое в архиве! Или подумайте о
find
, которая может выполнять произвольные действия, основываясь на типе и других атрибутах файлов, с которыми она сталкивается, (
find
является сложной программой; посмотрите find(1), если вы с ней не знакомы.) Или даже нечто простое, как пакет, оценивающий свободное дисковое пространство, тоже должно отличать обычные файлы от всего остального.
59
Технический термин warm fuzzy — Примеч. автора.
5.4.4.2. Возвращаясь к V7
cat
В разделе 4.4.4 «Пример: Unix cat» мы обещали вернуться к программе V7
cat
, чтобы посмотреть, как она использует системный вызов
stat
. Первая группа строк, использовавшая ее, была такой:
31 fstat(fileno(stdout), &statb);
32 statb.st_mode &= S_IFMT;
33 if (statb.st_mode != S_IFCHR && statb.st_mode != S_IFBLK) {
34 dev = statb.st_dev;
35 ino = statb.st_ino;
36 }
Этот код теперь должен иметь смысл. В строке 31 вызывается
fstat
для стандартного вывода, чтобы заполнить структуру
statb
. Строка 32 отбрасывает всю информацию в
statb.st_mode
за исключением типа файла, используя логическое AND с маской
S_IFMT
. Строка 33 проверяет, что используемый для стандартного вывода файл не является файлом устройства. В таком случае программа сохраняет номера устройства и индекса в
dev
и
ino
. Эти значения затем проверяются для каждого входного файла в строках 50–56.