Исторически (до V7 UNIX или около 1979 года) read
системный вызов работал как с файлами, так и с каталогами. read
on каталог вернул бы простую структуру данных, которую пользовательская программа проанализировала бы для получения записей каталога. Действительно, ls
инструмент V7 сделал именно это - 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 и не затрагивалась в течение очень долгого времени. Часть меня подозревает, что это сохраняется по какой-то причине обратной совместимости, но это может быть так же легко, как тот факт, что никто не заботится о том, чтобы удалить эту функцию, поскольку она на самом деле ничего не вредит.