Содержание


Разработка модулей ядра Linux

Часть 17. Сборка модулей. Дополнительные возможности

Comments

Серия контента:

Этот контент является частью # из серии # статей: Разработка модулей ядра Linux

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Разработка модулей ядра Linux

Следите за выходом новых статей этой серии.

В этой статье продолжается обсуждение процесса сборки и установки модулей ядра.

Сборка модуля из нескольких объектных файлов

Попробуем собрать модуль из основного файла mod.c и 3-х отдельно компилируемых файлов mfA.c, mfB.c и mfC.c, содержащих по одной отдельной функции, экспортируемой модулем. Полностью исходный код примеров представлен в архиве mobj.tgz в разделе «Материалы для скачивания».

Листинг 1. Главный файл: mod.c
#include <linux/module.h>
#include "mf.h"

static int __init init_driver( void ) { return 0; }
static void __exit cleanup_driver( void ) {}
module_init( init_driver );
module_exit( cleanup_driver );
Листинг 2. Один из трех подключаемых файлов: mfA.c
extern char *mod_func_1( void ) { 
   static char *ststr = __FUNCTION__ ; 
   return ststr; 
}; 
EXPORT_SYMBOL( mod_func_1 );

Файлы mfB.c и mfC.c полностью совпадают с mfA.c, только имена экспортируемых функций в них заменены, соответственно, на mod_func_2() и mod_func_3().

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

Листинг 3. Вызывающий модуль mcall.c
static int __init init_driver( void ) { 
   printk( "call: %s[%p]\n", mod_func_1(), (void*)mod_func_1 ); 
   printk( "call: %s[%p]\n", mod_func_2(), (void*)mod_func_2 ); 
   printk( "call: %s[%p]\n", mod_func_3(), (void*)mod_func_3 ); 
   return -1; 
} 
module_init( init_driver );

В данном проекте наибольший интерес представляет файл сборки Makefile.

Листинг 4. Makefile для сборки одного из 2-х модулей (mobj) из 4-х объектных файлов
...
EXTRA_CFLAGS += -O3 -std=gnu89 --no-warnings
OBJS = mod.o mf1.o mf2.o mf3.o
TARGET = mobj
TARGET2 = mcall

obj-m      := $(TARGET).o $(TARGET2).o
$(TARGET)-objs := $(OBJS)

all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules

$(TARGET).o: $(OBJS)
        $(LD) -r -o $@ $(OBJS)
...

Теперь можно изучить результат выполнения данной сборки:

$ nm mobj.ko | grep T 
00000000 T cleanup_module 
00000000 T init_module 
00000000 T mod_func_1 
00000010 T mod_func_2 
00000020 T mod_func_3

Это именно то, что требовалось: 4 отдельных объектных файла были собраны в единый файл модуля.

Рекурсивная сборка

Это вопрос, хотя и несвязанный непосредственно со сборкой модулей, очень часто возникает в проектах, работающих с модулями: как последовательно выполнить сборку одной и той же цели во всех включаемых каталогах? Хотелось бы иметь возможность собрать (или очистить по make clean) всю иерархию каталогов проектов, не уточняя конкретные названия этих каталогов. Для такой цели можно использовать Makefile, приведенный в листинге 5.

Листинг 5. Сценарий рекурсивной сборки
...
SUBDIRS = $(shell ls -l | awk '/^d/ { print $$9 }')
all:
        @list='$(SUBDIRS)'; for subdir in $$list; do \
          echo "= making all in $$subdir ="; \
          (cd $$subdir && make && cd ../) \
        done;
install:
        @list='$(SUBDIRS)'; for subdir in $$list; do \
          echo "= making install in $$subdir ="; \
          (cd $$subdir; make install; cd ../) \
        done
uninstall:
        @list='$(SUBDIRS)'; for subdir in $$list; do \
          echo "= making uninstall in $$subdir ="; \
          (cd $$subdir; make uninstall; cd ../) \
        done
clean:
        @list='$(SUBDIRS)'; for subdir in $$list; do \
          echo "= making clean in $$subdir ="; \
          (cd $$subdir && make clean && cd ../) \
        done;

В листинге 5 особый интерес представляет строка, формирующая в переменной SUBDIRS список подкаталогов текущего каталога, для каждого из которых потом последовательно выполняется make для той же цели, что и исходный вызов. Таким же образом обходятся каталоги в иерархии более одного уровня вложенности.

Инсталляция модуля

Выше, в сценарии рекурсивной сборки, в качестве целей сборки были названы install и uninstall. Инсталляция модуля должна состоять из следующих шагов:

  1. скопировать собранный файл модуля (*.ko) в его местоположение в иерархии модулей, часто это, например, каталог /lib/modules/`uname -r`/misc;
  2. обновить информацию о зависимостях модулей (в связи с добавлением нового), что делается вызовом утилиты depmod.

Но если в Makefile создаётся цель для инсталляции модуля, то обязательно должна быть создана и обратная цель для деинсталляции: лучше не иметь автоматической возможности инсталлировать модуль (и делать это вручную), чем автоматизировать процесс установки, не имея возможности удалить установленный модуль! Для деинсталляции модуля требуется:

  1. удалить файл модуля (*.ko) из его местоположения в иерархии модулей;
  2. обновить информацию о зависимостях модулей вызовом depmod.

В самом же файле Makefile эти цели могут быть записаны, как показано в листинге 6:

Листинг 6. Цели сборки: инсталляция и деинсталляция
CURRENT = $(shell uname -r) 
KDIR = /lib/modules/$(CURRENT)/build 
PWD = $(shell pwd) 
DEST = /lib/modules/$(CURRENT)/misc 
TARGET = ... 

obj-m   := $(TARGET).o
default: 
        $(MAKE) -C $(KDIR) M=$(PWD) modules 

install: 
        cp -v $(TARGET).ko $(DEST) 
        /sbin/depmod -v | grep $(TARGET) 
        /sbin/insmod $(TARGET).ko 
        /sbin/lsmod | grep $(TARGET) 

uninstall: 
        /sbin/rmmod $(TARGET) 
        rm -v $(DEST)/$(TARGET).ko 
        /sbin/depmod

Эти цели повторяются из проекта в проект, так что в большинстве демонстрируемых примеров мы уже не будем к ним возвращаться.

Требуются ли собирать ядро?

Перед тем, как приступить к созданию модуля, программист (хотя бы для себя) должен ответить на следующие вопросы:

  • требуется ли собирать ядро;
  • следует ли освоить технику сборки ядра из исходного кода;
  • следует ли вообще уметь устанавливать ядро из исходного кода.

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

Обычно заголовочные файлы, необходимые для разработки модулей, уже присутствуют в системе (это определяется предпочтениями авторов используемого Linux-дистрибутива). Но может оказаться, что исходный код ядра отсутствует, и в этом случае символьная ссылка /lib/modules/`uname -r`/build окажется неразрешённой, а каталог с исходным кодом ядра пустой.

$ ls /usr/src/kernels 
$

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

$ uname -r 
2.6.35.14-95.fc14.x86_64 
$ yum list all kernel-*
kernel-devel.x86_64      2.6.35.14-95.fc14           @updates 
kernel-headers.x86_64    2.6.35.14-95.fc14           @updates

В этом списке нас интересуют значения kernel-headers и kernel-devel. Здесь же показано точное требуемое соответствие версий пакетов той версии ядра, в которой ведётся разработка и компиляция модулей. Если пакета нет, то его необходимо установить (как показано ниже на примере одного из пакетов):

Листинг 7. Установка обязательных пакетов ядра
# yum install kernel-devel.x86_64 
... 
Установка: 
 kernel-devel         x86_64      2.6.35.13-95.fc14      updates      6.6 M 
...
Объем загрузки: 6.6 M 
Будет установлено: 24 M 
... 
Установлено: 
  kernel-devel.x86_64 0:2.6.35.13-95.fc14

В листинге 7 показан пример установки для 64-разрядной системы. Точно определить пакеты, требующиеся для конкретной системы, можно с помощью регулярных выражений для шаблонов имён. В любом случае, необходимо убедиться, что в системе установлены заголовочные файлы, соответствующие версии исполняющейся системы.

Листинг 8. Каталог разработки
$ ls /lib/modules/`uname -r`/build 
arch   drivers  include kernel   mm             samples  sound      usr 
...

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

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

Заключение

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

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


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=819816
ArticleTitle=Разработка модулей ядра Linux: Часть 17. Сборка модулей. Дополнительные возможности
publish-date=06052012