Символьные устройства linux примеры


Файлы символьных устройств

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

Первоначальная цель файлов устройства состоит в том, чтобы позволить процессам связываться с драйверами устройства в ядре, и через них с физическими устройствами (модемы, терминалы, и т.д.).

Каждый драйвер устройства, который является ответственным за некоторый тип аппаратных средств, имеет собственный главный номер. Список драйверов и их главных номеров доступен в /proc/devices . Каждое физическое устройство, управляемое драйвером устройства имеет малый номер. Каталог /dev включает специальный файл, названный файлом устройства, для каждого из тех устройств, которые реально установлены в системе.

Например, если Вы даете команду ls -l /dev/hd[ab]* , вы увидите все IDE разделы жесткого диска, которые могли бы быть связаны с машиной. Обратите внимание, что все из них используют тот же самый главный номер, 3, но малые номера у каждого свои! Оговорка: Считается, что вы используете архитектуру PC. Я не знаю ничего относительно файлов устройств Linux на других архитектурах .

Когда система была установлена, все файлы устройств были созданы командой mknod . Не имеется никакой технической причины, по которой они должны быть в каталоге /dev , это только полезное соглашение. При создании файла устройства для целей тестирования, как с упражнением здесь, вероятно имело бы смысл поместить его в каталог, где Вы компилируете модуль.

Устройства разделены на два типа: символьные и блочные. Различие в том, что блочные имеют буфер для запросов, так что они могут выбирать в каком порядке им отвечать. Это важно в случае устройств памяти, где скорее понадобится читать или писать сектора, которые ближе друг к другу, чем те, которые находятся далеко. Другое различие: блочные устройства могут принимать ввод и возвращать вывод только в блоках (чей размер может измениться согласно устройству), в то время как символьные устройства могут использовать столько байтов, сколько нужно. Большинство устройств в мире символьно, потому что они не нуждаются в этом типе буферизации и не работают с фиксированным размером блока. Вы можете узнать, является ли устройство блочным или символьным, рассматривая первый символ в выводе ls -l . Если это «b», значит устройство блочное, а если «c», то символьное.

Этот модуль разделен на две отдельных части: часть модуля, которая регистрирует устройство и часть драйвера устройства. init_module вызывает module_register_chrdev , чтобы добавить драйвер устройства к символьной таблице драйверов устройств ядра. Этот вызов также возвращает главный номер, который нужно использовать для драйвера. Функция cleanup_module вычеркивает из списка устройство.

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

Драйвер устройства выполняет четыре действия (функции), которые вызываются, когда кто-то пробует делать что-либо с файлом устройства, который имеет наш главный номер. Ядро знает, что вызвать их надо через структуру file_operations , Fops , который был дан, когда устройство было зарегистрировано, включает указатели на те четыре функции, которые данное устройство выполняет.

Читайте также:  Where is windows icon

Еще мы должны помнить, что мы не можем позволять модулю выгружаться командой rmmod всякий раз, когда root захочет его выгрузить. Причина в том что, если файл устройства открыт процессом, и мы удаляем модуль, то использование файла вызвало бы обращение к точке памяти где располагалась соответствующая функция. Если мы удачливы, никакой другой код не был загружен туда, и мы получим уродливое сообщение об ошибках. Если мы неудачливы (обычно так и бывает), другой модуль был загружен в то же самое место, что означает переход в середину другой функции внутри ядра. Результаты этого невозможно предсказывать, но они не могут быть положительны.

Обычно, когда Вы не хотите выполнять что-либо, Вы возвращаете код ошибки (отрицательное число) из функции, которая делает данное действие. С cleanup_module такой фокус не пройдет: если cleanup_module вызван, модуль завершился. Однако, имеется счетчик использований, который считает, сколько других модулей используют этот модуль, названный номером ссылки (последний номер строки в /proc/modules ). Если это число не нулевое, rmmod будет терпеть неудачу. Счетчик модульных ссылок доступен в переменной mod_use_count_ . Так как имеются макрокоманды, определенные для обработки этой переменной ( MOD_INC_USE_COUNT и MOD_DEC_USE_COUNT ), мы предпочитаем использовать их, а не mod_use_count_ непосредственно, так что мы будем в безопасности, если реализация изменится в будущем.

Источник

Символьные устройства linux примеры

Ядро Linux реализует поддержку двух типов устройств – символьных и блочных. Основное их отличие в том, что для блочных устройств операции ввода вывода осуществляются не отдельными байтами (символами), а блоками фиксированного размера.

В Linux вся работа с устройствами ведется через специальные файлы, которые обычно расположены в каталоге /dev . Специальные файлы не содержат данных, а просто служат точками, через которые можно обратиться к драйверу соответствующего устройства. У каждого специального файла есть три характеристики – тип устройства (character или block), старший номер устройства (major number) и младший номер (minor number). Для примера, посмотрим на содержимое каталога /dev :

Как видно, в листинге присутствует описание семи устройств, четырех блочных и трех символьных. Для каждого файла можно увидеть его тип (первая буква в списке прав доступа), пользователя-владельца, группу-владельца, major number, minor number, дату модификации и имя файла.

Для поддержки работы с устройствами в ядре хранятся две таблицы, одна для списка символьных устройств, другая для списка блочных устройств. Каждая строка таблицы сопоставлена какой-то разновидности устройств соответствующего типа – например, для типа “символьные устройства” можно выделить следующие разновидности: COM-порты, LPT-порты, PS/2-мыши, USB-мыши и т.д., для типа “блочные устройства” можно выделить SCSI-диски, IDE-диски, SCSI-CD-приводы, виртуальные диски которыми представляются RAID-контролеры и т.п.

Каждая ячейка в этих системных таблицах сопоставляется конкретному экземпляру устройства. Таким образом, с точки зрения ядра каждое устройство оказывается однозначно проидентифицировано тремя параметрами – типом устройства (блочное или символьное) и двумя числами – номерами строки и номером столбца таблицы, в которой хранится ссылка на драйвер этого устройства.

Пример таблицы символьных и устройств

Диспетчер томов LVM

Микшер первой зв. карты

Первая видеокарта NVidia

Пример таблицы блочных устройств

IDE Primary Master

Раздел 1 на IDE Primary Master

Раздел 2 на IDE Primary Master

Раздел 16 на IDE Primary Master

IDE Primary Slave

Раздел 1 на IDE Primary Slave

Раздел 1 на SCSI-диске 1

Раздел 2 на SCSI-диске 1

Раздел 1 на SCSI-диске 4

При попытке обращения к такому специальному файлу ядро переадресует обращение через нужный драйвер на устройство в соответствии с теми данными, которые указаны в таблице устройств, причем конкретная таблица устройств будет выбрана в зависимости от типа устройства, строка из таблицы будет выбрана по major number, и столбец будет выбран по minor number. Если мы посмотрим на примеры наших таблиц, то увидим, что обращение на специальный файл /dev/ttyS1 , который представляет символьное устройство со старшим номером 4 и младшим номером 65 будет адресовано на последовательный порт COM2, а обращение к файлу /dev/hda2 (блочное устройство со старшим номером 3 и младшим номером 2) будет адресовано на 2-й раздел жесткого диска IDE, работающего в режиме primary master.

Читайте также:  Rundll возникла ошибка при запуске не найден указанный модуль windows

13.1. Статическая организация каталога /dev

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

Процесс работы со статическим /dev особых проблем не вызывает – системный администратор при необходимости просто создает отсутствующие файлы командой mknod или MAKEDEV . В том случае, когда какая-либо программа обращается к устройству, чей драйвер не загружен (или загружен, но ни одного соответствующего устройства не было обнаружено), операционная система возвращает ошибку при попытке открытия файла такого “неверного” устройства. Ниже приведен пример создания специального файла, соответствующего блочному устройству с мажором 8 и минором 33 и попытка его использования (отметим, что этот специальный файл соответствует разделу на жестком диске, который не существует на тестовой машине, где выполнялись эти команды):

Сообщение No such device or address как раз и означает, что записи для данного устройства в таблице блочных устройств не существует.

Ядро Linux совместно с некоторыми системными утилитами поддерживает такую интересную возможность, как загрузка драйверов “по требованию”. Реализуется это следующим образом – в момент, когда какая-либо программа пытается открыть специальный файл, не связанный ни с каким драйвером, ядро делает попытку подобрать соответствующий драйвер самостоятельно. Необходимый драйвер для каждого специального файла определяется в файле /etc/modules.conf путем задания специального алиаса (alias) для модуля. Для активизации автоматической загрузки драйвера какого-либо символьного устройства в большинстве случаев достаточно просто записать в /etc/modules.conf строку следующего вида:

Для блочных устройств соответствующая запись слегка меняет свою форму:

X и Y – это major и minor специального файла, попытка открыть который должна активизировать автоматическую загрузку драйвера. Владельцы видеокарт на чипе nVidia могут увидеть этот подход в действии – программа инсталляции драйвера nVidia автоматически прописывает в modules.conf запись для загрузки «по требованию» той части драйвера, которая работает в режиме ядра.

Вместо X или Y может также быть подставлен символ “*” , означающий “любое число”. Например, пусть в modules.conf будет написан следующий текст:

Тогда при обращении к любому символьному устройству с major number равным 81 и которое не ассоциировано ни с каким драйвером, система попытается загрузить драйвер bttv (драйвер TV-тюнера на основе чипа bt848).

Эта возможность обеспечивает Linux возможность плавной загрузки и эффективного использования ресурсов – драйвер не загружается, пока в нем не возникнет необходимости. К сожалению, за простоту этой схемы приходится платить большим количеством специальных файлов в /dev .

13.2. Что такое DevFS

Для того, чтобы избавить администратора от ручного создания специальных файлов и для уменьшения количества файлов в /dev был реализован второй способ организации /dev – динамическое создание специальных файлов процессе загрузки драйверов. Реализовано это было следующим образом:

Ядро монтирует к каталогу /dev специальную файловую систему, называемую devfs – эта файловая система хранится целиком в оперативной памяти и не занимает никакого места на диске. Когда какой-либо драйвер в процессе загрузки или работы обнаруживает обслуживаемое им устройство, он регистрирует это устройство и сообщает о нем драйверу devfs. Драйвер devfs создает специальный файл, который виден прикладным программам и может быть корректно открыт. При выгрузке же драйвер устройства сообщает devfs о том, что соответствующее устройство уже не активно, и драйвер devfs удаляет запись о соответствующем специальном файле из файловой системы devfs.

Читайте также:  Что значит тильда linux

Файловая система devfs отличается тем, что как правило специальный файл для устройства создается с длинным путем – например, для раздела на scsi-диске путь может выглядеть примерно так: /dev/scsi/host1/bus1/target3/lun4/partition2 .

Эта особенность является весьма важным плюсом devfs, поскольку она позволяет адресовать дисковые устройства путем указания логического пути их подключения и избежать смены имен SCSI-дисков в некоторых случаях (об этих случаях будет рассказано позднее).

Для того, чтобы организовать более прозрачную структуру каталогов и файлов устройств, используется специальный демон devfsd. Он взаимодействует с драйвером devfs и ядром и в процессе активизации и деактивизации устройств он создает и удаляет символьные ссылки вида /dev/disks/disc0 или /dev/hda1 .

Надо отметить, что схема динамического /dev в некотором смысле близка к той организации каталога /dev , которая используется некоторыми коммерческими UNIX-системами (например, в Solaris), когда есть виртуальная файловая система /devices , и на ее файлы создаются ссылки из /dev , только в Linux роль программы cfgadm играет демон devfsd, и все изменения в состав /dev вносятся автоматически.

С помощью devfsd файловая система devfs также реализует автоматическую загрузку модулей, но в этом случае выбор модуля идет не через комбинацию type/major/minor, а путем указания имени запрошенного файла – когда приложение пытается открыть несуществующий файл устройства, devfs передает имя запрошенного файла демону devfsd, и последний загружает необходимые модули, например такой код в файле modules.devfs :

Приведет к тому, что при попытке обращения к любому файлу, чей полный путь начинается строкой /dev/nvidia , будет произведена попытка загрузить драйвер nvidia.o (для ядра 2.6 nvidia.ko )

В принципе, на сегодняшний день выбор того, каким именно образом необходимо организовывать /dev , остается за пользователем и создателем дистрибутива. Например, в Mandrake Linux используется devfs, а в RedHat, Fedora и SUSE каталог /dev организован статическим образом, а опытные пользователи часто меняют способ организации /dev в зависимости от своих предпочтений.

13.3. Немного о UDEV

В современных дистрибутивах и ядрах поддержка devfs/devfsd отключена, и на смену этой паре пришел специальный демон, называемый udev. В отличие от devsfd, который требовал поддержки со стороны ядра, udev такой поддержки не требует. При инициализации устройства ядро подает сигнал через файловую систему sysfs, и демон udevd, получив сигнал об этом событии, самостоятельно создает соответствующий специальный файл устройства в каталоге /dev в соответствии с правилами, описанными в его конфигурационных файлах. При необходимости в этих файлах можно указать например вызов некоторой внешней программы, создание символьной ссылки и так далее.

Например, если некоторое устройство после подключения перед началом работы требует дополнительной настройки с использованием внешних программ, можно создать соответствующее правило для udev, в котором будет указано какую программу вызвать и какие параметры ей необходимо передать – в частности, это может потребоваться для data-кабелей к некоторым мобильным телефонам Nokia, для устройств которым для корректной работы требуется firmware, или для сохранения или восстановления текущих настроек устройства.

Тем не менее, несмотря на внешние отличия между статической организацией /dev , devfs и udev, следует помнить что это всего лишь способ заполнения каталога /dev , и во всех случаях в конечном итоге на файловой системе создаются те же самые файлы символьных и блочных устройств.

Источник

Оцените статью