Как Linux различает реальные и несуществующие (например, устройства) файлы?

28

Это вопрос довольно низкого уровня, и я понимаю, что это может быть не лучшее место, чтобы спросить. Но, это казалось более подходящим, чем любой другой сайт SE, так что здесь идет.

Я знаю, что в файловой системе Linux некоторые файлы действительно существуют , например, такие , /usr/bin/bashкоторые существуют. Тем не менее, (насколько я понимаю), некоторые из них также фактически не существует как таковой и более виртуальных файлов, например: /dev/sda, /proc/cpuinfoи т.д. Мои вопросы (они два, но слишком тесно связаны , чтобы быть отдельные вопросы):

  • Как работает ядро ​​Linux, являются ли эти файлы настоящими (и, следовательно, читают их с диска) или нет, когда выдается команда чтения (или такая)?
  • Если файл ненастоящий: например, чтение из /dev/randomвернет случайные данные, а чтение из /dev/nullвернет EOF. Как это работает, какие данные для чтения из этого виртуального файла (и, следовательно, что делать, когда / если данные также записываются в виртуальный файл) - существует ли какая-то карта с указателями для разделения команд чтения / записи, подходящих для каждого файла, или даже для самого виртуального каталога? Таким образом, запись для /dev/nullможет просто вернуть EOF.
Джо
источник
1
Когда файл создан, ядро ​​записывает его тип. Обычные дисковые файлы затем обрабатываются иначе, чем символические ссылки, блочные устройства, символьные устройства, каталоги, сокеты, FIFO и т. Д. Знание должно знать ядро.
Джонатан Леффлер
см. man pge для mknod
Jasen
Это все равно, что спросить "как выключатель света узнает, включен ли свет?" Выключатель света отвечает за решение, включен ли свет.
Легкость гонки с Моникой

Ответы:

25

Итак, здесь есть два основных типа вещей:

  1. Обычные файловые системы, которые хранят файлы в каталогах с данными и метаданными, в привычной манере (включая программные ссылки, жесткие ссылки и т. Д.). Они часто, но не всегда, поддерживаются блочным устройством для постоянного хранения (tmpfs живет только в оперативной памяти, но в остальном идентична обычной файловой системе). Семантика их знакома; читать, писать, переименовывать и т. д., все работает так, как вы ожидаете.
  2. Виртуальные файловые системы, разных видов. /procи /sysпримеры здесь, как и пользовательские файловые системы FUSE, такие как sshfsили ifuse. В них гораздо больше разнообразия, потому что на самом деле они просто ссылаются на файловую систему с семантикой, которая в некотором смысле «нестандартна». Таким образом, когда вы читаете из файла в папке /proc, вы на самом деле не обращаетесь к определенному фрагменту данных, который был сохранен чем-то другим, записавшим его ранее, как в обычной файловой системе. По сути, вы делаете вызов ядра, запрашивая некоторую информацию, которая генерируется на лету. И этот код может делать все что угодно, так как это просто функция, реализующая readсемантику. Таким образом, у вас странное поведение файлов /proc, как например, притворяясь символическими ссылками, когда они не '

Ключ в том, что /devна самом деле, как правило, один из первых видов. В современных дистрибутивах нормально /devбыть чем-то вроде tmpfs, но в старых системах было нормально иметь простой каталог на диске без каких-либо специальных атрибутов. Ключевым моментом является то, что эти файлы /devявляются узлами устройства, тип специального файла, похожего на FIFO или сокеты Unix; узел устройства имеет старший и младший номер, и чтение или запись их делают вызов драйвера ядра, почти так же, как чтение или запись FIFO вызывает ядро ​​для буферизации вашего вывода в канале. Этот драйвер может делать все что угодно, но обычно он как-то касается аппаратного обеспечения, например, для доступа к жесткому диску или воспроизведения звука в динамиках.

