Я могу cat /dev
, я могу ls /dev
, я не могу less /dev
. Почему cat
позволяет мне cat
этому каталогу, но без других каталогов?
2 ответа
Исторически (до V7 UNIX или около 1979 года) системный вызов read
работал как с файлами, так и с каталогами. read
по каталогу вернет простую структуру данных, которую пользовательская программа будет анализировать для получения записей каталога. Действительно, инструмент V7 ls
делал именно это - read
каталог, анализировал результирующую структуру данных, выводил в формате структурированного списка.
Поскольку файловые системы становились все более сложными, эта "простая" структура данных усложнялась до такой степени, что была добавлена библиотечная функция readdir
чтобы помочь программам анализировать выходные данные read(directory)
. Различные системы и файловые системы могут иметь разные форматы на диске, что усложняется.
Когда Sun представила сетевую файловую систему (NFS), они хотели полностью абстрагироваться от структуры каталогов на диске. Вместо того, чтобы их read(directory)
возвращало независимое от платформы представление каталога, однако они добавили новый системный вызов - getdirents
- и запретили read
в сетевых каталогах. Этот системный вызов был быстро адаптирован для работы со всеми каталогами в различных вариантах UNIX, что сделало его стандартным способом получения содержимого каталогов. (История извлечена из https://utcc.utoronto.ca/~cks/space/blog/unix/ReaddirHistory)
Поскольку readdir
теперь является способом чтения каталогов по умолчанию, read(directory)
обычно не реализуется (возвращая -EISDIR) в большинстве современных операционных систем (например, QNX является заметным исключением, которое реализует readdir
как read(directory)
). Тем не менее, с дизайном "виртуальной файловой системы" в большинстве современных ядер, фактически зависит от отдельной файловой системы, работает ли чтение каталога или нет.
И действительно, в macOS файловая система devfs
лежащая в основе точки монтирования /dev
действительно поддерживает чтение (https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/miscfs/devfs/devfs_vnops.c # L629):
static int
devfs_read(struct vnop_read_args *ap)
{
devnode_t * dn_p = VTODN(ap->a_vp);
switch (ap->a_vp->v_type) {
case VDIR: {
dn_p->dn_access = 1;
return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
Это явно вызывает READDIR
если вы пытаетесь читать /dev
(чтение файлов в /dev
обрабатывается отдельной функцией - devfsspec_read
). Таким образом, если программа вызывает системный вызов read
для /dev
, она завершится успешно и получит список каталогов!
Эта функция, по сути, является пережитком с самых ранних дней UNIX и не затрагивалась в течение очень долгого времени. Часть меня подозревает, что это сохраняется по какой-то причине обратной совместимости, но это может быть так же легко, как тот факт, что никто не заботится о том, чтобы удалить эту функцию, поскольку это на самом деле ничего не вредит.
Меньше просмотрщик текстовых файлов, cat - инструмент для копирования произвольных данных. Таким образом, less выполняет свою собственную проверку, чтобы убедиться, что вы не открываете что-то, что будет содержать большие объемы данных или ведет себя очень странно. С другой стороны, у cat нет такой проверки вообще - если ядро позволяет вам что-то открыть (даже если это канал, устройство или что-то еще хуже), cat прочитает это.
Так почему же ОС позволяет cat открывать каталоги? Традиционно в системах в стиле BSD все каталоги могут быть прочитаны как файлы, и именно так программы будут перечислять каталог в первую очередь: просто интерпретируя структуры директив, хранящиеся на диске.
Позднее эти структуры на диске начали расходиться с директивой, используемой ядром: где ранее каталог представлял собой линейный список, более поздние файловые системы начали использовать хеш-таблицы, B-деревья и так далее. Так что чтение каталогов напрямую стало не простым делом - в ядре появились специальные функции для этого. (Я не уверен, что это было основной причиной, или они были добавлены прежде всего по другим причинам, таким как кеширование.)
Некоторые системы BSD продолжают открывать все каталоги для чтения; Я не знаю, дают ли они вам необработанные данные с диска, или вместо этого они возвращают эмулированный список директив, или позволяют ли они решать драйверу файловой системы.
Так что, возможно, macOS является одной из тех операционных систем, где ядро позволяет это, пока файловая система предоставляет данные. И разница в том, что /dev
находится в файловой системе devfs
которая была написана для того, чтобы позволить это в первые дни, в то время как /
находится в файловой системе APFS, которая не использовала эту функцию в наше время как ненужную.
Отказ от ответственности: я на самом деле не проводил никаких исследований на BSD или MacOS. Я просто обожаю это.