Как выгрузить драйвер linux

Драйверы устройств в Linux

Часть 2: Пишем в классе наш первый драйвер для Linux

Оригинал: «Device Drivers, Part 2: Writing Your First Linux Driver in the Classroom»
Автор: Anil Kumar Pugalia
Дата публикации: December 1, 2010
Перевод: Н.Ромоданов
Дата перевода: июнь 2012 г.

В этой статье, которая является частью серии статей о драйверах устройств в Linux, речь идет о концепции динамической загрузки драйверов — сначала мы перед тем, как собирать драйвер, напишем драйвер для Linux, а затем, после сборки, загрузим его.

Светлана и Пагс добрались в свой класс с опозданием и увидели, что их профессор уже начал читать лекцию. Светлана робко попросила разрешения войти. Раздраженный профессор Гопи ответил: «Входите! Вы, друзья, опять сегодня опоздали, и по какой причине»?

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

Он объяснил: «Как известно, при обычной установке драйверов в Windows для того, чтобы их активировать, необходимо перезагрузить систему. А если это, предположим, действительно неприемлемо в случае, если это нужно делать на сервере? Вот где выигрывает Linux. В Linux можно загружать и выгружать драйверы на лету, и это активно используется сразу после загрузки системы. Кроме того, драйвер мгновенно отключается после его выгрузки. Это называется динамической загрузкой и выгрузкой драйверов в Linux «.

Это впечатлило профессора. «Хорошо! Идите на свои места, но больше не опаздывайте». Профессор продолжил лекцию: «Теперь, когда вы уже знаете, что такое динамическая загрузка и выгрузка драйверов, я, прежде, чем мы перейдем к написанию нашего первого драйверов, покажу вам, как загружать и выгружать драйвера».

Динамическая загрузка драйверов

Эти динамически загружаемые драйвера чаще всего называют модулями, которые собираются в виде отдельных модулей с расширением .ko (объект ядра). В каждой системе Linux в корне файловой системы (/) есть стандартное место для всех предварительно собранных модулей. Они организованы аналогично древовидной структуре исходных кодов ядра и находятся в директории /lib/modules/ /kernel , где результат вывода системной команды uname -r (см.рис.1).

Рис.1: Предварительно собранные модули Linux

Чтобы динамически загружать и выгружать драйверы, воспользуйтесь следующими командами, которые находятся в директории /sbin и должны выполняться с привилегиями пользователя root:

  • lsmod — список модулей, загруженных в текущий момент
  • insmod — добавление / загрузка указанного файла модуля
  • modprobe — добавление / загрузка модуля вместе со всеми его зависимостями
  • rmmod — удаление / выгрузка модуля

Давайте в качестве примера рассмотрим соответствующие драйвера файловой системы FAT. На рис.2 показан весь процесс нашего эксперимента. Файлы с модулями будут fat.ko , vfat.ko и т.д., находящиеся в директории fat (в vfat для старых версий ядра) в /lib/modules/`uname -r`/kernel/fs . Если они представлены в сжатом формате .gz , вам нужно будет распаковать их с помощью команды gunzip , прежде чем вы сможете выполнить операцию insmod .

Рис.2: Операции с модулями Linux

Модуль vfat зависит от модуля fat , так что первым должен быть загружен модуль fat.ko . Чтобы автоматически выполнить распаковку и загрузку зависимостей, воспользуйтесь командой modprobe . Обратите внимание, что когда вы пользуетесь командой modprobe , вы не должны в имени модуля указывать расширение .ko . Команда rmmod используется для выгрузки модулей.

Читайте также:  Не запускается дисковая утилита при загрузке mac os

Наш первый драйвер для Linux

Перед тем, как написать наш первый драйвер, давайте рассмотрим некоторые понятия. Драйвер никогда не работает сам по себе. Он похож на библиотеку, загружаемую из-за функций, которые будут вызваны из работающего приложения. Он написан на языке C, но в нем отсутствует функция main() . Кроме того, он будет загружаться / компоноваться с ядром, поэтому он должен компилироваться аналогично тому, как было откомпилировано ядро, и вы можете в качестве заголовочных файлов использовать только те, что есть в исходном коде ядра, а не из стандартного директория /usr/include .

Интересный факт, касающийся ядра, это то, что оно, как мы видим даже на примере нашего первого драйвера, представляет собой объектно-ориентированную реализацию на языке C. В любом драйвере есть конструктор и деструктор. Когда модуль успешно загружается в ядро, то вызывается конструктор модуля, а дескруктор модуля вызывается, когда команде rmmod удается успешно выгрузить модуль. Это в драйвере две обычные функции, разве что они называются init и exit, соответственно, и вызываются с помощью макросов module_init() и module_exit() , которые определены в заголовков ядра module.h .

С учетом вышесказанного это полный код нашего первого драйвера; назовем его ofd.c. Обратите внимание, что отсутствует заголовок stdio.h (заголовок пользовательского пространства), вместо него мы используем аналог kernel.h (заголовок пространства ядра). Функция printk() эквивалентна функции printf() . Кроме того, для обеспечения совместимости версии модуля с ядром, в которое будет загружен модуль, добавлен заголовок version.h . С помощью макроса MODULE_* заполняется информация, относящаяся к модулю, которая будет использована как «подпись» модуля.

Сборка нашего первого драйвера

Т.к. у нас есть код на языке C, настало время его скомпилировать и создать файл модуля ofd.ko . Для этого мы используем систему сборки ядра. В приведенном ниже файле Makefile происходит обращение к системе сборки ядра из исходных кодов, а файл Makefile ядра, в свою очередь, обращается к файлу Makefile нашего нового драйвера с тем, чтобы собрать драйвер.

Чтобы собрать драйвер для Linux, у вас в системе должен быть исходный код ядра (или, по крайней мере, заголовки ядра). Предполагается, что исходный код ядра будет находиться в директории /usr/src/linux . Если в вашей системе он находится в каком-нибудь другом месте, то укажите это место в переменной KERNEL_SOURCE в файле Makefile .

Когда есть код на языке C ( ofd.c ) и готов файл Makefile , то все, что нам нужно сделать для сборки нашего первого драйвера ( ofd.ko ), это вызвать команду make .

Подведем итог

Как только у нас будет файл ofd.ko , мы в роли пользователя root или с помощью команды sudo выполним обычные действия.

Команда lsmod должна вам сообщить о том, что драйвер ofd загружен.

Пока студенты экспериментировали со своим первым модулем, прозвенел звонок, сообщивший об окончании урока. Профессор Гопи подвел итог: «В настоящий момент мы не увидели ничего, кроме того, что модуль lsmod сообщил о загрузке драйвера. Куда выводит информацию команда printk ? Найдите это самостоятельно на лабораторных занятиях и познакомьте меня с своими выводами. Также учтите, что наш первый драйвер будет шаблоном для любого драйвера, который можно написать для Linux. Написание специализированных драйверов это всего лишь вопрос о том, чем будет заполнен конструктор и деструктор драйвера. Поэтому дальнейшее изучение будет представлять собой расширение данного драйвера с целью получить драйвер с конкретными функциональными возможностями».

Источник

Драйверы устройств в Linux

Часть 17: Взаимодействие с модулями

В этой статье, которая является частью серии статей о драйверах устройств в Linux, демонстрируются различные варианты взаимодействия с модулями Linux.

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

Читайте также:  Mscvp100 dll для windows 10

Глобальные переменные и функции

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

Простым примером может стать драйвер, переключающийся между несколькими файлами, с функцией (функциями), находящейся в одном файле и вызываемой из другого файла. Сейчас для того, чтобы даже в такой ситуации избежать какой-нибудь коллизии в пространстве имен ядра, каждый модуль должен находиться в своем собственном пространстве имен. И мы знаем, что одновременно нельзя загружать два модуля с одним и тем же именем. Таким образом, можно достигнуть полного отсутствия коллизий. Но это также значит, что ничего, что есть внутри модуля, нельзя даже в том случае, если мы этого захотим, использовать в ядре в качестве глобальных переменных. И именно для таких случаев в заголовочном файле
определяются следующие макросы:

  • EXPORT_SYMBOL(sym)
  • EXPORT_SYMBOL_GPL(sym)
  • EXPORT_SYMBOL_GPL_FUTURE(sym)

Каждый из них экспортирует символьную переменную в качестве своих параметров, дополнительно помещая ее по умолчанию в одну из собственных секций _gpl или _gpl_future . Таким образом, для конкретной символьной переменной следует использовать только один из этих макросов, причем независимо от того, будет ли эта символьная переменная именем переменной или именем функции. Это продемонстрировано в следующем фрагменте кода ( our_glob_syms.c ):

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

На рис.1 показан фрагмент директория /proc/kallsyms до и после загрузки модуля our_glob_syms.ko , который компилировался с использованием обычного файла Makefile , предназначенного для драйверов.

Рис.1: Наш модуль глобальных символьных переменных

В следующем коде показан вспомогательный заголовочный файл ( our_glob_syms.h ), который должен добавляться в модули, использующие экспортные символьные переменные ool_cl и get_cool_cl :

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

Обратите внимание, что в заголовочном файле
, используемом в приводимых выше примерах, добавляются объявления и определения для разнообразных классов, о чем уже ранее упоминалось в более раннем обсуждении символьных драйверов

Параметры модуля

Зная о том, как аргументы командной строки передаются в приложение, было бы естественно спросить, можно ли что-то подобное сделать с модулем — и ответом будет: да, это возможно. Параметры можно передать в модуль при его загрузке, например, с помощью команды insmod . Интересно, что в отличие от аргументов командной строки, указываемых для приложения, параметры для модуля можно будет впоследствии изменить, если получить доступ к sysfs .

Параметры модуля настаиваются с помощью следующего макроса (определяется в

  • , который находится в
  • ):

    Здесь name является именем параметра, type является типом параметра, а с помощью perm задаются права доступа к файлу sysfs , соответствующему данному параметру. Поддерживаются значения следующих типов: byte, short, ushort, int, uint, long, ulong, charp (символьный указатель), bool и invbool (инвертированный Boolean).

    В следующем коде модуля ( module_param.c ) продемонстрировано использование в модуле параметра:

    Читайте также:  Asus n50v драйвера windows 10

    Рис.2: Эксперименты с параметром модуля

    Рис.3: Эксперименты с параметром модуля (в роли пользователя root)

    Обратите внимание, что прежде, чем настраивать параметр, необходимо определить переменную с тем же самым именем и совместимым типом данных. Затем выполните следующие действия и поэкспериментируйте так, как это показано на рис.2 и 3:

    • Соберите драйвер (файл module_param.ko ) — используйте для этого обычный файл Makefile , предназначенный для драйверов.
    • Загрузите драйвер с помощью команды insmod (с параметрами и без параметров)
    • Проведете эксперименты с соответствующими записями в /sys
    • И, наконец, выгрузите драйвер с помощью команды rmmod

    Обратите внимание на следующее:

    • Когда команда insmod запускается без параметров, то значением, используемым по умолчанию, станет начальное значение (равное 3) переменной cfg_value .
    • Для файла параметров cfg_value модуля module_param , находящегося в /sys/module/ , установлены права доступа 0764, что означает rwx — для пользователя, rw- — для группы и r— — для всех остальных.
    • Посмотрите результат работы команды dmesg/tail для всех данных, выдаваемых операцией печати printk в каждой из команд insmod и rmmod
    • Попытайтесь в роли обычного пользователя (не имеющего прав доступа root) сделать запись в файл /sys/module/module_param/parameters/cfg_value

    Подведем итог

    Благодаря последним полученным знаниям у наших двух героев достаточно хорошее представление о драйверах в Linux, и они готовы начать работу над завершающим проектом семестра. Есть предположения о теме, которой будет посвящен их проект? Подсказка: они выбрали одну из самых сложных тем, связанную с драйверами в Linux. Давайте в следующей статье посмотрим, как они с ней справятся.

    Источник

    Как выгрузить нерабочий модуль после insmod

    Получилось так, что добавленный мною код в модуль упал (код в module_init). Я получил stacktrace, но модуль висит в lsmod :

    Почему он висит в используемых и его нельзя удалить?

    Написано ж — Used by 1. Пока модуль используется, его невозможно удалить. Вопрос в том, кем он используется.

    Ну эт понятно. Так и делаю =)

    Есть rmmod -f, но после Oops машину лучше перезагрузить. Альтернативно, сделать специальный ioctl для обнуления счетчика.

    Добавьте module_init корректную обработку ошибки с освобождением всех ранее захваченных ресурсов.

    если модуль ни кем не юзается, то это говорит об ошибке в самом модуле
    и подчищать за собой лучше где-тить в module_exit();

    пардон, бред написал — опять залазил в ЛОР после бессонной ночи и не прочитал нормально %)

    в общем-то к постам от tailgunner и Andrey_Utkin могу только посоветовать использовать printk, чтоб локализовать ошибку %)

    Ты qemu не юзаешь, что ли?

    Действительно, почему бы не поэксперементировать с написанием модулей (которые, между прочим, в одном адресном пространстве с ядром и могут его порушить) на живой железке?

    А чё с ней будет-то, если эта железка специально для разработки.

    Да, порушенное ядро на живой железке(разработчика) это просто ужасно.

    Ну, например, притянутый за уши вариант — ваш модуль параллельно порушит какой-нибудь модуль для jfss2 (например) и вы попортите содержимое нанды, или модул для ext2 и вы попортите содержимое SDкарты, с которой железка грузится — придется заново заливать всю файловую систему.

    Я уже молчу, что на криво сделаной железке может возможность спалить что-нибудь программно (например если у вас есть какой-то внешний контроллер питания, которым ваш SoC управляет)

    Сильное заявление. Проверять я его конечно не буду. Слышал ещё, что NAND разрушается из-за свободных ионов, которые прилетают к нам из космоса..

    Я говорю не про физическую порчу нанды или SD карты, а про порчу содержимого, в случае если порушите драйвер файловой системы.

    А на счет порчи железа я не шучу. Погуглите про сжигание звуковухи на некоторых асусовских ноутах, при выкручивании громкости звука больше 100%. (ну точнее там сгорала не сама звуковуха, а ОУ на выходе)

    Источник

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