Чтобы ответить на оригинальные вопросы:

  1. Есть два вопроса, относящихся к тому, существует ли «файл» или нет; это то, существует ли буквально файл узла устройства и имеет ли смысл код ядра, поддерживающий его. Первый разрешается так же, как и все в обычной файловой системе. Современные системы используют udevили что-то в этом роде для отслеживания аппаратных событий и автоматического создания и уничтожения соответствующих узлов устройства /dev. Но старые системы или легкие пользовательские сборки могут просто иметь все узлы устройств буквально на диске, созданные заранее. Между тем, когда вы читаете эти файлы, вы обращаетесь к коду ядра, который определяется старшим и младшим номерами устройств; если это не разумно (например, вы пытаетесь прочитать блочное устройство, которое не существует), вы просто получите какую-то ошибку ввода-вывода.

  2. То, как это работает, зависит от того, какой код ядра вызывать для какого файла устройства. Для виртуальных файловых систем, таких как /proc, они реализуют свои собственные readи writeфункции; ядро просто вызывает этот код в зависимости от того, в какой точке монтирования оно находится, а реализация файловой системы позаботится обо всем остальном. Для файлов устройств это отправляется на основе старшего и младшего номеров устройств.

Том Хант
источник
Итак, если, скажем, у старой системы отключено питание, файлы в ней /devвсе еще будут там, но я думаю, они будут очищены при запуске системы?
Джо
2
Если старая система (без динамического создания устройства) была закрыта, как обычно, так и ненормально, узлы устройства оставались бы на диске, как любой файл. Затем, когда произойдет следующая загрузка, они также останутся на диске, и вы сможете использовать их как обычно. Только в современных системах происходит что-то особенное с созданием и уничтожением узлов устройства.
Том Хант
Поэтому более современная система, не использующая a, tmpfsбудет динамически создавать и удалять их по мере необходимости, например: загрузка и завершение работы?
Джо
3
devtmpfs, То /devфайловая система в современных Linux, подобна tmpfs, но имеет некоторые отличия от поддержки udev. (Ядро выполняет некоторое автоматическое создание узла перед передачей udev, чтобы сделать загрузку менее сложной.) Во всех этих случаях узлы устройства живут только в ОЗУ и создаются и уничтожаются динамически, как того требует оборудование. Предположительно, вы также можете использовать его udevна обычном диске /dev, но я никогда не видел, чтобы это было сделано, и нет никаких веских причин для этого.
Том Хант
17

Вот список файлов /dev/sda1моего почти современного сервера Arch Linux:

% ls -li /dev/sda1
1294 brw-rw---- 1 root disk 8, 1 Nov  9 13:26 /dev/sda1

Таким образом, запись в каталоге /dev/for sdaимеет номер индекса 1294. Это настоящий файл на диске.

Посмотрите, где обычно появляется размер файла. Вместо этого появляется «8, 1». Это основной и вспомогательный номер устройства. Также обратите внимание на 'b' в файле разрешений.

Файл /usr/include/ext2fs/ext2_fs.hсодержит эту (фрагмент) структуру C:

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    __u16   i_mode;     /* File mode */

Эта структура показывает нам структуру диска inode. В этой структуре много интересного; взгляни на это долго.

i_modeЭлемент struct ext2_inodeимеет 16 битов, и он использует только 9 для пользователя / группы / другой, чтение / запись / выполнение разрешений, а другой 3 для Setuid, setgid и липким. Он имеет 4 бита, чтобы различать такие типы, как «обычный файл», «ссылка», «каталог», «именованный канал», «сокет семейства Unix» и «блочное устройство».

Ядро Linux может следовать обычному алгоритму поиска в каталоге, а затем принимать решение на основе разрешений и флагов в i_modeэлементе. Для 'b', блокировать файлы устройств, он может найти старший и младший номера устройств и традиционно использовать основной номер устройства для поиска указателя на некоторую функцию ядра (драйвер устройства), которая работает с дисками. Дополнительный номер устройства обычно используется, например, номер устройства шины SCSI или номер устройства EIDE или что-то в этом роде.

Некоторые другие решения о том, как обращаться с подобным файлом, /proc/cpuinfoпринимаются на основе типа файловой системы. Если вы делаете:

% mount | grep proc 
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

Вы можете видеть, что /procимеет файловую систему типа "proc". Чтение из файла /procприводит к тому, что ядро ​​делает что-то другое в зависимости от типа файловой системы, точно так же, как открытие файла в файловой системе ReiserFS или DOS приводит к тому, что ядро ​​использует разные функции для поиска файлов и определения местоположения данных. файлы.

Брюс Эдигер
источник
Вы уверены, что только «реальные файлы на диске» имеют номер индекса? Я понял, 4026531975 -r--r--r-- 1 root root 0 Nov 14 18:41 /proc/mdstatчто явно не «настоящий файл».
Гюнтберт
7

В конце концов, все они - файлы для Unix, в этом вся прелесть абстракции.

То, как файлы обрабатываются ядром, теперь это другая история.

/ proc и в настоящее время / dev и / run (он же / var / run) являются виртуальными файловыми системами в оперативной памяти. / proc - это интерфейс / windows к переменным и структурам ядра.

Я рекомендую прочитать «Ядро Linux» http://tldp.org/LDP/tlk/tlk.html и «Драйверы устройств Linux», третье издание, https://lwn.net/Kernel/LDD3/ .

Мне также понравился дизайн и реализация операционной системы FreeBSD http://www.amazon.com/Design-Implementation-FreeBSD-Operating-System/dp/0321968972/ref=sr_1_1

Посмотрите на соответствующую страницу, которая относится к вашему вопросу.

http://www.tldp.org/LDP/tlk/dd/drivers.html

Руи Ф Рибейро
источник
спасибо, я немного изменил первый вопрос после того, как вы это прокомментировали.
Джо
Прочитайте последний комментарий, пожалуйста.
Руи Ф Рибейро
5

В дополнение к ответам @ RuiFRibeiro и @ BruceEdiger, вы делаете различие не совсем то, что делает ядро. На самом деле, у вас есть различные типы файлов: обычные файлы, каталоги, символические ссылки, устройства, сокеты (и я всегда забываю некоторые из них, поэтому я не буду пытаться составить полный список). Вы можете получить информацию о типе файла с помощью ls: это первый символ в строке. Например:

$ls -la /dev/sda
brw-rw---- 1 root disk 8, 0 17 nov.  08:29 /dev/sda

Буква «b» в самом начале означает, что этот файл является блочным устройством. Тире означает обычный файл, символьную ссылку «l» и т. Д. Эта информация хранится в метаданных файла и доступна, например, через системный вызов stat, поэтому ядро ​​может, например, по-разному считывать файл и символическую ссылку.

Затем вы делаете другое различие между «реальными файлами» /bin/bashи «виртуальными файлами», /proc/cpuinfoно lsсообщаете о них как о обычных файлах, так что разница будет другой:

ls -la /proc/cpuinfo /bin/bash
-rwxr-xr-x 1 root root  829792 24 août  10:58 /bin/bash
-r--r--r-- 1 root wheel      0 20 nov.  16:50 /proc/cpuinfo

Что происходит, так это то, что они принадлежат разным файловым системам. /procявляется точкой монтирования псевдофайловой системы, procfsтогда как /bin/bashнаходится на обычной файловой системе диска. Когда Linux открывает файл (это происходит по-разному в зависимости от файловой системы), он заполняет структуру данных, fileкоторая, среди прочих атрибутов, имеет структуру из нескольких указателей на функции, которые описывают, как использовать этот файл. Следовательно, он может реализовывать различные варианты поведения для разных типов файлов.

Например, это операции, объявленные /proc/meminfo:

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
    .open       = meminfo_proc_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

Если вы посмотрите на определение meminfo_proc_open, вы увидите, что эта функция заполняет буфер в памяти информацией, возвращаемой функцией meminfo_proc_show, задачей которой является сбор данных об использовании памяти. Эта информация может быть прочитана в обычном режиме. Каждый раз, когда вы открываете файл, meminfo_proc_openвызывается функция и обновляется информация о памяти.

lgeorget
источник
3

Все файлы в файловой системе являются «реальными» в том смысле, что они допускают файловый ввод / вывод. Когда вы открываете файл, ядро ​​создает дескриптор файла, который является объектом (в смысле объектно-ориентированного программирования), который действует как файл. Если вы читаете файл, дескриптор файла выполняет свой метод чтения, который, в свою очередь, запрашивает у файловой системы (sysfs, ext4, nfs и т. Д.) Данные из файла. Файловые системы предоставляют единый интерфейс для пользовательского пространства и знают, что нужно делать для обработки операций чтения и записи. Файловые системы, в свою очередь, просят другие слои обрабатывать их запросы. Для обычного файла, скажем, в файловой системе ext4, это будет включать поиск в структурах данных файловой системы (что может включать чтение с диска) и, в конечном итоге, чтение с диска (или кеша) для копирования данных в буфер чтения. Для файла в скажем sysfs, обычно это просто sprintf () что-то в буфере. Для узла dev блока он попросит драйвер диска прочитать некоторые блоки и скопировать их в буфер (старшие и младшие числа сообщают файловой системе, к какому драйверу обращаться с запросами).

jpkotta
источник