3.5. Менеджер устройств udev
Излишняя усложненность ядра приводит к нестабильности в системе. Пример тому — управление файлами устройств: можно создавать файлы устройств в пространстве пользователя, так почему бы не выполнять это в ядре? Ядро системы Linux отправляет уведомления процессам в пространстве пользователя (они называются udevd) после обнаружения нового устройства в системе (например, после подключения USB-накопителя). С другой стороны, процесс в пространстве пользователя проверяет характеристики нового устройства, создает файл устройства, а затем выполняет необходимую инициализацию устройства.
Это была теория. К сожалению, на практике такой подход проблематичен: файлы устройств необходимы уже на ранней стадии загрузки системы, поэтому уведомления udevd должны начинать работу рано. Чтобы создать файлы устройств, менеджеру udevd не следует зависеть от устройств, для создания которых предназначен. Он должен выполнять начальный запуск очень быстро, чтобы остальная часть системы не оказалась в подвешенном состоянии, ожидая запуска менеджера udevd.
3.5.1. Файловая система devtmpfs
Файловая система devtmpfs была разработана для решения проблемы с доступностью устройств во время загрузки (см. подробности в разделе 4.2). Эта файловая система подобна старой devfs, но является упрощенной. Ядро создает файлы устройств по мере надобности, а также уведомляет менеджер udevd о том, что доступно новое устройство. После получения такого сигнала менеджер udevd не создает файлы устройств, а выполняет инициализацию устройства и отправляет уведомление процессу. Кроме того, он создает несколько символических ссылок в каталоге /dev для дальнейшей идентификации устройств. Примеры вы можете отыскать в каталоге /dev/disk/by-id, в котором каждому присоединенному диску соответствует одна или несколько записей.
Рассмотрим, например, такой типичный диск:
lrwxrwxrwx 1 root root 9 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671 -> ../../sda
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671-part1 ->
../../sda1
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671-part2 ->
../../sda2
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671-part5 ->
../../sda5
Менеджер udevd дает имена ссылкам по типу интерфейса, а затем по информации об изготовителе и модели, серийному номеру и разделу (если применяется).
В следующем разделе подробно рассказано о том, как менеджер udevd выполняет свою работу: как узнает о том, какие символические ссылки создать, и как создает их. Эта информация необязательна, чтобы продолжить чтение книги. Если вы впервые встречаетесь с устройствами Linux, можете смело переходить к следующей главе, чтобы начать изучение того, как использовать диски.
3.5.2. Работа и настройка менеджера udevd
Демон udevd работает следующим образом.
1. Ядро отправляет демону udevd событие-уведомление, называемое uevent, через внутреннюю сетевую ссылку.
2. Демон udevd загружает все атрибуты, содержащиеся в уведомлении uevent.
3. Демон udevd проводит разбор правил, а затем предпринимает действия или устанавливает дополнительные атрибуты на основе правил.
Входящее уведомление uevent, которое демон udevd получает от ядра, может выглядеть так:
ACTION=change
DEVNAME=sde
DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host4/
target4:0:0/4:0:0:3/block/sde
DEVTYPE=disk
DISK_MEDIA_CHANGE=1
MAJOR=8
MINOR=64
SEQNUM=2752
SUBSYSTEM=block
UDEV_LOG=3
Можно заметить, что здесь присутствует смена устройства. После получения уведомления uevent демон udevd знает путь устройства в файловой системе sysfs, а также некоторые другие атрибуты, связанные с его свойствами, и готов начать обработку правил.
Файлы правил расположены в каталогах /lib/udev/rules.d и /etc/udev/rules.d. Правила из каталога /lib являются правилами по умолчанию, а правила из каталога /etc переопределяют их. Подробное объяснение правил вы сможете найти на странице руководства udev(7).
Взглянем на символические ссылки из примера /dev/sda в поразделе 3.5.1. Эти ссылки были определены с помощью правил из файла /lib/udev/rules.d/60-persistent-storage.rules. В нем вы увидите следующие строки:
# ATA devices using the "scsi" subsystem
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi",
ATTRS{vendor}=="ATA", IMPORT{program}="ata_id —export $tempnode"
# ATA/ATAPI devices (SPC-3 or later) using the "scsi" subsystem
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi",
ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id —export $tempnode"
Эти правила сопоставляют диски ATA, представленные с помощью SCSI-подсистемы ядра (см. раздел 3.6). Можно заметить здесь несколько правил, предназначенных для отслеживания различных способов, с помощью которых могут быть представлены устройства, но суть заключается в том, что демон udevd будет пытаться подобрать устройство, начиная с символов sd или sr без номера (с использованием выражения KERNEL=="sd*[!0-9]|sr*"), а также подсистему (SUBSYSTEMS=="scsi") и, наконец, некоторые другие атрибуты. Если все эти условные выражения окажутся истинными, то демон udevd переходит к следующему выражению:
IMPORT{program}="ata_id —export $tempnode"
Это уже не условие, а скорее указание выполнить импорт переменных из команды /lib/udev/ata_id. Если у вас есть подобный диск, попробуйте самостоятельно ввести в командной строке следующее:
$ sudo /lib/udev/ata_id —export /dev/sda
ID_ATA=1
ID_TYPE=disk
ID_BUS=ata
ID_MODEL=WDC_WD3200AAJS-22L7A0
ID_MODEL_ENC=WDC\x20WD3200AAJS22L7A0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_REVISION=01.03E10
ID_SERIAL=WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671
—snip—
Теперь импорт настраивает окружение так, чтобы все имена переменных в данном выводе приняли указанные значения. Например, любое правило, которое появится следом, будет распознавать ENV{ID_TYPE} в качестве диска.
Особо следует отметить переменную ID_SERIAL. В каждом из правил на втором месте следует такое условие:
ENV{ID_SERIAL}!="?*"
Это означает, что оно будет истинным, только если для переменной ID_SERIAL не указано значение. Следовательно, если она уже определена, условие будет ложным и все правило также станет ложным, в результате чего демон udevd перейдет к следующему правилу.
В чем же суть? Целью этих двух правил (и многих других в том же файле) является поиск серийного номера дискового устройства. Если значение переменной ENV{ID_SERIAL} установлено, демон udevd может проверить следующее правило:
KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*",
SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
Для этого правила необходимо задать значение переменной ENV{ID_SERIAL}. В нем есть также одна директива:
SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
После встречи с этой директивой демон udevd добавляет символическую ссылку на обнаруженное устройство. Итак, теперь вы знаете, откуда берутся символические ссылки на устройства.
Вероятно, вам любопытно узнать, как отличить условное выражение от директивы. Условные выражения записываются с помощью двух знаков равенства (==) или восклицательного знака и знака равенства (!=). Для директив используют либо один знак равенства (=), либо символ «плюс» и знак равенства (+=), либо двоеточие со знаком равенства (:=).
3.5.3. Команда udevadm
Команда udevadm является инструментом администрирования менеджера udevd. С ее помощью можно перезагрузить правила для udevd, а также события-триггеры. Однако, вероятно, самыми мощными функциями команды udevadm являются возможность поиска и обнаружения системных устройств, а также способность отслеживать уведомления uevents, когда демон udevd получает их от ядра. Единственную сложность может составить синтаксис этой команды, который становится немного запутанным.
Начнем с рассмотрения системного устройства. Вернувшись к примеру из подраздела 3.5.2, чтобы взглянуть на все атрибуты менеджера udev, которые использованы и сгенерированы в сочетании с правилами для устройства (такого как /dev/sda), запустите следующую команду: