Журнал LinuxFormat - перейти на главную

LXF77:sysfs

Материал из Linuxformat
Версия от 15:18, 16 января 2009; Yaleks (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Содержание

Оборудование и Linux Новая страница: sysfs

Нам часто приходится слышать, что Linux «недостаточно хорошо поддерживает современное оборудование». Владимир Попов хочет привести пару подкрепленных примерами доводов в защиту открытой ОС.

Вам никогда не казалось парадоксальным, что Linux, который используется в мобильных телефонах и на суперкомпьютерах, пользователи IBM PC часто упрекают в том, что он «плохо поддерживает оборудование»? Не будем вспоминать, что для Microsoft (а ведь именно с Windows сравнивают Linux на персональных компьютерах) львиную долю работы по поддержке оборудования выполняют производители этого самого оборудования, просто попытаемся проанализировать, есть ли реальные основания для таких упрёков.

Настоящая статья представляет собой попытку «защиты» Linux, опирающуюся на информацию о новой драйверной модели, ставшей частью ядра версии 2.6

IBM PC: что внутри?

Для начала об оборудовании, непосредственно контактирующем с внутренними шинами IBM PC. Это те самые карты, которые раньше во множестве вставлялись непосредственно в слоты материнской платы. На настоящий момент большая часть этих устройств находится «на борту» — в составе микросхем, расположенных на материнской плате, но сути это не меняет. Каждое такое устройство использует определённые ресурсы — адреса памяти и портов ввода/вывода (о линиях прерывания и каналах прямого доступа к памяти (DMA) упоминать не будем, поскольку шина ISA усилиями Intel таки похоронена окончательно). Казалось бы: устройство скорее предоставляет ресурсы, чем использует их. Но поскольку адресное пространство у компьютера одно, то получается, что предоставляя ячейки памяти или регистры портов ввода/вывода, устройство использует именно адреса компьютера.

И хотя вышеупомянутых карт в составе IBM PC с каждым годом становилось всё меньше, количество устройств неизменно возрастало, и каждому требовались ресурсы. Совершенно очевидно, что нужен был механизм, который бы эти ресурсы распределял (хотя бы для того, что-бы обеспечить возможность работы в составе компьютера двух одинаковых устройств, требующих, естественно, одинаковых ресурсов). Такой механизм существует, и называется он — Plug-and-Play. Это может показаться странным, но такое несколько «игривое» название получило именно распределение ресурсов устройствам, а не подключение устройства к компьютеру без перезагрузки последнего, как можно было бы предположить. Для такого «горячего» подключения используется совсем иной термин — hotplug. Первоначально о hotplug можно было говорить только в применении к USB и PCMCIA/Cardbus. Потом этот список был дополнен устройствами Firewire и CompactPCI, а в настоящее время hotplug возможен для «нормальных» PCI-устройств (посредством специальных hotplug-контроллеров), IDE (некоторые RAID контроллеры), SCSI, модулей памяти и, наконец, CPU. Вот уж, воистину: «всё смешалось в доме Облонских», как справедливо заметил великий русский писатель.

Основной шиной, посредством которой обмениваются информацией все устройства современного IBM PC, является PCI. Пользователи Linux могут легко в этом убедиться, набрав в командной строке lspci, lspci -vv или scanpci -v. Остальные шины, будь-то AGP или USB, получают доступ к PCI посредством контроллеров, называемых мостами. События, происходящие на этих «прочих» шинах, в принципе, не интересуют систему управления Plug-and-Play, поскольку не влекут за собой перераспределения шинных ресурсов. Так бы и сосуществовали мирно разные устройства: Plug-and-Play контролировал бы распределение внутренних ресурсов вычислительной системы, hotplug реагировал бы на подключения к шинам USB и PCMCIA, пока «горячее» подключение не стали использовать для PCI-устройств.

Кроме того, есть ещё одно обстоятельство, подталкивающее разработчиков Linux к пересмотру принципов работы с оборудованием. Дело в том, что, с точки зрения ОС, принципиально различным образом подключенные устройства, в общем-то, очень похожи. И тем, и другим требуется драйвер, да и представлены они, в конечном счёте, одинаково. Однако об этом — ниже.

Устройства Linux

Linux, как известно, унаследовал от UNIX представление устройств в виде файлов, находящихся в каталоге /dev и характеризующихся уникальным идентификаторами — числами minor и major. Существование таких файлов — абсолютно необходимое условие доступности устройств, и не так давно общепринятой практикой было их «заблаговременное» создание. В настоящий момент каталог /dev, заполненный подобным образом, выглядит просто необъятным, что, по меньшей мере, раздражает.

Кроме того, поскольку под номера minor и major изначально было выделено всего по байту, и в скором времени их перестанет хватать. Одним словом, изменения назрели, и, забегая вперёд, скажем, что udev, опирающийся на данные новой драйверной модели, доступные через sysfs, является очередной (после devfs) попыткой решения проблемы неуправляемого роста /dev.

Ещё одно условие использования устройства — это существование и загрузка соответствующего модуля ядра — драйвера. Модуль этот может быть включён в состав ядра во время компиляции последнего, но сути это не меняет: нет модуля — нет работоспособного устройства. Вот теперь картина получается вполне законченной:

  • система Plug-and-Play (а в случае Linux это BIOS) обнаруживает устройство, конфигурирует его и выделяет ему необходимые ресурсы c учётом «пожеланий» всех остальных устройств;
  • драйвер находит «своё» устройство, читает его конфигурацию, и после этого готов выполнять свои коммуникативные функции;
  • драйвер должен быть «ассоциирован» с файлом устройства (а последний, в свою очередь, должен существовать), и только после этого обращение к файлу в /dev, фактически, станет обращением к устройству.

Для виртуальных устройств, каковыми являются сетевые адаптеры ethN, достаточно последовательно загружать командой modprobe module_name модули из каталога /lib/modules/version/kernel/drivers/net, пока вывод команды cat /proc/net/dev не укажет на то, что устройство eth0 появилось. Именно так некоторые live-cd и поступают.

Для устройств, нуждающихся в наличии «своего» файла в /dev, потребуется наличие devfs и соответствующего демона devfsd, реагирующего на подключение устройства загрузкой соответствующего драйвера и созданием файла в /dev. Так, для звукового PCI-адаптера модули нужно брать из /lib/modules/version/kernel/sound/pci, а результат оценивать по по наличию устройства /dev/dsp.

Разумеется, существовали и более интеллектуальные способы поиска нужного драйвера для устройства, основанные на сопоставлении идентификаторов оборудования (cat /proc/bus/pci/devices) и наличия аналогичных данных модулей из файла /lib/modules/version/kernel/modules.pcimap, только то, что подходит для сетевого адаптера или звуковой карты, трудно приложить к неизвестному оборудованию: драйвер, положим, нашли и загрузили, а какой файл устройства создавать? Да и принципы работы devfs оказались не без изъяна. Чего стоило только «размножение» устройств при многократном подключении одного и того же USB-накопителя…

Новая драйверная модель Linux

Довольно серьёзные проблемы в работе с PnP устройствами преодолены в Linux к середине 2004-го года. Из не-PnP системы Linux превратился в систему с децентрализованным механизмом PnP: Plug-and-Play реализуется для каждого отдельного устройства. Теперь драйвер, обнаружив «своё» устройство, запрашивает закреплённые за ним ресурсы и, как правило, «соглашается» с таким распределением. Драйвер, используя функции ядра, может попытаться переопределить ресурсы устройства. В этом случае за отсутствие возможных конфликтов отвечает ядро — оно просто не принимает адреса, конфликтующие с другими устройствами.

Драйверная модель требовала изменений. Прежде всего, устранению подлежали следующие недостатки ядра 2.4:

  • отсутствие единого метода представления связей между драйверами (модулями ядра) и устройствами;
  • отсутствие общего hotplug-механизма, единого для PnP-конфигурируемых устройств и устройств, подключаемых к независимым шинам, таким как USB или PCMCIA;
  • загромождение procfs информацией, не имеющей отношения к процессам. Фактически такой, для хранения которой она изначально не предназначалась.

Вообще-то, говорить следовало бы прежде всего об изменениях в архитектуре ядра и появлении новых объектов подсистемы обслуживания устройств (kobjects), но это всё «дела ядерные». Для большинства же пользователей наиболее заметным следствием внедрения новой драйверной модели стало появление sysfs — виртуальной файловой системы, экспортирующей в пространство пользователя информацию ядра о присутствующих в системе устройствах и драйверах. Автором sysfs (ранее именовавшейся driverfs) является Патрик Мошель (Patrick Mochel), а для больших систем ее дорабатывал Маниш Сони (Maneesh Sony).

Если вернуться к вышеперечисленным недостаткам ядра 2.4, то можно сказать, что первые два устраняются благодаря тому, что информация как об устройствах, так и о загруженных драйверах, представляется в рамках одной файловой системы. Причём в список устройств включаются устройства на всех шинах IBM PC. procfs же разгружается потому, что sysfs монтируется не как подкаталог /proc, а как каталог /sys.

sysfs не может быть модулем, она всегда — часть ядра. Для обеспечения доступа к ней нужно выполнить команду mount -t sysfs sysfs /sys. Обычно это монтирование выполняется первым же стартовым скриптом.

Существует непосредственная связь между sysfs и объектами инфраструктуры ядра. Всем зарегистрированным системой объектам инфраструктуры (тем самым kobjects) в /sys соответствуют свои каталоги. Форма дерева каталогов, берущего своё начало в /sys, максимально точно соответствует соотношению между объектами. Активно используемые символические ссылки делают это дерево несколько «развесистым», но зато отражают отношения наследования и связи между объектами в максимальной степени.

Если каждое устройство или драйвер — каталог, то файлы каталогов — атрибуты этих объектов. Таким образом, sysfs превращает операции ввода/вывода для этих файлов в методы атрибутов объектов ядра. Операции записи/чтения атрибутов kobjects становятся такими же доступными, как операции записи и чтения файлов.

Файлы атрибутов — текстовые. Существует правило, в соответствии с которым одному атрибуту соответствует один файл. Поскольку хранение по одному значению в файле может оказаться неэффективным, то «на будущее» приветствуется выражение атрибутов в форме массива значений одного типа. Нужно сказать, что sysfs родилась не на пустом месте, и если информация о связях объектов ядра — это, действительно, новость, то большую часть атрибутов устройств можно было получить и в «старой» /proc/sys. Файлы атрибутов в /sys, однако, существенно отличаются от файлов procfs, синтаксический анализ содержимого которых мог быть довольно сложным.

Структура sysfs выражает соотношения структур данных ядра. Названия подкаталогов /sys говорят сами за себя:

  • devices/ — полностью соответствует внутреннему дереву устройств ядра, а символические ссылки в подкаталогах (когда они есть, разумеется) указывают на шину устройства, принадлежность его к определённому классу, соответствующий загруженный драйвер и т. п. Дерево может быть достаточно сложным и отражает связь между устройствами;
  • bus/ — представляет собой перечень шин, зарегистрированных в ядре. Каталог каждой шины содержит подкаталоги devices/ и drivers/. Причём,
  • devices/ — это символические ссылки на каталоги устройств, описанных в системе (реально расположенных в /sys/devices/…);
  • drivers/ — каталоги драйверов, загруженных для устройств, подключенных к данной шине. Каждый такой каталог содержит, как минимум, пару файлов-атрибутов bind и unbind, предназначенных для управления драйвером (см. LXF73), а когда драйвер обнаруживает «своё» устройство, то в каталоге появляется символическая ссылка на каталог этого устройства.
  • block/ содержит каталоги всех блочных устройств, присутствующих в настоящее время в системе. В данном случае под устройством понимается совокупность физического устройства и драйвера. То есть, если при подключении USB-накопителя некоторое новое устройство в /sys/devices/ появится всегда (можно говорить о наличии физического устройства), то появление каталога /sys/block/sda зависит ещё и от наличия в памяти необходимых драйверов (usb-storage, sd_mod и т. д. — включая все драйвера, необходимые для поддержки usb);
  • class/ — отражает группировку устройств в классы. Всякое подключенное устройство создаст новый подкаталог в дереве /sys/class. Как и в предыдущем случае, подразумевается наличие и устройства, и его драйвера;
  • остальные каталоги могут быть очень полезны. Так, в module/ можно узнать атрибуты, а в некоторых случаях даже изменить параметры загруженных модулей. Через файлы power/ осуществляется перевод системы в режим suspend, но к определению оборудования содержимое остальных каталогов отношения уже не имеет.

Более детальную информацию об особенностях модели драйверов при желании можно найти в файлах каталога /usr/src/linux/ Documentation/driver-model. Обширной, однако, её пока не назовёшь. Да и вообще, нельзя сказать, что информация в /sys предназначена для «простых пользователей». Sysfs используется системными утилитами, такими, как udev и HAL, обеспечивая их доступом к информации об устройствах и их драйверах. Модифицируя атрибуты устройств и драйверов, udev и HAL теперь могут даже изменять конфигурацию последних.

Что из этого следует?

Из того факта, что существование sysfs, в принципе, может быть проигнорировано «простым» пользователем, совершенно не следует, что новая драйверная модель не отражается на «повседневном» использовании Linux. Окончательный переход на ядро 2.6 влечёт за собой неминуемое изменение архитектуры дистрибутивов, предлагаемых их разработчиками. Внешние изменения в системе управления загрузкой модулей (новые расширения файлов модулей, modprobe. conf вместо modules.conf) отнюдь не случайны: они введены для обеспечения возможности временного сосуществования старой и новой моделей.

Вслед за вышеупомянутыми udev и HAL (ближайшими к драйверной модели ядра утилитами), изменения косаются определителей оборудования, инсталляторов, LiveCD — всех, кому приходится заниматься конфигурацией системы.

На настоящий момент загрузка драйверов для постоянно присутствующего в системе оборудования обеспечивается либо командами «modprobe module_name» в стартовых скриптах, либо строками «alias string module_name» в modprobe.conf, либо принудительным запуском hotplug (т.наз. coldplug, когда hotplug-события синтезируются в ходе загрузки системы), либо комбинацией всех трёх способов. За загрузку драйверов для подключаемых «на ходу» устройств, отвечает, обычно, hotplug. Тот же hotplug генерирует события для udev, заставляя последний создавать для вновь появившегося устройства файл в /dev. Всё это, разумеется, происходит при участии ядра. Интерфейсом же между ядром и утилитами является, как вы, наверное, догадываетесь, sysfs.

Новая драйверная модель и рост вычислительных возможностей IBM PC делают возможным упрощение этой схемы. Судите сами: что такое десяток-другой килобайт модуля драйвера для ЭВМ с гигабайтом памяти? Так почему бы не загрузить модули «с запасом»? udev/HAL останется только создавать в /dev соответствующий файл при подключении устройства, hotplug же, таким образом, становится вообще не нужен.

Ещё один вариант: возложить на udev обработку hotplug-событий. Почему бы не устранить промежуточное звено, тем более, что стиль управления работой udev представляется более гибким? Очевидно, всё определяется назначением целевой системы. Так, домашний компьютер стоит настроить «вручную», поскольку потеря времени на определение устройств при каждой загрузке раздражает («ну что ты там ищешь? ничего нового за ночь не выросло»), а LiveCD должен определять оборудование «с нуля».

Практикум

В качестве практикума предлагаю посредством анализа /sys рассмотреть программную поддержку ide-устройств. Наиболее полно дерево устройств представлено, как уже говорилось, в каталоге /sys/devices. Однако полнота представления может быть и утомительной. Поэтому, оставив в стороне каталоги /sys/block и /sys/class (их содержимое зависит как от наличия собственно устройств, так и от присутствия драйверов, а хотелось бы пройти весь путь исключительно от «железа»), сосредоточимся на /sys/bus.

Переходим в подкаталог ide/. Поскольку управлять контроллерами через sysfs мы не планируем, то интересовать нас будут исключительно каталоги. Набираем: tree -d (ключ -d отфильтровывает файлы) — и вот, что мы имеем:

.
|-- devices
| |-- 0.0 -> ../../../devices/pci0000:00/0000:00:1f.1/ide0/0.0
| `-- 0.1 -> ../../../devices/pci0000:00/0000:00:1f.1/ide0/0.1
`-- drivers
    |-- ide-cdrom
    |-- ide-disk
    | `-- 0.0 -> ../../../../devices/pci0000:00/0000:00:1f.1/ide0/0.0
    `-- ide-scsi
      `-- 0.1 -> ../../../../devices/pci0000:00/0000:00:1f.1/ide0/0.1

«Стрелочки», как вы, вероятно, догадались, обозначают символические ссылки. Анализ прост до примитивности: имеем два ide-устройства и три драйвера. О том, что одно из устройств — диск, а второе — CDROM, также говорить излишне. То, что драйвер ide-disk обслуживает винчестер — естественно, а вот то, что CD-ROM обслуживается драйвером ide-scsi, уже представляет некоторый интерес. И не потому, что CDROM на самом деле подключен к шине IDE (cdrecord рекомендует эмулировать ide-cdrom как scsi-устройство), а потому, что, если память не изменяет, ядро 2.6 советует от этой эмуляции уже отказаться. Отметим это обстоятельство (нужно будет попробовать) и отправимся по ссылке ../../../devices/pci0000:00/0000:00:1f.1/ide0/0.0, поскольку в /sys/bus/ide ничего интересного больше нет. «По пути» к этому каталогу советую внимательно «смотреть по сторонам». Так мы узнаем, что контроллер ide «сидит» на шине PCI (о чём и так можно было догадаться) и имеет идентификатор 0000:00:1f.1. Способ формирования идентификатора особого интереса не представляет, да и потребуется он только в том случае, если мы захотим сопоставить наблюдаемые данные с результатом вывода упоминавшейся выше lspci. В каталоге /sys/devices/pci0000:00/0000:00:1f.1/ исследовав символические ссылки, можно узнать, что драйвер, используемый PCI-контроллером, - PIIX_IDE. Спустившись в ide0, увидим, что контроллер этот обслуживает два устройства, младшее из которых (0.0) и является винчестером. Символические ссылки указывают на то, что устройство идентифицируется в системе как /dev/hda, принадлежит шине ide и обслуживается драйвером ide-disk. Последовав по символической ссылке block/, можно получить данные о разметке диска.

Аналогичный поход для CD-ROM приведёт нас к устройству ide0/0.1, которое окажется scsi (а что ещё можно было ожидать от драйвера ide-scsi?). В соответствии с архитектурой scsi, мы обнаружим host0 (принадлежащий классу scsi_host, разумеется). host0, в свою очередь, содержит target0:0:0, единственное устройство которого (0:0:0:0) и является нашим CD-ROM. Он принадлежит шине scsi, идентифицируется в системе как /dev/sr0, обслуживается драйвером sr и входит в класс scsi_generic с именем sg0. Перебрав атрибуты, можно найти и модель, и тип, и производителя привода. Пожалуй, этого достаточно. Прошу прощения, если утомил. Зато, надеюсь, убедил в том, что sysfs предоставляет предостаточно информации для того, чтобы идентифицировать устройство, определить модули, которые обеспечивают его программную поддержку и определить положение в дереве устройств.

И ещё один пример для самых любознательных. На сей раз — более полезный.

Предлагаю попытаться подключить, скажем, USB-IrDA Bridge неизвестного производителя из одной большой восточной страны. USBIrDA Bridge — это такая штуковина, которая, будучи подключенной к USB-разъёму, эмулирует ИК-порт. Для связи с мобильным телефоном, например. Ни о каком драйвере производителя и речи быть не может, да и опознавательных знаков всего два: «IrDA» и «Made in China». Итак…

Начать, как и в предыдущем случае, предлагаю с /sys/bus. Интересующий каталог, разумеется — usb/. Поскольку драйвер для моего «подопытного» в системе заведомо отсутствует, то переходим сразу в каталог devices/. USB-контроллеры, коих у меня обнаружилось целых пять, представляют из себя эдакие «дуальные» устройства: с одной стороны это мосто между шинами PCI и USB, а с другой — первое устройство на USB-шине, называемое, обычно, корневым концентратором (root hub). Оба «лица» представлены в каталоге /sys/bus/usb/devices/ в виде символических ссылок. Мосты имеют «собственные» имена (usb1..usb5), а все hub-ы (как, впрочем и остальные устройства, подключаемые к usb-шине) представлены численными идентификаторами вида N-P:С.I, где N — номер моста (он же — номер hub-а), P — номер порта hub-а, вообще-то соответствующий определённому usb-разъёму, а для самого hub-а всегда равный нулю. C — номер конфигурации и I — номер интерфейса. Мосты обнаруживаются драйвером usb, hub-ы — драйвером hub.

Любое подключение usb-устройства приводит к появлению в каталоге новых ссылок. Так, в частности, подключение «подопытного» устройства привело к появлению ссылки 2-1:1.0, что означает: к первому порту второго hub-а подключено устройство, конфигурация — 1, интерфейс — 0. Переход в каталог этого устройства подтвердит, что нужный драйвер не загружен — ссылки driver не существует. Теперь попытаемся применить подход, описанный для сетевых карт: командой modprobe последовательно загружаем модули из каталога /lib/modules/2.6.15/kernel/drivers/net/irda, благо их там пока всего 12. Если повезёт, то после загрузки очередного модуля (в моём случае это оказался stir4200) ссылка driver появится. Более того, в моём случае появилась ссылка net:irda0, что говорит о том, что в системе обнаружено устройство класса net с именем irda0. С чем себя и поздравляю. Это, правда, не означает, что вы тут же сможете подключить и мобильный телефон: пока это только основание для уверенности, что связь по ИК-каналу между телефоном и вашей Linux-системой возможна. Однако, это уже совсем другая история…

Выводы

Драйверная модель ядра Linux версии 2.6.х предоставляет, без сомнения, новые возможности для определения оборудования. В некоторых случаях можно говорить о новом подходе к написанию инсталляторов и LiveCD. Можно также рассчитывать на увеличение количества модулей поддержки оборудования в составе ядра, поскольку одной из целей внедрения модели было упрощение разработки драйверов.

С другой стороны, мне кажется, что в данном случае, вслед за разработчиками ядра, приверженцы Linux выбирают «не Windows-way». С новыми возможностями ядра специалист получит дополнительные возможности для оптимизации системы, а «простой пользователь» опять станет заложником создателей дистрибутивов — как скоро (и насколько успешно) они интегрируют эти самые возможности.

Иными словами, Linux продолжает развиваться в направлении предоставления больших возможностей для создания систем различного назначения и сложности, а не как система, ориентированная на конечного пользователя офисного или домашнего компьютера. Последнего, будем надеяться, порадуют своими разработками составители «пользователь-ориентированных» дистрибутивов — ведь расширение возможностей системы означает и большие возможности для создания идеального настольного компьютера. Но не будем требовать от них слишком многого: динамично развивающееся ядро в который раз заставляет пересматривать архитектуру системы.

Персональные инструменты
купить
подписаться
Яндекс.Метрика