Это на самом деле довольно просто, по крайней мере, если вам не нужны детали реализации.
Во-первых, в Linux все файловые системы (ext2, ext3, btrfs, reiserfs, tmpfs, zfs, ...) реализованы в ядре. Некоторые могут перенести работу в пользовательский код через FUSE, а некоторые приходят только в форме модуля ядра (родной ZFS является ярким примером последнего из-за лицензионных ограничений), но в любом случае остается компонент ядра. Это важная основа.
Когда программа хочет прочитать файл, она вызовет различные вызовы системной библиотеки, которые в конечном итоге окажутся в ядре в виде последовательности open()
, read()
, close()
(возможно, с seek()
хорошая мера). Ядро берет предоставленный путь и имя файла и через уровень файловой системы и устройства ввода / вывода преобразует их в физические запросы чтения (а во многих случаях также запросы записи - например, обновления времени) в некоторое хранилище.
Однако нет необходимости переводить эти запросы специально в физическое постоянное хранилище. Контракт ядра заключается в том, что при выдаче определенного набора системных вызовов будет предоставлено содержимое соответствующего файла. Где именно в нашем физическом мире "файл" существует, это вторично.
В /proc
обычно монтируется так называемый procfs
. Это особый тип файловой системы, но, поскольку это файловая система, она на самом деле не отличается от, например, файловой системы ext3
смонтированной где-то. Таким образом, запрос передается в код драйвера файловой системы procfs, который знает обо всех этих файлах и каталогах и возвращает определенные фрагменты информации из структур данных ядра .
"Уровень хранения" в данном случае - это структуры данных ядра, а procfs
предоставляет чистый, удобный интерфейс для доступа к ним. Имейте в виду, что монтирование procfs в /proc
- это просто соглашение; Вы могли бы так же легко установить его в другом месте. Фактически, это иногда делается, например, в chroot-тюрьмах, когда выполняющемуся там процессу по какой-то причине требуется доступ к /proc.
Это работает так же, если вы записываете значение в некоторый файл; на уровне ядра это переводит в последовательность вызовов open()
, seek()
, write()
, close()
которые снова передаются драйверу файловой системы; опять же, в данном конкретном случае, код procfs.
Конкретная причина, по которой вы видите, что file
возвращается empty
заключается в том, что многие файлы, предоставляемые procfs, имеют размер 0 байт. Размер 0 байт, вероятно, является оптимизацией на стороне ядра (многие файлы в /proc являются динамическими и могут легко варьироваться по длине, возможно, даже от одного чтения к другому, и вычисление длины каждого файла в каждом прочитанном каталоге будет потенциально очень дорого). Исходя из комментариев к этому ответу, которые вы можете проверить в своей системе, запустив strace или аналогичный инструмент, file
сначала выполняет вызов stat()
для обнаружения каких-либо специальных файлов, а затем использует возможность, если размер файла отображается как 0, прервать и сообщить, что файл пуст.
Это поведение на самом деле задокументировано и может быть отменено путем указания -s
или --special-files
при вызове file
, хотя, как указано на странице руководства, это может иметь побочные эффекты. Приведенная ниже цитата взята из справочной страницы BSD file 5.11 от 17 октября 2011 года.
Обычно файл пытается только прочитать и определить тип файлов аргументов, отчеты stat(2) которых являются обычными файлами. Это предотвращает проблемы, потому что чтение специальных файлов может иметь особые последствия. Указание опции -s
заставляет файл также читать файлы аргументов, которые являются блочными или символьными специальными файлами. Это полезно для определения типов файловой системы данных в разделах необработанного диска, которые являются блочными специальными файлами. Эта опция также заставляет файл игнорировать размер файла, как сообщает stat(2), так как в некоторых системах он сообщает нулевой размер для разделов необработанного диска.