2 февраля 2010 г.

Управление устройствами с помощью udev

Эта статья перевод 19-ой главы OpenSUSE Reference Guide, которое можно скачать в PDF-формате или просто посмотреть в формате html через браузер здесь.

В Linux работу по подключению и удалению устройств выполняет ядро системы. Изменения состояния устройств (подключение нового или удаление существующего) должны быть при этом видимы в пользовательском пространстве. При подключении новых устройств они должны тут же корректно настраиваться и (при необходимости) опознаваться пользовательскими приложениями. Если пользователь системы работает с конкретным устройством, то его необходимо проинформировать о любом изменении состояния данного устройства.
udev обеспечивает все необходимые средства для динамического создания и удаления файлов устройств и символических ссылок в каталоге /dev. Правила udev позволяют использовать внешние программы для обработки событий ядра об устройствах (kernel device events), что позволяет вам изменять по вашему желанию порядок работы udev, например, написанием собственных скриптов или запроса и импорта дополнительных данных для использования в процессе работы ядра с устройством.
 

1. Каталог /dev

Файлы устройств (device nodes) в каталоге /dev обеспечивают доступ к соответствующим устройствам. Благодаря udev в данном каталоге находятся файлы только тех устройств, которые в настоящий момент подключены к системе. Каждое устройство имеет свой соответствующий файл. Если устройство отключается от системы — данный файл удаляется.

Содержимое каталога /dev хранится на виртуальной файловой системе и все файлы, находящиеся в нем, создаются при каждом запуске системы. Модифицированные или созданные вручную файлы не сохраняются после перезагрузки. Файлы и каталоги, которые необходимо сохранить или которые всегда должны присутствовать в каталоге /dev, независимо от состояния соответствующего устройства, необходимо помещать в каталог /lib/udev/devices. При запуске системы содержимое данного каталога копируется в /dev как есть (с теми же правами доступа).

2. Kernel uevents и udev

Вся информация необходимая для работы с устройствами экспортируется ядром как виртуальная файловая система sysfs. В ней для каждого из устройств, которые ядро обнаружило и инициализировало, создается отдельный каталог, в котором содержатся файлы, описывающие те или иные параметры устройства.

Каждый раз при подключении или отключении устройства ядро посылает событие uevent, которое информирует udev о произошедших изменениях. Демон udev считывает правила для своей работы из каталога /etc/udev/rules.d/ единожды при своем запуске и затем хранит их в памяти. Если правила меняются, добавляются или изменяются, существует возможность заставить udev перечитать все правила командой udevadm control --reload-rules. В SUSE это может быть также сделано командой /etc/init.d/boot.udev reload. Более подробная информация о правилах udev и их синтаксисе содержится ниже в разделе 6.
Каждое полученное событие сравнивается с набором загруженных правил. С помощью правил можно добавлять специфичные для окружения опции, создавать символические ссылки на создаваемый файл устройства или указать программу, запускаемую после того как файл устройства будет создан. События ядра uevents от драйверов устройств передаются через сокет ядра netlink.

3. Драйверы устройств, модули ядра и устройства

Для каждого обнаруженного устройства ядро создает специальную внутреннюю структуру, в то время как драйвер посылает событие uevent демону udev. Устройства идентифицируют сами себя с помощью идентификатора специального вида. Обычно эти идентификаторы содержат уникальные коды производителя устройства, самого устройства и другие параметры специфичные для конкретной подсистемы. Такой идентификатор называется MODALIAS. Ядро собирает информацию об устройстве, формирует идентификатор MODALIAS для него и передает его вместе с событием uevent. Для USB-мыши, например, он выглядит так:

MODALIAS = usb:v046DpC03Ed2000dc00dsc00dp00ic03isc01ip02
Каждый драйвер устройства содержит список идентификаторов тех устройств, с которыми он может работать. Данный список находится в файле модуля ядра. Специальная утилита depmod считывает эти идентификаторы из модулей ядра и сохраняет их в файле modules.alias в каталоге /lib/modules/$(uname -r). Это позволяет упростить процесс загрузки необходимых модулей ядра для каждого обнаруживаемого устройства, поскольку uevent также содержит MODALIAS. Если вызывается команда modprobe $MODALIAS, она находит в файле modules.alias идентификатор устройства и соответствующий ему модуль. Если соответствие найдено — загружается необходимый модуль. Все это автоматически обрабатывается udev.


4. Инициализация устройств при начальной загрузке системы

Все события, связанные с обнаружением устройств и происходящие при запуске системы до запуска udev теряются, поскольку вся инфраструктура для обработки этих событий должна находится на корневой файловой системе, которая недоступна в данный момент. Для компенсации этих потерь, ядро создает файл uevent, расположенный в каталоге, принадлежащим каждому из найденных устройств в файловой системе sysfs, в котором дублирует то же событие, которое потерялось во время загрузки. После своего запуска udev выполняет проход по всем файлам uevent в файловой системе /sys, что заново вызывает все события для создания и конфигурирования устройств.
Например, USB-мышь не может быть инициализирована на начальных стадиях загрузки, потому что ее драйвер не доступен в данный момент. Событие uevent об обнаружении данного устройства потерялось и для него к тому же не удалось найти модуль ядра. Вместо чтобы заново выполнять поиск всех для подключенных устройств, udev просто просматривает все файлы uevents из sysfs, после того как станет доступна корневая файловая система, поэтому событие для USB-мыши просто будет обработано заново, после чего будет считан нужный модуль ядра на смонтированной корневой файловой системе и мышь сможет быть инициализирована.
С точки зрения пользовательского пространства нет никакой видимой разницы между «холодным» обнаружением устройств (coldplug) и обнаружением устройств при нормальной работе системы. В обоих случаях будут использоваться те же правила и те же программы конфигурации устройств.

5. Мониторинг демона udev

Для визуализации событий ядра, связанных с устройствами, необходимо использовать программу udevadm monitor .

UEVENT[1185238505.276660] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1 (usb)
UDEV [1185238505.279198] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1 (usb)
UEVENT[1185238505.279527] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0 (usb)
UDEV [1185238505.285573] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0 (usb)
UEVENT[1185238505.298878] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10 (input)
UDEV [1185238505.305026] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10 (input)
UEVENT[1185238505.305442] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/mouse2 (input)
UEVENT[1185238505.306440] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/event4 (input)
UDEV [1185238505.325384] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/event4 (input)
UDEV [1185238505.342257] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/mouse2 (input)

Строки, начинающиеся с UEVENT, показывают события, которые направляются ядром и могут быть считаны через сокет netlink. Строки, начинающиеся с UDEV, показывают результат обработки udev этих событий. Время поступления и обработки событий указывается в микросекундах. Время между UEVENT и UDEV — это то время, которое udev затрачивает для обработки этого события или специально задерживает обработку для синхронизации данного события с аналогичными или обрабатываемыми в настоящий момент событиями. Например, обработка событий для разделов жесткого диска всегда задерживается до обработки события для диска в целом, потому что способы обработки событий для разделов могут опираться на те данные, которые появятся после инициализации физического диска.
Команда udevadm monitor --env покажет полное окружение для обрабатываемого события:

ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10
SUBSYSTEM=input
SEQNUM=1181
NAME="Logitech USB-PS/2 Optical Mouse"
PHYS="usb-0000:00:1d.2-1/input0"
UNIQ=""
EV=7
KEY=70000 0 0 0 0
REL=103
MODALIAS=input:b0003v046DpC03Ee0110-e0,1,2,k110,111,112,r0,1,8,amlsfw

udev также использует системный демон ведения журналов - syslog. Приоритет ведения log-файлов указывается в конфигурационном файле udev - /etc/udev/udev.conf , также есть возможность поменять его «на лету» командой:
udevadm control log_priority=level/number

6. Изменение процесса обработки событий, связанных с устройствами, с помощью правил udev

Правила udev могут ссылаться либо на информацию, находящуюся в возбуждаемом ядром событии, либо на информацию, которую ядро экспортирует через sysfs. Также есть возможность запроса дополнительной информации из внешних программ. Каждое событие проверяется на соответствие со всеми правилами, которые находятся в каталоге /etc/udev/rules.d. Каждая строка в файле с правилами udev содержит хотя бы одну пару ключ/значение. Существует два типа ключей: ключ-условие и ключ присваивания. Если ключ-условие совпал при обработке события, то данное правило выполняется и с помощью ключей присваивания устанавливаются указанные переменные. В правилах можно указывать имя файла устройства, попросить создать символьную ссылку на файл устройства или запустить указанную программу для обработки данного события. Если не будет найдено соответствие правилу, то при создании файла устройства будет использовано имя по умолчанию. Подробная информация о синтаксисе правил и возможных ключах приведена в man-странице udev. Приведенные ниже примеры позволяют получить базовое представление о синтаксисе правил. Примеры взяты из правил udev по умолчанию, которые находятся в файле /etc/udev/rules.d/50-udev-default.rules.

Пример правил udev:
# console
KERNEL=="console", MODE="0600", OPTIONS="last_rule"

# serial devices
KERNEL=="ttyUSB*", ATTRS{product}=="[Pp]alm*Handheld*", SYMLINK+="pilot"

