Linux программирование в примерах
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К счастью, вывод согласуется с выводом
, давая нам уверенность [59], что мы в самом деле написали правильный код.lsВоспроизведение вывода ls замечательно и хорошо, но действительно ли это полезно? Ответ — да. Любое приложение, работающее с иерархиями файлов, должно быть способно различать различные типы файлов. Подумайте об архиваторе, таком как
илиtar. Было бы пагубно, если бы такая программа рассматривала файл дискового устройства как обычный файл, пытаясь прочесть его и сохранить его содержимое в архиве! Или подумайте оcpio, которая может выполнять произвольные действия, основываясь на типе и других атрибутах файлов, с которыми она сталкивается, (findявляется сложной программой; посмотрите find(1), если вы с ней не знакомы.) Или даже нечто простое, как пакет, оценивающий свободное дисковое пространство, тоже должно отличать обычные файлы от всего остального.find5.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(). Строка 32 отбрасывает всю информацию вstatbза исключением типа файла, используя логическое AND с маскойstatb.st_mode. Строка 33 проверяет, что используемый для стандартного вывода файл не является файлом устройства. В таком случае программа сохраняет номера устройства и индекса вS_IFMTиdev. Эти значения затем проверяются для каждого входного файла в строках 50–56.ino50 fstat(fileno(fi), &statb);51 if (statb.st_dev == dev && statb.st_ino == ino) {52 fprintf(stderr, "cat: input %s is output\n",53 ffig ? "-" : *argv);54 fclose(fi);55 continue;56 }Если значения
иst_devвходного файла совпадают с соответствующими значениями выходного файла,st_inoвыдает сообщение и продолжает со следующего файла, указанного в командной строке.catПроверка сделана безусловно, хотя
иdevустанавливаются, лишь если вывод не является файлом устройства. Это срабатывает нормально из-за того, как эти переменные объявлены:inoint dev, ino = -1;Поскольку
инициализирован значением (-1), ни один действительный номер индекса не будет ему соответствовать [60]. То, чтоinoне инициализирован так, является небрежным, но не представляет проблемы, поскольку тест в строке 51 требует, чтобы были равными значения как устройства, так и индекса. (Хороший компилятор выдаст предупреждение, чтоdevиспользуется без инициализации: 'dev' сделает это.)gcc -WallОбратите также внимание, что ни один вызов
не проверяется на ошибки. Это также небрежность, хотя не такая большая, маловероятно, чтоfstat()завершится неудачей с действительным дескриптором файлаfstat()Проверка того, что входной файл не равен выходному файлу, осуществляется лишь для файлов, не являющихся устройствами. Это дает возможность использовать
для копирования ввода из файлов устройств в самих себя, как в случае с терминалами:cat$ <b>tty</b> /* Вывести имя устройства текущего терминала *//dev/pts/3$ <b>cat /dev/pts/3 > /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()