Из интерфейса программирования Linux , §14.1
Каждый файл устройства имеет основной идентификационный номер и вспомогательный идентификационный номер. Основной идентификатор идентифицирует общий класс устройства и используется ядром для поиска соответствующего драйвера для этого типа устройства. Дополнительный идентификатор однозначно идентифицирует конкретное устройство в общем классе. Основные и второстепенные идентификаторы файла устройства отображаются командой ls -l.
[...]
Каждый драйвер устройства регистрирует свою связь с конкретным идентификатором основного устройства, и эта связь обеспечивает соединение между специальным файлом устройства и устройством. Имя файла устройства не имеет значения, когда ядро ищет драйвер устройства.
См. Также эту старую (2001 г. ) главу « Драйверы устройств Linux» (2e) .
то есть цель состоит в том, чтобы обеспечить уникальное сопоставление основных: второстепенных для устройства: экземпляра для каждого типа устройства. Строго говоря, у вас может быть два разных устройства с одним и тем же мажором: второстепенное, если одно - char, а другое - block:
# ls -l /dev/ram1 /dev/mem
crw-r----- 1 root kmem 1, 1 Jan 1 1970 /dev/mem
brw-rw---- 1 root disk 1, 1 Jan 1 1970 /dev/ram1
В Linux в любой момент времени на одной системе главное: младшие номера для каждого типа устройства уникальны. Однако числа могут меняться со временем и не обязательно должны быть одинаковыми в разных системах Linux (даже в одном и том же дистрибутиве, ядре и оборудовании). Обратите внимание, что символьные и блочные устройства имеют разные пространства нумерации, например, основной номер 1 назначен дискам RAM, основной номер 1 назначен набору устройств ядра, включая ноль и ноль.
Исторически основные устройства были (в основном) статически распределены через реестр (также все еще присутствует, хотя и не поддерживается, в исходном коде ядра Documentation/devices.txt
). В наши дни многие устройства распределяются динамически, этим управляет udev и отображаемые в них отображения /proc/devices
. Фиксированные устройства все еще существуют incude/uapi/linux/major.h
(недавно перенесены из include/major.h
)
Теперь, хотя основная и второстепенная комбинации однозначно идентифицируют конкретные экземпляры устройства, ничто не мешает вам создать несколько узлов (файлов) устройств, которые ссылаются на одно и то же устройство. Их даже не нужно создавать /dev
(но они должны быть в файловой системе, которая поддерживает создание узлов устройства и не монтируется с nodev
опцией).
Обычно используется создание дублирующих нулевых, нулевых и случайных устройств в chroot:
# find /dev /var/chroot -regextype posix-extended -regex ".*/(zero|null|random)" -type c |
xargs ls -l
crwxrwxrwx 1 root root 1, 3 2012-11-21 03:22 /dev/null
crw-rw-r-- 1 root root 1, 8 2012-05-07 10:35 /dev/random
crw-rw-rw- 1 root root 1, 5 2012-11-21 03:22 /dev/zero
crwxrwxrwx 1 root root 1, 3 2012-11-21 03:22 /var/chroot/sendmail/dev/null
crw-rw-r-- 1 root root 1, 8 2012-05-07 10:35 /var/chroot/sendmail/dev/random
crw-rw-rw- 1 root root 1, 5 2012-11-21 03:22 /var/chroot/sendmail/dev/zero
Имена являются просто псевдонимами, ядро не заботится о большинстве имен или расположений, оно заботится о старшем номере, чтобы оно могло выбрать правильный драйвер, а драйвер (обычно) заботится о младшем номере, чтобы он мог выбрать правильный экземпляр.
Большинство имен просто условны (хотя некоторые определены в POSIX ). Также обратите внимание, что одно устройство может зарегистрироваться для нескольких основных номеров, проверьте sd
драйвер в /proc/devices
; имя модуля драйвера ( .ko
) не обязательно должно совпадать с именем устройства и не должно совпадать с именем узла устройства /dev
, а один модуль драйвера может управлять несколькими логическими / физическими устройствами или именами устройств.
Напомним: у вас может быть два или более узла устройства (внутри /dev/
или в другом месте), которые имеют одинаковые основные: второстепенные номера, но если они одного типа, они относятся к одному и тому же устройству. Вы можете иметь один драйвер, который может обрабатывать несколько основных экземпляров, но внутри ядра и внутри драйвера, для каждого типа (символ или блок) основной: младший номер используется для ссылки на конкретное устройство (основной) и конкретный экземпляр ( несовершеннолетний) устройства.
У вас не может быть двух узлов устройств с одинаковым типом и основным: второстепенным и ожидающим, что они получат доступ к двум различным логическим или физическим устройствам. Когда к устройству обращаются, ядро выбирает один драйвер на основе типа и основного номера (а не на основании имени узла устройства), и по соглашению младший номер детерминистически выбирает конкретный экземпляр или подфункцию.
Обновление
Немного интересной истории и перспективы * BSD можно найти в презентации BSDCon от Poul-Henning Kamp's 2002 :
https://www.usenix.org/legacy/events/bsdcon/full_papers/kamp/kamp_html/
Если вы вернетесь назад во времени к 1978 году (любезно предоставлено Alcatel-Lucent, Технический журнал Bell System, июль-август 1978 года), « Система разделения времени Unix » четко излагает это (p1937):
Устройства характеризуются основным номером устройства, второстепенным номером устройства и классом (блок или символ). Для каждого класса имеется массив точек входа в драйверы устройств. Основной номер устройства используется для индексации массива при вызове кода для конкретного драйвера устройства. Дополнительный номер устройства передается драйверу устройства в качестве аргумента. Младший номер не имеет никакого значения, кроме того, что приписывается ему водителем. Обычно драйвер использует младший номер для доступа к одному из нескольких идентичных физических устройств.