# printer
SUBSYSTEM=="usb", KERNEL=="lp*", NAME="usb/%k", SYMLINK+="usb%k", GROUP="lp"

# kernel firmware loader
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware.sh"

Первое правило (console) содержит три ключа: один ключ-условие (KERNEL) и два ключа присваивания (MODE,OPTIONS). С помощью данного правила udev ищет в списке устройств соответствие типу «console» и, если оно находится, то вызывается его обработка. Ключ MODE устанавливает указанные права доступа на файл устройства (чтение и запись для владельца файла в данном случае). Ключ OPTIONS делает это правило последним для обработки устройств данного типа, любые последующие правила для данного устройства не будут оказывать никакого эффекта.

Следующее правило (serial devices) больше не существует в файле 50-udev-default.rules, но по-прежнему заслуживает рассмотрения в качестве примера. Оно состоит из двух ключей-условий (KERNEL и ATTRS) и одного ключа присваивания (SYMLINK). С помощью ключа KERNEL udev ищет устройства, соответствующие типу ttyUSB. Звездочка (*) указывает на необходимость применения данного правила ко всем устройствам данного типа. Ключ ATTRS проверяет наличие в файловой системе файла с атрибутами данного устройства, который содержит указанную строку. Ключ SYMLINK приведет к созданию символической ссылки на это устройство - /dev/pilot. Оператор «+ =», используемый в данном правиле, указывает udev дополнительно выполнять это действие, даже если предыдущие или последующие правила создают другие символические ссылки. Поскольку это правило содержит два ключа-условия, оно применяется только если выполняются они оба.

Правило для работы с USB-принтерами (printer) также содержит два ключа-условия (SUBSYSTEM и KERNEL), которые аналогичным образом должны оба совпасть с типом устройства для выполнения оставшейся части правила. Три оставшихся ключа: задают имя создаваемого файла (ключ NAME), группу-владельца (ключ GROUP) и создают символическую ссылку на файл устройства (SYMLINK). Использование символа * указывает на использование правила для различных принтеров (устройств lp). Обозначение «%k» задает использование для имени устройства и символической ссылки того имени, которое назначает ядро системы. Например, символическая ссылка на первый USB-принтер будет /dev/usblp0.

Последнее приведенное правило делает возможной загрузку с помощью udev дополнительного firmware с помощью внешнего скрипта. Ключ-условие SUBSYSTEM ищет устройства, относящиеся к подсистеме firmware. Ключ ACTION проверяет добавлялись ли в систему устройства подсистемы firmware. Последний ключ RUN+= запускает сценарий для поиска необходимого firmware и его загрузки.
Некоторые общие соглашения для всех правил:

  • Каждое правило состоит из одной или нескольких пар ключ/значение, разделенных запятыми.

  • Ключевая операция определяется оператором. Правила udev позволяют использование нескольких различных операторов.

  • Каждое задаваемое значение должно быть заключено в кавычки.

  • Каждая строка файла правил представляет собой одно правило. Если правило более одной строки используйте символ \ для добавления нескольких строк так же, как делали бы это в скриптах оболочки.

  • Правила udev поддерживают использование символов *, ? и [] образом, аналогичным bash-скриптам.

  • Правила udev поддерживают использование специальных обозначений (см. ниже).

6.1 Операторы в правилах udev

При создании ключа можно выбрать один из нескольких операторов в зависимости от типа, который вы хотите создать. Ключи-условия обычно используются для нахождением совпадения, которое или совпадает или явно не соответствует искомому значению. Ключи-условия могут содержать следующие операторы:
= = (два знака «равно» подряд без пробела) - проверка на равенство. Выражение справа знака равенства служит образцом для поиска.

! = (восклицательный знак и знак «равно» без пробела) - проверка на не-равенство. Выражение справа знака равенства служит образцом для поиска.


Любой из следующих операторов может использоваться с ключами присваивания:

= (знак «равно») - присваивание значения ключу. Если данный ключ ранее содержал список значений, то этот список заменяется вновь присваиваемым значением. Ключу можно присвоить только единственное значение.

+ = (знаки «плюс» и равно без пробела) - присвоить ключу, содержащему список записей еще одно значение.

: = (знаки «двоеточие» и равно без пробела) - присваивание ключу окончательного значения. Запретить любые последующие изменения более поздними правилами.

6.2 Использование специальных обозначений в правилах udev

Правила udev поддерживают использование специальных обозначений, которые следует использовать тем же образом, как делается в любом другом сценарии. В правилах udev могут использоваться следующие обозначения:

%r, $root - Директория для файлов устройств (по умолчанию /dev)
%p, $devpath - Значение DEVPATH (см. ниже)
%k, $kernel - параметр KERNEL или имя, присвоенное устройству ядром системы (другими словами, то имя под которым это устройство «знает» ядро - прим.перев.)
%n, $number - номер устройства
%N, $tempnode - временное имя файла устройства, которое создается для того, чтобы предоставить доступ к устройству до создания постоянного файла
%M, $major - «старший» (major) номер устройства
%m, $minor - «младший» (minor) номер устройства

Примечание перев.:
Символьные и блочные устройства имеют фиксированные старший и младший номера, связанные с ними. Орган, который отвечает за присвоение номеров устройств это LANANA - Linux Assigned Name and Number Authority.

%s{attribute}, $attr{attribute} - значение атрибута файловой системы sysfs (задается параметром attribute) (подробнее см. man udev)
%E{variable}, $attr{variable} - значение переменной среды (задается параметром variable)
%c, $result — строка, возвращаемая внешней программой (PROGRAM - см. ниже). Также может быть выбрана часть строки (пробелы воспринимаются, как разделители строки), которая выделяется указанием номера части, как аргумента %c{N}. Если после числа стоит знак «+», то подставляется указанная часть строки плюс ее оставшаяся часть строки: %c{N+}
%% - символ %
$$ - символ $

6.3 Использование ключей-условий в udev

Ключи-условия описывают проверки, которые должны быть выполнены для применения к ним правил udev. Возможно использование следующих ключей:

ACTION - название события, связанного с устройством, например, добавление устройства («add») или его удаление («remove»).

DEVPATH - путь к каталогу (относительно файловой системы /sys), содержащему файлы event для данного устройства, например, для драйвера ipw3945 данный ключ примет следующее значение DEVPATH=/bus/pci/drivers/ipw3945

KERNEL - имя данное устройству ядром системы (внутреннее имя устройства)
SUBSYSTEM - подсистема, в которой произошло возбуждение события, связанного с устройством, например, SUBSYSTEM="usb" для всех событий, связанных с USB-устройствами.
ATTR{filename} - атрибуты устройства в файловой системе sysfs. Например, для нахождения строк, содержащих аргумент vendor (производитель устройства), можно использовать следующую строку ATTR{vendor}=="On[sS]tream".

KERNELS - заставляет udev использовать DEVPATH для определения имени устройства.
SUBSYSTEMS - заставляет udev использовать DEVPATH для определения подсистемы устройства.

DRIVERS - заставляет udev использовать DEVPATH для определения драйвера устройства.

ATTRS{filename} - заставляет udev использовать DEVPATH для устройства с указанным аргументом файловой системы sysfs.

ENV{key} - задает значение переменной окружения, например, ENV{ID_BUS}="ieee1394" может использоваться для поиска всех событий, связанных с шиной FireWire.
PROGRAM - заставляет udev выполнить внешнюю программу. В случае успешного завершения работы программа обязана возвращать код выхода равным нулю. Вывод программы доступен через ключ RESULT.
RESULT - ищет соответствие выводу последнего вызова PROGRAM. Применяется либо в том же правиле, что и PROGRAM, либо в последующем.


6.4 Использование ключей присваивания udev

По сравнению с ключами-условиями, приведенными выше, ключи присваивания, как следует из названия, присваивают значения, имена или какие-либо еще параметры файлам устройств, создаваемых udev.
NAME - имя создаваемого файла устройства. Все последующие правила для данного устройства игнорируются.
SYMLINK - имя создаваемой символической ссылки на указанное устройство. Для одного устройства может быть создано несколько символических ссылок одним (в этом случае список ссылок разделяется пробелами) или несколькими правилами udev.

OWNER, GROUP, MODE - задают соответствующие права доступа для создаваемого файла устройства.

ATTR{key} - задает параметр, который будет записан, как аргумент устройства в файловой системе sysfs. Если используется оператор «==» (см.выше), то данный ключ также используется для поиска соответствия аргумента устройству.

ENV{key} - говорит udev экспортировать внешнюю переменную среды. Если используется оператор «==» (см.выше), то данный ключ также используется для поиска соответствия указанной переменной среды.

RUN - просит udev добавить указанную программу в список программ, которые будут выполнены для данного устройства. Для избегать блокирования дальнейших событий для этого устройства, необходимо чтобы данный список был как можно короче.
LABEL - метка для последующего перехода с помощью оператора GOTO.

GOTO - заставляет udev пропустить набор правил и продолжить со строки, указанной передаваемой GOTO меткой LABEL.

