Перейти к тексту

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Профиль создается, когда вы в первый раз заходите в developerWorks. Выберите данные в своем профиле (имя, страна/регион, компания) которые будут общедоступными и будут отображаться, когда вы публикуете какую-либо информацию. Вы можете изменить данные вашего ИБМ аккаунта в любое время.

Вся введенная информация защищена.

  • Закрыть [x]

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

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Вся введенная информация защищена.

  • Закрыть [x]

Нестандартные сценарии использования модулей ядра: Часть 47. Подключаемые плагины

Олег Цилюрик, преподаватель тренингового отделения, Global Logic
Фото автора
Олег Иванович Цилюрик, много лет был разработчиком программного обеспечения в крупных центрах разработки: ВНИИ РТ, НПО "Дельта", КБ ПМ. Последние годы работал над проектами в области промышленной автоматики, IP телефонии и коммуникаций. Автор нескольких книг. Преподаватель тренингового отделения международной софтверной компании Global Logic.

Описание:  Статья является частью миницикла, посвященного использованию модулей ядра для решения различных прикладных задач. В статье рассматривается создание полноценной системы динамически подгружаемых плагинов, функционирующей в пространстве ядра.

Дата:  07.02.2013
Уровень сложности:  средний
Активность:  1306 просмотров
Комментарии:  


Введение

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


Подключаемые плагины

Хотя мы рассмотрим намеренно упрощенный пример работы с динамическими модулями, приведенный в архиве plugin.tgz в разделе "Материалы для скачивания", но впоследствии вы сможете использовать полученные знания и в рамках практических задач.

Добавим новый системный вызов __NR_str_trans, создав подключаемый файл syscall.h, содержимое которого приведено ниже:

1.	// номер нового системного вызова 
2.	#define __NR_str_trans 223 

Пользовательская программа, расположенная в файле syscall.с, передаёт корневому модулю (master.ko) строку, содержащую запись числового значения (в различных системах счисления: 8, 10, 16), и ожидает получить обратно вычисленное числовое значение.


Листинг 1. Вызывающий процесс.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include "syscall.h" 

static void do_own_call( char *str ) { 
   int n = syscall( __NR_str_trans, str, strlen( str ) ); 
   if( n >= 0 ) 
      printf( "syscall return %d\n", n ); 
   else 
      printf( "syscall error %d : %s\n", n, strerror( -n ) ); 
} 

int main( int argc, char *argv[] ) { 
   if( 1 == argc ) do_own_call( "9876" ); 
   else { 
      int i; 
      for( i = 1; i < argc; i++ ) 
         do_own_call( argv[ i ] ); 
   } 
   return EXIT_SUCCESS; 
};

Корневой модуль (master.ko) устанавливает в таблицу системных вызовов новый системный обработчик __NR_str_trans, но сам не занимается непосредственно переводом полученной строки в числовое значение в зависимости от её формата (восьмеричное, десятичное, или шестнадцатеричное значение), а выбирает и загружает для вычисления значения соответствующий модуль: oct.ko, dec.ko или hex.ko.

Все модули-плагины экспортируют одно и то же имя точки входа (str_translate), и поэтому не могут быть загружены одновременно. Корневой модуль, загрузив модуль-плагин, должен найти по адресу экспортируемое плагином имя str_translate() и передать ему строку для обработки, а полученный результат и будет итогом работы нового системного вызова. После обработки полученного запроса модуль-плагин должен быть обязательно выгружен во избежание конфликта экспортируемых имён при последующих запросах.

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

#include "../common.c" 

static char this_mod_file[ 40 ]; 

long str_translate( const char *buf ); 
EXPORT_SYMBOL( str_translate ); 

Файл common.c рассматривался в предыдущей статье, поэтому сейчас обсуждаться не будет. В листинге 2 приведен модуль для преобразования строки с восьмеричным значением в число, исходный код остальных модулей для преобразования десятичных и шестнадцатеричных значений можно найти в архиве plugin.tgz в разделе "Материалы для скачивания".


Листинг 2. Модуль для преобразования восьмеричной строки.

#include "slave.c" 

static const char dig[] = "01234567"; 

long str_translate( const char *buf ) { 
   long res = 0; 
   const char *p = buf; 
   printk( "+ %s : запрос : %s\n", this_mod_file, buf ); 
   while( *p != '\0' ) { 
      char *s = strchr( dig, *p ); 
      if( s == NULL ) return -EINVAL; 
      res = res * 8 + ( s - dig ); 
      p++; 
   } 
   return res; 
}; 

