Это на самом деле довольно просто, по крайней мере, если вам не нужны детали реализации.
Во-первых, в 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
вызове, хотя, как указано на странице руководства, это может иметь побочные эффекты. Приведенная ниже цитата взята из справочной страницы 5.11 файла BSD от 17 октября 2011 года.
Как правило, file пытается только прочитать и определить тип файлов аргументов, отчеты stat (2) которых являются обычными файлами. Это предотвращает проблемы, потому что чтение специальных файлов может иметь особые последствия. Задание этой -s
опции заставляет файл также читать файлы аргументов, которые являются блочными или символьными специальными файлами. Это полезно для определения типов файловой системы данных в разделах необработанного диска, которые являются блочными специальными файлами. Эта опция также заставляет файл игнорировать размер файла, как сообщает stat (2), поскольку в некоторых системах он сообщает нулевой размер для разделов необработанного диска.
strace file /proc/version
илиltrace -S /proc/version
, оптимизация довольно мала.stat()
Сначала он выполняет вызов и обнаруживает, что размер равен 0, пропуская, таким образом,open()
- но перед этим он загружает несколько магических файлов.