IMPORT{type} - загрузка переменных в окружение события, таких как, например, вывод внешней программы. udev может импортировать переменные нескольких различных типов. Если тип не указан, udev пытается самостоятельно его определить, ориентируясь на права доступа указанного файла:

  • Если это исполняемый файл - udev выполняет внешнюю программу и импортирует ее вывод.

  • Если это текстовый файл - udev импортирует его содержимое

  • Если это родительское устройство - udev импортирует его параметры

WAIT_FOR_SYSFS - просит udev подождать создания указанного файла в файловой системе sysf. Например, WAIT_FOR_SYSFS = "ioerr_cnt" информирует udev подождать создание файла ioerr_cnt.
OPTIONS - данный ключ может принимать несколько возможных значений:

  • last_rule заставляет udev игнорировать все последующие правила, касающиеся данного устройства;

  • ignore_device заставляет udev игнорировать это событие полностью;

  • ignore_remove заставляет udev игнорировать все последующие события удаления устройства;

  • all_partitions заставляет udev создать файлы устройств для всех доступных разделов блочных устройств. 

7. Постоянные имена устройств

Динамически наполняемый каталог /dev и инфраструктура udev позволяет обеспечить постоянные имена для всех дисковых устройств, независимо от порядка их распознавания или присоединения. Каждое блочное устройство, определенное ядром проверяется на предмет шины подключения, типа дисков или файловых систем. Наряду с динамически создаваемыми файлами устройств, udev также создает набор постоянных символических ссылок, указывающих на устройство:
/dev/disk
|-- by-id
| |-- scsi-SATA_HTS726060M9AT00_MRH453M4HWHG7B -> ../../sda
| |-- scsi-SATA_HTS726060M9AT00_MRH453M4HWHG7B-part1 -> ../../sda1
| |-- scsi-SATA_HTS726060M9AT00_MRH453M4HWHG7B-part6 -> ../../sda6
| |-- scsi-SATA_HTS726060M9AT00_MRH453M4HWHG7B-part7 -> ../../sda7
| |-- usb-Generic_STORAGE_DEVICE_02773 -> ../../sdd
| `-- usb-Generic_STORAGE_DEVICE_02773-part1 -> ../../sdd1
|-- by-label
| |-- Photos -> ../../sdd1
| |-- SUSE10 -> ../../sda7
| `-- devel -> ../../sda6
|-- by-path
| |-- pci-0000:00:1f.2-scsi-0:0:0:0 -> ../../sda
| |-- pci-0000:00:1f.2-scsi-0:0:0:0-part1 -> ../../sda1
| |-- pci-0000:00:1f.2-scsi-0:0:0:0-part6 -> ../../sda6
| |-- pci-0000:00:1f.2-scsi-0:0:0:0-part7 -> ../../sda7
| |-- pci-0000:00:1f.2-scsi-1:0:0:0 -> ../../sr0
| |-- usb-02773:0:0:2 -> ../../sdd
| |-- usb-02773:0:0:2-part1 -> ../../sdd1
`-- by-uuid
|-- 159a47a4-e6e6-40be-a757-a629991479ae -> ../../sda7
|-- 3e999973-00c9-4917-9442-b7633bd95b9e -> ../../sda6
`-- 4210-8F8C -> ../../sdd1

8. Файлы, используемые udev

/sys/* - виртуальная файловая система, создаваемая ядром Linux, экспортирующая информацию обо всех известных на данный момент устройствах. Эта информация используется udev для создания устройств в /dev

/dev / * - содержит динамически создаваемые файлы устройств и статический контент, скопированный во время загрузки системы из /lib/udev/devices/*
Следующие файлы и каталоги содержат важнейшие элементы инфраструктуры udev:

/etc/udev/udev.conf - главный конфигурационный файл udev.
/etc/udev/rules.d/* - каталог с правилами udev.

/lib/udev/devices/* - статический контент, копируемый udev в /dev при загрузке системы

/lib/udev/* - вспомогательные программы, вызываемые из правил udev.


9. Источники получения дополнительной информации

Для получения дополнительной информации об инфраструктуре udev обратитесь к man-страницам:

man udev - содержит общую информацию о udev, ключах, правилах и других важных вопросах конфигурации.
man udevadm - утилита udevadm может быть использована для мониторинга и управления работой udev, отправки событий ядра, связанных с устройствами, управления очередью событий и предоставляет простые механизмы диагностики.
man udevd - информация о демоне udev.

Оригинальная статья: Copyright © 2006- 2009 Novell, Inc.
Перевод: Copyright © Чернышов Антон, УЦ R-Style


Текст распространяется на условиях GNU Free Documentation License, Version 1.2 или более поздней версии

Комментариев нет:

Отправить комментарий