Введение
После рассмотрения общих вопросов, связанных с инсталляцией и де-инсталляцией модулей ядра, следует рассмотреть более сложные вопросы, связанные с данным аспектом разработки модулей ядра. В данной статье будет рассматриваться использование параметров для передачи значений в модуль ядра в ходе его загрузки.
Параметры, передаваемые модулю при загрузке
При загрузке модуля ему могут быть переданы значения параметров. Здесь наблюдается полная аналогия (по смыслу, но не по формату) с передачей параметров пользовательскому процессу из командной строки через параметры: argс
и массив argv[]
. Такую передачу параметров модулю при его загрузке можно проследить в ближайшем рассматриваемом драйвере символьного устройства (архив cdev.tgz с этим примером будет рассмотрен в одной из следующих статей). Более того, для этого модуля, если значение параметра не указано явно, устанавливается значение по умолчанию (динамически определяемый системой старший номер устройства), а если параметр указан, то принудительно устанавливается заданное значение, даже если оно и недопустимо с точки зрения системы. Этот фрагмент кода выглядит, как показано ниже:
static int major = 0;
module_param( major, int, S_IRUGO );
|
В нем определяется переменная-параметр (с именем major
), и далее это же имя указывается в макросе module_param()
. Подобный макрос должен быть записан для каждого предусмотренного параметра и должен последовательно определять:
- имя (параметра и переменной);
- тип значения этого параметра;
- права доступа (к параметру, отображаемому как путевое имя в системе /sys).
Значения параметров также могут быть установлены во время загрузки модуля через insmod
или modprobe
; последняя команда также может считать значение параметра из своего файла конфигурации (/etc/modprobe.conf), используемого для загрузки модулей.
Обработка входных параметров для модуля обеспечивается макросами (описанными в <linux/moduleparam.h>). Ниже перечислены основные макросы (также там упомянут ряд мало употребляемых макросов). Два макроса для большей наглядности приводятся с полным определением через другие макросы:
module_param_named( name, value, type, perm )
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
module_param_string( name, string, len, perm )
module_param_array_named( name, array, type, nump, perm )
#define module_param_array( name, type, nump, perm ) \
module_param_array_named( name, name, type, nump, perm )
|
Из этого подмножества чаще всего употребляются два макроса: module_param()
и module_param_array()
(детально изучить, как они работают, можно будет с помощью примера, который будет рассматриваться ниже).
Примечание: В последнем параметре perm
указываются права доступа (например, S_IRUGO / S_IWUSR), относящиеся к имени параметра, отображаемому в подсистеме /sys, если же нас не интересует имя параметра, отображаемое в /sys, то правильным значением для параметра perm
будет 0.
Для параметров модуля в макросе module_param()
могут использоваться следующие типы:
bool
, invbool
- булева величина (true или false) - связанная переменная должна быть типа int
(тип invbool
инвертирует значение, так что значение true приходит как false и наоборот).
charp
- значение указателя на char
- выделяется память для строки, заданной пользователем (не нужно предварительно выделять место для строки), и указатель устанавливается соответствующим образом.
int
, long
, short
, uint
, ulong
, ushort
- базовые целые величины разной размерности; версии, начинающиеся с u, являются беззнаковыми величинами.
В качестве входного параметра может быть определён и массив выше перечисленных типов (макрос module_param_array()
).
В листинге 1 показан пример, демонстрирующий большинство приёмов, используемых для загрузки параметров модуля (файл mod_params.c из архива parms.tgz в разделе "Материалы для скачивания").
Листинг 1. Модуль, использующий входные параметры
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
static int iparam = 0;
module_param( iparam, int, 0 );
static int k = 0; // имена параметра и переменной различаются
module_param_named( nparam, k, int, 0 );
static char* sparam;
module_param( sparam, charp, 0 );
#define FIXLEN 5
static char s[ FIXLEN ] = ""; // имена параметра и переменной различаются
module_param_string( cparam, s, sizeof( s ), 0 );
static int aparam[] = { 0, 0, 0, 0, 0 };
static int arnum = sizeof( aparam ) / sizeof( aparam[ 0 ] );
module_param_array( aparam, int, &arnum, S_IRUGO / S_IWUSR );
static int __init mod_init( void ) {
int j;
char msg[ 40 ] = "";
printk( "========================================\n" );
printk( "iparam = %d\n", iparam );
printk( "nparam = %d\n", k );
printk( "sparam = %s\n", sparam );
printk( "cparam = %s {%d}\n", s, strlen( s ) );
sprintf( msg, "aparam [ %d ] = ", arnum );
for( j = 0; j < arnum; j++ )
sprintf( msg + strlen( msg ), " %d ", aparam[ j ] );
printk( "%s\n========================================\n", msg );
return -10000;
}
module_init( mod_init );
|
В коде этого модуля есть два момента, которые могут показаться Си-программисту непривычными: отсутствие резервирования памяти для символьного параметра sparam
и динамический размер (после загрузки модуля) параметра-массива aparam
. Но оба эти вопроса объясняются ниже.
Выполним следующие команды, чтобы сравнить, как выполняется загрузка модуля с параметрами по умолчанию, а затем - с переопределением значений всех параметров:
Листинг 2. Примеры загрузки модуля с указанием параметров и без указания параметров.
$ sudo /sbin/insmod ./mod_params.ko
insmod: error inserting './mod_params.ko': -1 Operation not permitted
$ dmesg / tail -n7
========================================
iparam = 0
nparam = 0
sparam =
cparam = {0}
aparam [ 5 ] = 0 0 0 0 0
========================================
$ sudo /sbin/insmod ./mod_params.ko iparam=3 nparam=4 sparam=str1 \
cparam=str2 aparam=5,4,3
insmod: error inserting './mod_params.ko': -1 Operation not permitted
$ dmesg / tail -n7
========================================
iparam = 3
nparam = 4
sparam = str1
cparam = str2 {4}
aparam [ 3 ] = 5 4 3
========================================
|
Команда insmod
жестко контролирует параметры, передаваемые при загрузке, и их значения (хотя, естественно, всё проконтролировать абсолютно невозможно), так как если модуль, загруженный с ошибочными значениями параметров, станет составной частью ядра, возникнет угроза целостности системы. Если хотя бы один из параметров будет признан некорректным, загрузка модуля производиться не будет. Ниже показано, как контролируется процесс загрузки модуля с указанием параметров:
$ sudo /sbin/insmod ./mod_params.ko aparam=5,4,3,2,1,0
insmod: error inserting './mod_params.ko': -1 Invalid parameters
$ dmesg / tail -n2
aparam: can only take 5 arguments
mod_params: `5' invalid for parameter `aparam'
|
Как видно, имела место попытка поместить в массив aparam
большее число элементов, чем в нем было изначально зарезервировано (5).
$ sudo /sbin/insmod ./mod_params.ko zparam=3
insmod: error inserting './mod_params.ko': -1 Unknown symbol in module
$ dmesg / tail -n1
mod_params: Unknown parameter `zparam'
|
Была попытка передать параметр, не определённый в модуле.
$ sudo /sbin/insmod ./mod_params.ko iparam=qwerty
insmod: error inserting './mod_params.ko': -1 Invalid parameters
$ dmesg / tail -n1
mod_params: `qwerty' invalid for parameter `iparam'
|
Была попытка присвоения не числового значения параметру числового типа.
$ sudo /sbin/insmod ./mod_params.ko cparam=123456789
insmod: error inserting './mod_params.ko': -1 No space left on device
$ dmesg / tail -n2
cparam: string doesn't fit in 4 chars.
mod_params: `123456789' too large for parameter `cparam'
|
Была превышена максимальная длина для строки, передаваемой копированием.
Хотя, надо отдать должное, в реализации подсистемы параметризации модулей, имеющейся в ядре, присутствуют и определенные странности:
$ sudo insmod mod_params.ko sparam='new string parameter'
insmod: error inserting 'mod_params.ko': -1 Unknown symbol in module
$ dmesg / tail -n20 / grep -v ^audit
mod_params: Unknown parameter `string'
|
Заключение
В ходе статьи были последовательно рассмотрены все аспекты использования параметров, передаваемых модулям в ходе загрузки. Из фундаментальных вопросов разработки модулей ядра нам осталось изучить только предназначение счетчика ссылок использования модуля и инструментарий, доступный разработчику модулей ядра, что и будет выполнено в следующих статьях.