static int __init mod_init( void ) { 
   set_mod_name( this_mod_file, __FILE__ ); 
   printk( "+ модуль %s загружен\n", this_mod_file ); 
   return 0; 
} 

static void __exit mod_exit( void ) { 
   printk( "+ модуль %s выгружен\n", this_mod_file ); 
} 

Такая однотипность помогает отследить суть происходящего. К данному моменту мы получили три идентичных модуля: dec.ko, hex.ko, oct.ko.

$ ls -l *.ko 
-rw-rw-r-- 1 olej olej  95223 Фев 12 15:13 dec.ko 
-rw-rw-r-- 1 olej olej  95553 Фев 12 15:13 hex.ko 
-rw-rw-r-- 1 olej olej 126348 Фев 12 15:13 master.ko 
-rw-rw-r-- 1 olej olej  95223 Фев 12 15:13 oct.ko 

Каждый из этих трёх модулей экспортирует одно и то же имя точки входа str_translate(). Поэтому любые два из этих модулей не могут быть загружены одновременно, в чём легко убедиться:

$ sudo insmod hex.ko 
$ dmesg | tail -n30 | grep + 
+ модуль hex.ko загружен 
$ cat /proc/kallsyms | grep T | grep translate 
c0579604 T isofs_name_translate 
c063f888 T set_translate 
c063f8a4 T inverse_translate 
f8ab2000 T str_translate	[hex] 
$ sudo insmod oct.ko 
insmod: error inserting 'oct.ko': -1 Invalid module format 
$ dmesg | tail -n30 | grep oct 
oct: exports duplicate symbol str_translate (owned by hex) 

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

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


Листинг 3. Вызывающий модуль (файл master.c)

#include <linux/fs.h> 
#include <linux/vmalloc.h> 
#include "syscall.h" 
#include "../common.c" 
#include "../find.c" 
#include "CR0.c" 

static char this_mod_file[ 40 ];      // имя файла master-модуля 

static void **taddr,                  // адрес таблицы sys_call_table 
            *old_sys_addr;            // адрес старого обработчика (sys_ni_syscall)  

asmlinkage long (*sys_init_module)    // системный вызов sys_init_module() 
                ( void __user *umod, unsigned long len, const char __user *uargs ); 
asmlinkage long (*sys_delete_module) // системный вызов sys_delete_module() 
                ( const char __user *name, unsigned int flags ); 

static long load_slave( const char* fname ) { 
   long res = 0; 
   struct file *f; 
   long len; 
   void *buff; 
   size_t n; 
   f = filp_open( fname, O_RDONLY, 0 ); 
   if( IS_ERR( f ) ) { 
      printk( "+ ошибка открытия файла %s\n", fname ); 
      return -ENOENT; 
   } 
   len = vfs_llseek( f, 0L, 2 ); // 2 - means SEEK_END
   if( len <= 0 ) { 
      printk( "+ ошибка lseek\n" ); 
      return -EINVAL; 
   } 
   printk( "+ длина файла модуля = %d байт\n", (int)len ); 
   if( NULL == ( buff = vmalloc( len ) ) ) { 
      filp_close( f, NULL ); 
      return -ENOMEM; 
   }; 
   printk( "+ адрес буфера чтения = %p\n", buff ); 
   vfs_llseek( f, 0L, 0 );       // 0 - means SEEK_SET 
   n = kernel_read( f, 0, buff, len ); 
   printk( "+ считано из файла %s %d байт\n", fname, n ); 
   if( n != len ) { 
      printk( "+ ошибка чтения\n" ); 
      vfree( buff ); 
      filp_close( f, NULL ); 
      return -EIO; 
   } 
   filp_close( f, NULL ); 
   { mm_segment_t fs = get_fs(); 
     set_fs( get_ds() ); 
     res = sys_init_module( buff, len, "" ); 
     set_fs( fs ); 
   } 
   vfree( buff ); 
   if( res < 0 ) 
      printk( "+ ошибка загрузки модуля %s : %ld\n", fname, res ); 
   return res; 
} 

