Активные разработки в направлении совершенствования ядра Linux привели
к появлению большого количества нововведений, полезных и удобных не
только пользователю, но и многочисленной армии программистов. Одним из
таких нововведений является Unified Device Model Of Kernel (далее
UDM). Речь идет о некоей обобщенной модели взаимодействия устройств и
драйверов в ядре линукс, абстрактном механизме взаимодействия между
ядром, устройствами, драйверами и пользовательским уровнем.
Использование этой модели тесно связано с новой файловой системой -
sysfs, которая также появилась в новом ядре с целью частично вытеснить
файловую систему /proc.
В этой статье я хотел рассказать о своих первых впечатлениях от работы
с UDM & Sysfs. Суть UDM состоит в том, что в ядре объявляется
несколько объектов (device, device_driver, bus_type, etc..) и набор
функций для работы с ними. С их помощью разработчик модулей ядра
(драйверов устройств) может построить взаимосвязанную, масштабируемую
систему. Среди преимуществ UDM можно выделить следующие:
* поддержка горячего подключения устройств
* отображение взаимосвязей "устройств" и "драйверов" в sysfs
* отслеживание жизненного цикла объектов модели
Каждый объект в UDM представляет собой структуру (struct). Взаимосвязь
объектов происходит через связные списки. Но от программиста это
тщательно скрывается (до разумных пределов, ведь все ядро есть в
исходном коде), то есть напрямую работать с объектами UDM как с
элементами связного списка не приходится. Каждый объект UDM,
попадающий в список, отображается в виртуальной файловой системе
sysfs. И это - как оказалось - действительно удобно. Поскольку
алгоритмы создания файлов и директорий в sysfs неразрывно следуют за
созданием древовидной структуры и также хорошо скрыты от программиста.
Я хорошо это ощутил, так как раньше разрабатывал модули под linux 2.4
и использовал procfs для взаимодействия пользователького уровня с
уровнем ядра. Так вот в linux 2.4 все приходилось делать самому -
создание файлов и директорий было специальной задачей.
Основные объекты UDM:
1. device - устройство
2. device_driver - драйвер устройства
3. bus_type - шина взаимодействия устройства и драйвера
4. device_attribute, driver_attribute, bus_attribute -
соответствующие атрибуты
Рассмотрим их подробнее.
bus_type. Хранит в себе список всех устройств и драйверов,
"подключенных" к ней. Основная задача - определять, какому драйверу
соответствует подключаемое к системе устройство (callback match). При
регистрации шины в linux директория с ее названием автоматически
появляется в sysfs : /sys/bus/my_bus
device_driver. Некая абстракция драйвера устройства. Драйвер
регистрируется в шине. Освновная задача - выполнить необходимые
действия при подключении устройства (probe) и его отключении (remove).
После подключения к шине создаются директории
/sys/bus/drivers/my_driver, /sys/drivers/my_driver.
device_attribute, driver_attribute, bus_attribute. Это структуры,
содержащие callback'и read & write. Каждый атрибут представляет собой
файл в sysfs. В зависимости от типа атрибута (bus_, device_, driver_),
файл будет находиться либо в каталоге для устройства, либо в каталоге
для драйвера, либо - для шины. Основное назначение атрибутов -
обеспечение взаимодействия ядра и пользовательского уровня. С помощью
них пользователь или пользовательская программа может настраивать
параметры устройства, драйвера, и т.п.
Алгоритмически взаимодействие вышеописанных объектов (структур)
выглядит так:
1. Во время загрузки модуля ядра происходит заполнение структуры
bus_type и регистрация ее в системе (bus_register). Результат: в
директории /sys/bus/ появится новый каталог с названием
bus_type->name. В этом каталоге автоматически появятся директории
drivers/, devices/.
2. Далее, в этом или уже в другом модуле, заполняется структура
device_driver. У этой структуры необходимо заполнить поле bus -
только тогда в каталоге my_bus/drivers/ появится новый драйвер.
Она регистрируется функцией driver_register. Зарегистрировано
может быть несколько драйверов. Результат: в каталоге
/sys/bus/my_bus/drivers появляется каталог с именем драйвера
(driver->name)
3. Некий модуль определяет, что устройство подключено к системе (по
прерываниям или методом опроса). Он создает и заполняет структуру
device и регистрирует устройство в системе - device_register. Что
происходит при регистрации устройства: 1) для каждого драйвера из
связного списка bus вызывается функция match, которой передаются
указатели на драйвер и новое устройство, до тех пор пока не будет
найден соответствующий драйвер 2) у найденного драйвера вызывается
функция probe, которой передается указатель на подключенный
device. Далее драйвер инициализирует подключенное устройство по
своему усмотрению. Данные, специфичные для данного устройства,
можно хранить по указателю device->driver_data. Результат: в sysfs
появляется директория с названием устройства:
/sys/bus/my_bus/devices/my_device
После регистрации любого объекта можно создать для него атрибут (файл
в sysfs). Для этого используются функции
(device_/bus_/driver_)create_file и
(device_/bus_/driver_)_remove_file. Структура атрибута содержит два
callback'а - store и show. Store вызывается, когда в файл (например
/sys/bus/my_bus/devices/my_device/some_attr ) записывается информация
пользователем. Show - когда считывается.
Процедура отключения устройства имеет обратный порядок. Сначала
вызывается device_unregister(). После этого ядро автоматически
вызывает driver->remove(device) и из sysfs удаляется соответствующий
каталог.
Ко всему сказано необходимо добавить, что:
* вышеописанные действия не только создают систему директорий и
файлов в sysfs, но и устанавливают взаимосвязь между драйверами,
устройствами. Например структура device хранит в себе указатель на
driver, который соответствует данному устройству.
* при помощи итераторов можно "пробежаться" по всем драйверам и
устройствам, подключенным к какой-либо шине.
* при помощи итераторов можно "пробежаться" по всем устройствам,
подключенным к какому-либо драйверу.
То, как все вышеописанное происходит в ядре, можно прочитать в
исходных текстах ядра: