- Источники информации
- Олег цилюрик программирование модулей ядра linux
- Динамические структуры и управление памятью
- Циклический двусвязный список
- Модуль использующий динамические структуры
- Сложно структурированные данные
- Обсуждение
- Олег цилюрик программирование модулей ядра linux
- О сборке модулей детальнее
- Параметры компиляции
- Как собрать одновременно несколько модулей?
- Как собрать модуль и использующие программы к нему?
- Пользовательские библиотеки
- Как собрать модуль из нескольких объектных файлов?
- Рекурсивная сборка
Источники информации
[1]. «The Linux Kernel Module Programming Guide», Peter Jay Salzman, Michael Burian, Ori Pomerantz, 2001. Перевод: Андрей Киселёв, «Руководство по программированию модулей ядра Linux», 2004. Прочитать можно здесь или здесь .
[2]. «Linux Device Drivers», by Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman, (3rd Edition), 2005, 2001, 1998 O’Reilly Media, Inc., ISBN: 0-596-00590-3. Перевод: «Драйверы Устройств Linux, Третья Редакция»:
http://dmilvdv.narod.ru/Translate/LDD3/index.html?linux_device_drivers.html
Перевод второго издания доступен здесь.
[3]. «Linux Kernel Development», Robert Love, (3rd Edition), 2010. Русское 2-е издание: Р. Лав, «Разработка ядра Linux», М.: «И.Д.Вильямс», 2006, стр. 448.
[4]. «Professional Linux Kernel Architecture (Wrox Programmer to Programmer)», by Wolfgang Mauerer, Wiley Publishing Inc., 2008, p.1335.
[5]. «Essential Linux Device Drivers», by Sreekrishnan Venkateswaran, Prentice Hall, 2008, p.714.
Сайт книги: http://elinuxdd.com
Архив кодов примеров: http://elinuxdd.com/
[6]. «Writing Linux Device Drivers», Jerry Cooperstein, 2009,
том 1: «A guide with exercises», стр. 372
том 2: «Lab Solutions», стр. 259
Авторский сайт: http://coopj.com/
Архив кодов примеров: http://coopj.com/LDD/
[7]. Клаудия Зальзберг Родригес, Гордон Фишер, Стивен Смолски, «Linux. Азбука ядра», Пер. с англ., М.: «Кудиц-образ», 2007, стр. 577.
[8]. А. Гриффитс, «GCC. Полное руководство. Platinum Edition», Пер. с англ., М.: «ДиаСофт», 2004, ISBN 966-7992-33-0, стр. 624.
[9]. Олег Цилюрик, Егор Горошко, «QNX/UNIX: анатомия параллелизма», СПб.: «Символ-Плюс», 2005, ISBN 5-93286-088-X, стр. 288. Книга по многим URL в Интернет представлена для скачивания, например, здесь: http://bookfi.org/
[10]. Бовет Д., Чезати М., «Ядро Linux, 3-е издание», Пер. с англ., СПб.: «БХВ-Петербург», 2007, ISBN 978-5-94157-957-0, стр. 1104. Книга может быть скачана: http://bookfi.org/book/485672
[11]. Крищенко В. А., Рязанова Н. Ю., «Основы программирования в ядре операционной системы GNU/Linux», сдано в издательство МГТУ в 2008 году.
Текст статьи: http://sevik.ru/syslinux/pdf/sys_linux.pdf
Примеры кода к статье: http://sevik.ru/syslinux/samples/syslinux_samples.tar.gz
[14]. Роб Кёртен, «Введение в QNX Neutrino. Руководство для разработчиков приложений реального времени», Пер. с англ., СПб.: BHV-СПб, 2011, ISBN 978-5-9775-0681-6, 368 стр. http://www.books.ru/shop/
[15]. Клаус Вейрле, Фронк Пэльке, Хартмут Риттер, Даниэль Мюллер, Марк Бехлер, «Linux: сетевая архитектура. Структура и реализация сетевых протоколов в ядре», Пер. с англ., М.: «КУДИЦ-ОБРАЗ», 2006, ISBN 5-9579-0094-X, стр. 656.
[16]. David Mosberger, Stephane Eranian, «IA-64 Linux Kernel», Hewlet-Packard Company, Prentice Hall PTR, 2002, стр. 522
[17]. Greg Kroab-Hartman, «Linux Kernel in a Nutshell», O’Reilly Vtdia, Inc., 2007, ISBN-10: 0-596-10079-5, стр. 184.
[18]. Rajaram Regupathy, «Bootstrap Yourself with Linux-USB Stack: Design, Develop, Debug, and Validate Embedded USB», Course Technology, a part of Cengage Lernining, 2012, ISBN-10: 1-4354-5786-2, стр. 302.
[19]. У. Р. Стивенс, «UNIX: взаимодействие процессов», СПб.: «Питер», 2003, ISBN 5-318-00534-9, стр. 576.
[20]. У. Ричард Стивенc, Стивен А. Раго, «UNIX. Профессиональное программирование», второе издание, СПб.: «Символ-Плюс», 2007, ISBN 5-93286-089-8, стр. 1040. Полный архив примеров кодов к этой книге может быть взят здесь: http://www.kohala.com/start/apue.linux.tar.Z
[21]. W.Richard Stevens’ Home Page (ресурс полного собрания книг и публикаций У. Р. Стивенса):
http://www.kohala.com/start/
[22]. Tigran Aivazian (tigran@veritas.com), «Внутреннее устройство Ядра Linux 2.4», 21 October 2001, Перевод: Андрей Киселев. Прочитать можно здесь или здесь .
[23]. «GNU Make. Программа управления компиляцией. GNU make Версия 3.79. Апрель 2000», авторы: Richard M. Stallman и Roland McGrath, перевод: Владимир Игнатов, 2000.
Найдете здесь и здесь .
[24] — «Отладчик GNU уровня исходного кода. Восьмая Редакция, для GDB версии 5.0. Март 2000», авторы: Ричард Столмен, Роланд Пеш, Стан Шебс и др.
Находится здесь или здесь .
Источник
Олег цилюрик программирование модулей ядра linux
Библиотека сайта rus-linux.net
На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux | ||
Назад | Внутренние механизмы ядра | Вперед |
Динамические структуры и управление памятью
Статический и динамический способ размещения структур данных имеют свои положительные и отрицательные стороны, главными из которых принято считать: а). статическая память: надёжность, живучесть и меньшая подверженность ошибкам; б). динамическая память: гибкость использования. Использование динамических структур всегда требует того или иного механизма управления памятью: создание и уничтожение терминальных элементов динамически увязываемых структур.
Циклический двусвязный список
Чтобы уменьшить количество дублирующегося кода, разработчики ядра создали (с ядра 2.6) стандартную реализацию кругового, двойного связного списка; всем другим нуждающимся в манипулировании списками (даже простейшими линейными односвязными, к примеру) рекомендуется разработчиками использовать это средство. Именно поэтому они заслуживают отдельного рассмотрения.
Примечание: При работе с интерфейсом связного списка всегда следует иметь в виду, что функции списка выполняют без блокировки. Если есть вероятность того, что драйвер может попытаться выполнить на одном списке конкурентные операции, вашей обязанностью является реализация схемы блокировки. Альтернативы (повреждённые структуры списка, потеря данных, паники ядра), как правило, трудно диагностировать.
Чтобы использовать механизм списка, ваш драйвер должен подключить файл
. Этот файл определяет простую структуру типа list_head :
Для использования в вашем коде средства списка Linux, необходимо лишь вставлять list_head внутри собственных структур, входящих в список, например:
Заголовки списков должны быть проинициализированы перед использованием с помощью макроса INIT_LIST_HEAD . Заголовок списка может быть объявлен и проинициализирован так (динамически):
Альтернативно, списки могут быть созданы и проинициализированы статически при компиляции:
Некоторые функции для работы со списками определены в
. Как мы видим, API работы с циклическим списком позволяет выразить любые операции с элементами списка, не вовлекая в операции манипулирование с внутренними полями связи списка; это очень ценно для сохранения целостности списков:
— добавляет новую запись new сразу же после головы списка, как правило, в начало списка. Таким образом, она может быть использована для создания стеков. Однако, следует отметить, что голова не должна быть номинальной головой списка; если вы передадите структуру list_head , которая окажется где-то в середине списка, новая запись пойдёт сразу после неё. Так как списки Linux являются круговыми, голова списка обычно не отличается от любой другой записи.
— добавляет элемент new перед головой данного списка — в конец списка, другими словами, list_add_tail() может, таким образом, быть использована для создания очередей первый вошёл — первый вышел.
— данная запись удаляется из списка. Если эта запись может быть когда-либо вставленной в другой список, вы должны использовать list_del_init() , которая инициализирует заново указатели связного списка.
— данная запись удаляется из своего текущего положения и перемещается (запись) в начало голову списка. Чтобы переместить запись в конец списка используется list_move_tail() .
— возвращает ненулевое значение, если данный список пуст.
— объединение двух списков вставкой нового списка list сразу после головы head .
Структуры list_head хороши для реализации линейных списков, но использующие его программы часто больше заинтересованы в некоторых более крупных структурах, которые увязываются в список как целое. Предусмотрен макрос list_entry , который связывает указатель структуры list_head обратно с указателем на структуру, которая его содержит. Он вызывается следующим образом:
— где ptr является указателем на используемую структуру list_head , type_of_struct является типом структуры, содержащей этот ptr , и field_name является именем поля списка в этой структуре.
Пример: в нашей ранее показанной структуре todo_struct поле списка называется просто list . Таким образом, мы бы хотели превратить запись в списке listptr в соответствующую структуру, то могли бы выразить это такой строкой:
Макрос list_entry() несколько необычен и требует некоторого времени, чтобы привыкнуть, но его не так сложно использовать.
Обход связных списков достаточно прост: надо только использовать указатели prev и next . В качестве примера предположим, что мы хотим сохранить список объектов todo_struct , отсортированный в порядке убывания. Функция добавления новой записи будет выглядеть примерно следующим образом:
Однако, как правило, лучше использовать один из набора предопределённых макросов для создание циклов, которые перебирают списки. Например, предыдущий цикл мог бы быть написать так:
Использование предусмотренных макросов помогает избежать простых ошибок программирования; разработчики этих макросов также приложили некоторые усилия, чтобы они выполнялись производительно. Существует несколько вариантов:
— макрос создаёт цикл for, который выполняется по одному разу с указателем cursor , присвоенным поочерёдно указателю на каждую последовательную позицию в списке (будьте осторожны с изменением списка при итерациях через него).
— эта версия выполняет итерации назад по списку.
— если операции в цикле могут удалить запись в списке, используйте эту версию: он просто сохраняет следующую запись в списке в next для продолжения цикла, поэтому не запутается, если запись, на которую указывает cursor , удаляется.
— эти макросы облегчают процесс просмотр списка, содержащего структуры данного типа type . Здесь cursor является указателем на содержащий структуру тип, и member является именем структуры list_head внутри содержащей структуры. С этими макросами нет необходимости помещать внутри цикла вызов макроса list_entry() .
В заголовках
определены ещё некоторые дополнительные декларации для описания динамических структур.
Модуль использующий динамические структуры
Ниже показан пример модуля ядра (архив list.tgz ), строящий, использующий и утилизирующий простейшую динамическую структуру в виде односвязного списка:
$ sudo /sbin/insmod ./mod_list.ko size=3
Сложно структурированные данные
Одной только ограниченной структуры данных struct list_head достаточно для построения динамических структур практически произвольной степени сложности, как, например, сбалансированные B-деревья, красно-чёрные списки и другие. Именно поэтому ядро 2.6 было полностью переписано в части используемых списковых структур на использование struct list_head . Вот каким простым образом может быть представлено с использованием этих структур бинарное дерево:
Не представляет слишком большого труда для такого представления создать собственный набор функций его создания-инициализации и манипуляций с узлами такого дерева.
Обсуждение
При обсуждении заголовков списков, было показано две (альтернативно, на выбор) возможности объявить и инициализировать такой заголовок списка:
— статический (переменная объявляется макросом и тут же делаются все необходимые для инициализации манипуляции):
— динамический (переменная сначала объявляется, как и любая переменная элементарного типа, например, целочисленного, а только потом инициализируется указанием её адреса):
Такая же дуальность (статика + динамика) возможностей будет наблюдаться далее много раз, например относительно всех примитивов синхронизации. Напрашивается вопрос: зачем такая избыточность возможностей и когда что применять? Дело в том, что очень часто (в большинстве случаев) такие переменные не фигурируют в коде автономно, а встраиваются в более сложные объемлющие структуры данных. Вот для таких встроенных объявлений и будет годиться только динамический способ инициализации. Единичные переменные проще создавать статически.
Источник
Олег цилюрик программирование модулей ядра linux
Библиотека сайта rus-linux.net
На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux | ||
Назад | Окружение и инструменты | Вперед |
О сборке модулей детальнее
Далее рассмотрим некоторые особенности процедуры сборки ( make ) проектов, и нарисуем несколько сценариев сборки ( Makefile ) для наиболее часто востребованных случаев, как например: сборка нескольких модулей в проекте, сборка модуля объединением нескольких файлов исходных кодов и подобные.
Параметры компиляции
Параметры компиляции модуля можно существенно менять, изменяя переменные, определённые в скрипте, осуществляющем сборку, например:
EXTRA_CFLAGS += -O3 -std=gnu89 —no-warnings
Таким же образом дополняем определения нужных нам препроцессорных переменных, специфических для сборки нашего модуля:
EXTRA_CFLAGS += -D EXPORT_SYMTAB -D DRV_DEBUG
Примечание: Откуда берутся переменные, не описанные по тексту файлу Makefile , как, например, EXTRA_CFLAGS ? Или откуда берутся правила сборки по умолчанию (как в примере примере использования ассемблерного кода разделом ранее)? И как посмотреть эти правила? Всё это вытекает из правил работы утилиты make : в конце книги отдельным приложением приведена краткая справка по этим вопросам, там же приведена ссылка на детальное описание утилиты make .
Как собрать одновременно несколько модулей?
В уже привычного нам вида Makefile может быть описано сборка скольки угодно одновременно собираемых модулей (архив export.tgz ):
Как собрать модуль и использующие программы к нему?
Часто нужно собрать модуль и одновременно некоторое число пользовательских программ, используемых одновременно с модулем (тесты, утилиты, . ). Зачастую модуль и пользовательские программы используют общие файлы определений (заголовочные файлы). Вот фрагмент подобного Makefile — в одном рабочем каталоге собирается модуль и все использующие его программы (архив ioctl.tgz ):
Интерес такой совместной сборки состоит в том, что и модуль и пользовательские процессы включают (директивой #include ) одни и те же общие и согласованные определения (пример, в том же архиве ioctl.tgz ):
Такие файлы содержат общие определения:
Некоторую дополнительную неприятность на этом пути составляет то, что при сборке приложений и модулей (использующих совместные определения) используются разные дефаултные каталоги поиска системных ( ) файлов определений: /usr/include для процессов, и /lib/modules/`uname -r`/build/include для модулей. Приемлемым решением будет включение в общий включаемый файл фрагмента подобного вида:
При всём подобии имён заголовочных файлов (иногда и полном совпадении написания:
), это будут включения заголовков из совсем разных наборов API (API разделяемых библиотек *.so для пространства пользователя, и API ядра — для модулей). Первый (пользовательский) из этих источников будет обновляться, например, при переустановке в системе новой версии компилятора GCC и комплекта соответствующих ему библиотек (в первую очередь libc.so ). Второй (ядерный) из этих источников будет обновляться, например, при обновлении сборки ядра (из репозитария дистрибутива), или при сборке и установке нового ядра из исходных кодов.
Пользовательские библиотеки
В дополнение к набору приложений, обсуждавшихся выше, удобно целый ряд совместно используемых этми приложениями функций собрать в виде единой библиотеки (так устраняется дублирование кода, упрощается внесение изменений, да и вообще улучшается структура проекта). Фрагмент Makefile из архива примеров time.tgz демонстрирует как это записать, не выписывая в явном виде все цели сборки (перечисленные списком в переменной OBJLIST ) для каждого такого объектного файла, включаемого в библиотеку (реализующего отдельную функцию библиотеки). В данном случае мы собираем статическую библиотеку libdiag.a :
Здесь собираются две цели prog и lib , объединённые в одну общую цель all . При желании, статическую библиотеку можно поменять на динамическую (разделяемую), что весьма часто востребовано в реальных крупных проектах. При этом в Makefile требуется внести всего незначительные изменения (все остальные файлы проекта остаются в неизменном виде):
Примечание: В случае построения разделяемой библиотеки необходимо, кроме того, обеспечить размещение вновь созданной библиотеки (в нашем примере это libdiag.so ) на путях, где он будет найдена динамическим загрузчиком, размещение «текущий каталог» для этого случая неприемлем: относительные путевые имена не применяются для поиска динамических библиотек. Решается эта задача: манипулированием с переменными окружения LD_LIBRARY_PATH и LD_RUN_PATH , или с файлом /etc/ld.so.cache (файл /etc/ld.so.conf и команда ldconfig ) . но это уже вопросы системного администрирования, далеко уводящие нас за рамки предмета рассмотрения.
Как собрать модуль из нескольких объектных файлов?
Соберём (архив mobj.tgz ) модуль из основного файла mod.c и 3-х отдельно транслируемых файлов mf1.c, mf2.c, mf3.c , содержащих по одной отдельной функции, экспортируемой модулем (весьма общий случай):
Файлы mf2.c, mf3.c полностью подобны mf1.c только имя экспортируемых функций заменены, соответственно, на mod_func_B( void ) и mod_func_C( void ) . Заголовочный файл, включаемый в текст модулей:
Ну и, наконец, в том же каталоге собран второй (тестовый) модуль, который импортирует и вызывает эти три функции как внешние экспортируемые ядром символы:
Самое интересное в этом проекте, это:
— привычные, из предыдущих примеров, всё те же определения переменных — опущены.
Теперь мы можем испытывать то, что мы получили:
$ nm mobj.ko | grep T
$ sudo insmod ./mobj.ko
$ lsmod | grep mobj
$ cat /proc/kallsyms | grep mod_func
$ sudo insmod ./mcall.ko
start module, export calls: mod_func_A + mod_func_B + mod_func_C
И в завершение проверим число ссылок модуля, и попытаемся модули выгрузить:
$ lsmod | grep mobj
mobj 1032 1 mcall
$ sudo rmmod mobj
ERROR: Module mobj is in use by mcall
$ sudo rmmod mcall
$ sudo rmmod mobj
Рекурсивная сборка
Это вопрос, не связанный непосредственно со сборкой модулей, но очень часто возникающий в проектах, оперирующих с модулями: выполнить сборку (одной и той же цели) во всех включаемых каталогах, например, на каких-то этапах развития, архив примеров к книге имел вид:
dev exec int80 netproto pci signal thread tools user_space
dma first_hello IRQ net parms proc sys time usb
Хотелось бы иметь возможность собирать (или очищать от мусора) всю эту иерархию каталогов-примеров. Для такой цели используем, как вариант, такой Makefile :
Интерес здесь представляет строка, формирующая в переменной SUBDIRS список подкаталогов текущего каталога, для каждого из которых потом последовательно выполняется make для той же цели, что и исходный вызов.
Источник