6.5 Драйвер API ядра |
Предыдущая Содержание Следующая |
Одной из основных сложных задач, с которой сталкивается разработчик при переносе приложений на встроенный Linux, является наличие в Linux разных режимов программирования: пространство ядра/пространство пользователя. В Linux, поскольку адресное пространство ядра является защищённым, приложение не может непосредственно вызвать какую-любо функцию ядра или получить доступ к какой-любо структуре данных ядра. Все обращения к объектам ядра должны происходить через чётко определённые интерфейсы, называемые системными вызовами. Защищённое адресное пространство ядра значительно увеличивает усилия по переносу приложений на встроенный Linux из традиционной ОС реального времени, которая имеет плоскую модель памяти. Давайте рассмотрим пример, чтобы понять трудности, с которыми сталкивается разработчик при переносе приложений с ОС реального времени на Linux. Изделие, работающее под управлением RTOS имеет RTC (Real Time Counter, счётчик реального времени). Для установки RTC доступна функция rtc_set. rtc_set изменяет регистры RTC для установки нового значения.
rtc_set(new_time){ Получаем из new_time год, месяц, день, час, минуту и секунду; программируем регистры RTC новыми значениями; }
Также доступна функция rtc_get_from_user, которая принимает от пользователя новое значение RTC и вызывает функцию rtc_set.
rtc_get_from_user(){ читаем значение времени, введённое пользователем в new_time; вызываем rtc_set(new_time); }
При переносе вышеописанного приложения разработчик под Linux сталкивается с дилеммой. Функция rtc_set непосредственно изменяет регистров RTC, поэтому она должна перейти в ядро. С другой стороны, функция rtc_get_from_user читает пользовательский ввод, поэтому она должна перейти в пользовательское пространство. В Linux функция rtc_get_from_user не сможет вызывать функцию rtc_set, так как последняя является функцией ядра. При переносе такого приложения на Linux возможны следующие решения.
▪Перенести всё в ядро: Это решение сможет работать, но это уничтожает преимущества перехода на Linux, защиту памяти. ▪Написать новые системные вызовы: при таком подходе для каждой функции, перенесённой в ядро, для вызова этой функции в пространстве пользователя мог бы быть предоставлен интерфейс системного вызова. Недостатки такого подхода:
В этом разделе мы обсудим эффективный метод переноса таких приложения на Linux. Мы называем это драйвером API ядра (kapi, kernel API driver). При таком подходе для каждой функции ядра, которая должна быть экспортирована в пространство пользователя, в пользовательском пространстве пишется функция-заглушка. При вызове этой заглушки происходит вызов драйвера API ядра, который затем вызывает требуемую функцию в ядре. Драйвер API ядра (или драйвер kapi) реализован в виде символьного драйвера /dev/kapi. Он обеспечивает интерфейс ioctl для всех функций, которые должны быть экспортированы в пространство пользователя. Рисунок 6.7 поясняет случай нашего примера RTC.
Рисунок 6.7 Экспорт функций ядра с помощью kapi.
Таким образом, чтобы перенести вышеописанное приложения для работы с RTC на Linux, разработчик должен:
▪Обеспечить в kapi драйвере ioctl RTC_SET: реализация этого ioctl в драйвере вызывает функцию ядра rtc_set с необходимыми аргументами. ▪Написать в пространстве пользователя заглушку rtc_set: заглушка вызывает ioctl RTC_SET для /dev/kapi. Она также передаёт необходимые параметры для этого ioctl.
Таким образом, используя драйвер kapi, функция пользовательского пространства rtc_get_from_user вызывает функцию-заглушку rtc_set, как будто это вызов реальной функции ядра. В этом разделе мы обсудим:
▪Шаги для написания функций-заглушек пользовательского пространства ▪Реализацию драйвера kapi ▪Как добавить свою функцию ioctl в драйвер kapi
Примером драйвера kapi является модуль ядра, написанный для ядра 2.6. Исходные тексты находятся в следующих файлах.
▪kapi-user.c: содержит пример заглушки в пользовательском пространстве ▪kapi-kernel.c: содержит исходный код драйвера kapi ▪kapi.h: заголовочный файл, который должен быть включён в kapi-user.c и kapi-kernel.c
В этом примере мы экспортируем в пространство пользователя функцию ядра my_kernel_func. Прототип функции:
int my_kernel_func(int val, char* in_str, char *out_str);
Эта функция принимает три аргумента, один целочисленный и два указателя на char. Первый указатель на char является входным для функции (copy-in), а второй указатель используется для вывода данных из функции (copy-out). Возвращаемым значением функции является целое число.
|
Предыдущая Содержание Следующая |