static long unload_slave( const char* fname ) { 
   long res = 0; 
   mm_segment_t fs = get_fs(); 
   set_fs( get_ds() ); 
   if( strrchr( fname, '.' ) != NULL ) 
      *strrchr( fname, '.' ) = '\0'; 
   res = sys_delete_module( fname, 0 ); 
   set_fs( fs ); 
   if( res < 0 ) 
      printk( "+ ошибка выгрузки модуля %s\n", fname ); 
   return res; 
} 

// новый системный вызов 
asmlinkage long sys_str_translate( const char __user *buf, size_t count ) { 
   static const char* slave_name[] =              // имена файлов slave-модулей 
                      { "dec.ko", "oct.ko", "hex.ko" }; 
   static char buf_msg[ 80 ], mod_file[ 40 ], *par1; 
   int res = copy_from_user( buf_msg, (void*)buf, count ), ind, trs; 
   buf_msg[ count ] = '\0'; 
   long (*loaded_str_translate)( const char *buf ); 
   printk( "+ системный запрос %d байт: %s\n", count, buf_msg ); 
   if( buf_msg[ 0 ] == '0' ) { 
      if( buf_msg[ 1 ] == 'x' ) ind = 2; // шестнадцатеричный случай
      else ind = 1;                      // восьмеричный случай
   } 
   else if( strchr( "123456789", buf_msg[ 0 ] ) != 0 ) 
      ind = 0;                           //десятичный случай
   else return -EINVAL; 
   strcpy( mod_file, slave_name[ ind ] ); 
   par1 = buf_msg + ind; 
   if( ( res = load_slave( mod_file ) ) < 0 ) return res; 
   if( ( loaded_str_translate = find_sym( "str_translate" ) ) != NULL ) 
      printk( "+ адрес обработчика = %p\n", loaded_str_translate ); 
   else { 
      printk( "+ str_translate не найден\n" ); 
      return -EINVAL; 
   } 
   if( ( trs = loaded_str_translate( par1 ) ) < 0 ) 
      return trs; 
   printk( "+ вычислено значение %d\n", trs ); 
   res = unload_slave( mod_file ); 
   if( res < 0 ) return res; 
   else return trs; 
}; 

static int __init mod_init( void ) { 
   long res = 0; 
   void *waddr; 
   set_mod_name( this_mod_file, __FILE__ ); 
   if( ( taddr = find_sym( "sys_call_table" ) ) != NULL ) 
      printk( "+ адрес sys_call_table = %p\n", taddr ); 
   else { 
      printk( "+ sys_call_table не найден\n" ); 
      return -EINVAL; 
   } 
   old_sys_addr = (void*)taddr[ __NR_str_trans ]; 
   printk( "+ адрес в позиции %d[__NR_str_trans] = %p\n", __NR_str_trans, old_sys_addr );
   if( ( waddr = find_sym( "sys_ni_syscall" ) ) != NULL ) 
      printk( "+ адрес sys_ni_syscall = %p\n", waddr ); 
   else { 
      printk( "+ sys_ni_syscall не найден\n" ); 
      return -EINVAL; 
   } 
   if( old_sys_addr != waddr ) { 
      printk( "+ непонятно! : адреса не совпадают\n" ); 
      return -EINVAL; 
   } 
   printk( "+ адрес нового sys_call = %p\n", &sys_str_translate ); 
   if( ( waddr = find_sym( "sys_init_module" ) ) == NULL ) { 
      printk( "+ sys_init_module не найден\n" ); 
      return -EINVAL; 
   } 
   printk( "+ адрес sys_init_module = %p\n", waddr ); 
   sys_init_module = waddr; 
   if( ( waddr = find_sym( "sys_delete_module" ) ) == NULL ) { 
      printk( "+ sys_delete_module не найден\n" ); 
      return -EINVAL; 
   } 
   printk( "+ адрес sys_delete_module = %p\n", waddr ); 
   sys_delete_module = waddr; 
   rw_enable(); 
   taddr[ __NR_str_trans ] = sys_str_translate; 
   rw_disable(); 
   printk( "+ модуль %s загружен\n", this_mod_file ); 
   return res; 
} 

static void __exit mod_exit( void ) { 
   printk( "+ адрес syscall при выгрузке = %p\n", (void*)taddr[ __NR_str_trans ] );
   rw_enable(); 
   taddr[ __NR_str_trans ] = old_sys_addr; 
   rw_disable(); 
   printk( "+ восстановлен адрес syscall = %p\n", old_sys_addr ); 
   printk( "+ модуль %s выгружен\n", this_mod_file ); 
   return; 
} 

