Литмир - Электронная Библиотека
A
A

  devtype = "char";

 else if (S_ISBLK(sbuf.st_mode))

  devtype = "block";

 else {

  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);

}

Вот что происходит при запуске программы:

$ <b>ch05-devnum /tmp </b>/* Попробовать не устройство */

/tmp is not a block or character device

$ <b>ch05-devnum /dev/null</b> /* Символьное устройство */

char: major: 1, minor: 3

$ <b>ch05-devnum /dev/hda2</b> /* Блочное устройство */

block: major: 3, minor: 2

К счастью, вывод согласуется с выводом

ls
, давая нам уверенность[59], что мы в самом деле написали правильный код.

Воспроизведение вывода ls замечательно и хорошо, но действительно ли это полезно? Ответ — да. Любое приложение, работающее с иерархиями файлов, должно быть способно различать различные типы файлов. Подумайте об архиваторе, таком как

tar
или
cpio
. Было бы пагубно, если бы такая программа рассматривала файл дискового устройства как обычный файл, пытаясь прочесть его и сохранить его содержимое в архиве! Или подумайте о
find
, которая может выполнять произвольные действия, основываясь на типе и других атрибутах файлов, с которыми она сталкивается, (
find
является сложной программой; посмотрите find(1), если вы с ней не знакомы.) Или даже нечто простое, как пакет, оценивающий свободное дисковое пространство, тоже должно отличать обычные файлы от всего остального.

5.4.4.2. Возвращаясь к V7

cat

В разделе 4.4.4 «Пример: Unix cat» мы обещали вернуться к программе V7

cat
, чтобы посмотреть, как она использует системный вызов
stat()
. Первая группа строк, использовавшая ее, была такой:

31 fstat(fileno(stdout), &amp;statb);

32 statb.st_mode &amp;= S_IFMT;

33 if (statb.st_mode != S_IFCHR &amp;&amp; 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.

50 fstat(fileno(fi), &amp;statb);

51 if (statb.st_dev == dev &amp;&amp; statb.st_ino == ino) {

52  fprintf(stderr, &quot;cat: input %s is output\n&quot;,

53   ffig ? &quot;-&quot; : *argv);

54  fclose(fi);

55  continue;

56 }

Если значения

st_dev
и
st_ino
входного файла совпадают с соответствующими значениями выходного файла,
cat
выдает сообщение и продолжает со следующего файла, указанного в командной строке.

Проверка сделана безусловно, хотя

dev
и
ino
устанавливаются, лишь если вывод не является файлом устройства. Это срабатывает нормально из-за того, как эти переменные объявлены:

int dev, ino = -1;

Поскольку

ino
инициализирован значением (-1), ни один действительный номер индекса не будет ему соответствовать[60]. То, что
dev
не инициализирован так, является небрежным, но не представляет проблемы, поскольку тест в строке 51 требует, чтобы были равными значения как устройства, так и индекса. (Хороший компилятор выдаст предупреждение, что
dev
используется без инициализации: '
gcc -Wall
' сделает это.)

Обратите также внимание, что ни один вызов

fstat()
не проверяется на ошибки. Это также небрежность, хотя не такая большая, маловероятно, что
fstat()
завершится неудачей с действительным дескриптором файла

Проверка того, что входной файл не равен выходному файлу, осуществляется лишь для файлов, не являющихся устройствами. Это дает возможность использовать

cat
для копирования ввода из файлов устройств в самих себя, как в случае с терминалами:

$ <b>tty</b> /* Вывести имя устройства текущего терминала */

/dev/pts/3

$ <b>cat /dev/pts/3 &gt; /dev/pts/3</b> /* Копировать ввод от клавиатуры на экран */

<b>this is a line of text</b> /* Набираемое в строке */

this is a line of text /* cat это повторяет */

5.4.5. Работа с символическими ссылками

В общем, символические ссылки ведут себя подобно прямым ссылкам; файловые операции, такие, как

open()
и
stat()
, применяются к указываемому файлу вместо самой символической ссылки. Однако, бывают моменты, когда в самом деле необходимо работать с символической ссылкой вместо файла, на которую она указывает.

По этой причине существует системный вызов

lstat()
. Он действует точно также, как
stat()
, но если проверяемый файл окажется символической ссылкой, возвращаемые сведения относятся к символической ссылке, а не к указываемому файлу. А именно:

вернуться

59

Технический термин warm fuzzyПримеч. автора.

вернуться

60

Это утверждение было верно для V7, на современных системах больше нет таких гарантий — Примеч. автора.

58
{"b":"576259","o":1}