Проверим совместную работу всех компонентов: пользовательского процесса, корневого модуля и трёх модулей-плагинов.

$ ./syscall 0x77 
syscall error -1 : Operation not permitted 
$ sudo insmod master.ko 
$ dmesg | tail -n30 | grep + 
+ адрес sys_call_table = c07ab3d8 
+ адрес в позиции 223[__NR_str_trans] = c045b9a8 
+ адрес sys_ni_syscall = c045b9a8 
+ адрес нового sys_call = f99db024 
+ адрес sys_init_module = c0470f50 
+ адрес sys_delete_module = c046f4e8 
+ модуль master.ko загружен 
$ sudo ./syscall 077 
syscall return 63 
$ dmesg | tail -n30 | grep + 
+ системный запрос 3 байт: 077 
+ длина файла модуля = 95223 байт 
+ адрес буфера чтения = f9c09000 
+ считано из файла oct.ko 95223 байт 
+ модуль oct.ko загружен 
+ адрес обработчика = f9b83000 
+ oct.ko : запрос : 77 
+ вычислено значение 63 
+ модуль oct.ko выгружен 
$ sudo ./syscall 77 
syscall return 77 
$ dmesg | tail -n30 | grep + 
+ системный запрос 2 байт: 77 
+ длина файла модуля = 95223 байт 
+ адрес буфера чтения = f9c41000 
+ считано из файла dec.ko 95223 байт 
+ модуль dec.ko загружен 
+ адрес обработчика = f9c75000 
+ dec.ko : запрос : 77 
+ вычислено значение 77 
+ модуль dec.ko выгружен 
$ sudo ./syscall 0x77 
syscall return 119 
$ dmesg | tail -n30 | grep + 
+ системный запрос 4 байт: 0x77 
+ длина файла модуля = 95553 байт 
+ адрес буфера чтения = f9c7b000 
+ считано из файла hex.ko 95553 байт 
+ модуль hex.ko загружен 
+ адрес обработчика = f9caf000 
+ hex.ko : запрос : 77 
+ вычислено значение 119 
+ модуль hex.ko выгружен 
$ sudo ./syscall z77 
syscall error -1 : Operation not permitted 
$ sudo rmmod master 
$ lsmod | head -n4 
Module                  Size  Used by 
minix                  19212  1 
fuse                   48375  2 
ip6table_filter         2227  0 
$ dmesg | tail -n37 | grep + 
+ адрес syscall при выгрузке = f99db024 
+ восстановлен адрес syscall = c045b9a8 
+ модуль master.ko выгружен 
$ sudo ./syscall 0x77 
syscall error -1 : Operation not permitted 

Этот пример выводит так много промежуточных результатов, что дополнительно комментировать его работу нет необходимости. Правда, может возникнуть закономерный вопрос: а где же обещанная возможность вводить в проект новые модули-плагины, не затрагивая код корневого модуля? В явном виде подобная функциональность в нашем примере отсутствует. Но, если вынести определение соответствия между именем используемого плагина (переменная ind) и именем файла модуля (массив slave_name[]) в текстовый конфигурационный файл, то в результате будет создана динамически расширяемая система (вопрос чтения информации из файлов был рассмотрен в одной из первых статей данного цикла).


Заключение

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

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



Загрузка

ИмяРазмерМетод загрузки
plugin.tgz6KBHTTP

Информация о методах загрузки


Ресурсы

Об авторе

Фото автора

Олег Иванович Цилюрик, много лет был разработчиком программного обеспечения в крупных центрах разработки: ВНИИ РТ, НПО "Дельта", КБ ПМ. Последние годы работал над проектами в области промышленной автоматики, IP телефонии и коммуникаций. Автор нескольких книг. Преподаватель тренингового отделения международной софтверной компании Global Logic.

Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Спасибо. Эта запись была помечена для модератора.


Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Сообщение о нарушении не было отправлено. Попробуйте, пожалуйста, позже.


developerWorks: вход


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Выберите ваше отображаемое имя

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

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

(Должно содержать от 3 до 31 символа.)


Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Оценить эту статью

Комментарии

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=857580
ArticleTitle=Нестандартные сценарии использования модулей ядра: Часть 47. Подключаемые плагины
publish-date=02072013