практикум по Linux Kernel

Вопросы программного кода и архитектуры Linux

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 15 фев 2015, 15:00

Olej писал(а): Итак:
Теперь, разобравшись что там у них произошло, можно переписать (расширить) и предыдущие задачи...
Задача: Напишите модуль, который создаёт иерархию имён в /proc, как минимум глубиной больше или равной 2 (а вообще то говоря, произвольной глубины), чтобы в эту ноду можно было писать целочисленное значение, и считывать его оттуда. Запись не численного значения должна возвращать ошибку и не изменять значения.
Вот так:

Код: Выделить всё

#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <asm/uaccess.h>

#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )
#define ERR(...) printk( KERN_ERR "! "__VA_ARGS__ )

#define DEFAULT "data"
static char path[ 80 ] = DEFAULT;                    // путевое имя от /proc
module_param_string( path, path, sizeof( path ), 0 );

static long value = 0;

static ssize_t node_read( struct file *file,         // чтение из /proc/<path>:
                          char *buf, size_t count, loff_t *ppos ) {
   char buf_msg[ 30 ];
   if( *ppos != 0 ) return 0;                        // EOF
   sprintf( buf_msg, "%ld\n", value );  
   if( copy_to_user( buf, buf_msg, strlen( buf_msg ) ) ) return -EFAULT;
   LOG( "read: return %s", buf_msg );
   *ppos = strlen( buf_msg );
   return strlen( buf_msg );
}

static ssize_t node_write( struct file *file,        // запись в /proc/<path>:
                           const char *buf, size_t count, loff_t *ppos ) {
   char buf_msg[ 30 ], *endp;
   long res;
   int len = count < sizeof( buf_msg ) - 1 ? count : sizeof( buf_msg ) - 1;
   if( copy_from_user( buf_msg, buf, len ) ) return -EFAULT;
   if( '\n' == buf_msg[ len -1 ] ) buf_msg[ len -1 ] = '\0'; 
   else buf_msg[ len ] = '\0';
   res = simple_strtol( buf_msg, &endp, 10 );
   if( strlen( buf_msg ) != endp - buf_msg ) return -EINVAL;
   LOG( "write: update %s\n", buf_msg );
   value = res;
   return len;
}

static const struct file_operations node_fops = {
   .owner = THIS_MODULE,
   .read  = node_read,
   .write  = node_write
};

static void erase( struct proc_dir_entry *node ) {    // удаление иерархии 
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
   struct proc_dir_entry *parent;
   do {
      parent = node->parent;
      LOG( "remove entry %s\n", node->name );
      remove_proc_entry( node->name, parent );
      node = parent;
   } while( strcmp( node->name, "/proc" ) != 0 );
#else
   char *beg = path, *fin, name[ 20 ];                // доменный компонент имени 
   while( 1 ) {
      if( NULL == ( fin = strchr( beg, '/' ) ) ) break;  
      if( fin != beg ) break;
      beg++;
   }
   if( NULL == fin ) 
      strcpy( name, beg );
   else {
      strncpy( name, beg, fin - beg );
      name[ fin - beg ] = '\0';
   }
   remove_proc_subtree( name, NULL );
   LOG( "remove subtree /proc/%s\n", name );
#endif
}

static struct proc_dir_entry *last_proc_node = NULL;  // нижний уровень

static int __init proc_init( void ) {
   char *beg = path, *fin, name[ 20 ];                // доменный компонент имени 
   struct proc_dir_entry *parent_proc_node = NULL;
   if( '/' == path[ strlen( path ) - 1 ] )            // страховка
      path[ strlen( path ) - 1 ] = '\0';
   while( 1 ) {                                       
      if( NULL == ( fin = strchr( beg, '/' ) ) )
         break;                                       // не каталог  
      if( fin == beg ) {
         beg++;
         continue;
      }
      strncpy( name, beg, fin - beg );
      name[ fin - beg ] = '\0';     
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
      last_proc_node = create_proc_entry( 
                          name, S_IFDIR | S_IRWXUGO, parent_proc_node );
      if( last_proc_node != NULL ) 
         last_proc_node->uid = last_proc_node->gid = 0;
#else
      last_proc_node = proc_mkdir( name, parent_proc_node );
#endif
      if( NULL == last_proc_node ) {
         ERR( "can't create directory %s\n", name );
         goto err;
      }
      LOG( "create directory: %s\n", name );
      parent_proc_node = last_proc_node;
      beg = fin + 1;
   }
   strcpy( name, beg );                               // терминальное имя
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
   last_proc_node = create_proc_entry(
                       name, S_IFREG | S_IRUGO | S_IWUGO, parent_proc_node );
   if( last_proc_node != NULL ) {
      last_proc_node->uid = last_proc_node->gid = 0;
      last_proc_node->proc_fops = &node_fops;
   }
#else
   last_proc_node = proc_create( 
                       name, S_IFREG | S_IRUGO | S_IWUGO, parent_proc_node, &node_fops );
#endif
   if( NULL == last_proc_node ) {
      ERR( "can't create node %s\n", name );
      goto err;
   }
   LOG( "create node: %s\n", name );
   return 0;
err:
   erase( parent_proc_node );
   return -ENOENT;
}

static void __exit proc_exit( void ) {
   erase( last_proc_node );
   LOG( "erased /proc/%s\n", path );
} 

module_init( proc_init );
module_exit( proc_exit ); 
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
В общем виде - и под старую и под новую реализации ядра - это оказалось не так просто сделать.

Код: Выделить всё

bash-4.2$ uname -a
Linux notebook.localdomain 3.14.8-200.fc20.i686 #1 SMP Mon Jun 16 22:36:56 UTC 2014 i686 i686 i386 GNU/Linux
bash-4.2$ sudo insmod procdat.ko path=d1/d2/d3/d4/t
bash-4.2$ tree /proc/d1
/proc/d1
└── d2
    └── d3
        └── d4
            └── t
3 directories, 1 file
bash-4.2$ echo 4444444444 >/proc/d1/d2/d3/d4/t
bash-4.2$ cat /proc/d1/d2/d3/d4/t
4444444444
bash-4.2$ echo строка >/proc/d1/d2/d3/d4/t
bash: echo: ошибка записи: Недопустимый аргумент
Тут вам и контроль типов ... и все удовольствия ;-)

(архив переименован по сравнению с тем, как он показывался раньше, в старой реализации - чтобы не путать его с примером из текста proc.tgz, в предыдущих сообщениях)
Вложения
lproc.tgz
(7.8 КБ) 213 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 15 фев 2015, 15:15

Olej писал(а): Разборку я покажу, начиная с примера, который использовал в Драйверы и модули ядра Linux (очередной цикл обновлений): простенький модуль, создающий минимальную иерархию имён /proc/mod_dir/mod_node, и умеющий писать символьную строку в терминальное имя mod_node, а затем её оттуда читать ... простенько и со вкусом :lol: .
Если кого интересуют эти ядерно-драйверные разборки ;-) , то я напомню, что текст рукописи + архив всех примеров - лежат по показанной ссылке, можете их брать в последней редакции там.

Там же, заведено уже ... сообщение, страничка: Практикум по Linux Kernel, где будут выложены ... по готовности (а готово оно уже % на 80):
- архив вот этих задач, которые выкладываются здесь в теме;
- подчищенный текст "Драйверы и модули ядра Linux (очередной цикл обновлений)" под эти задачи ... планировалось изрядно сократить этот текст для продвинутых разработчиков, которым основы модулей ядра не нужно повторять ... Но уже видно, что существенно сократить его не получается - было 425 стр., в текущем виде 395 стр.. По крайней мере, это будет обновлённый и освежённый текст ... под ядро 3.10 и далее.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 15 фев 2015, 19:02

Olej писал(а): Задача: Создайте модель системы авторегулирования в /proc:
Olej писал(а): Задача: А теперь сделайте это же, но для ядра 3.10 или старше :cry:
Ну и тогда самое время расширить и задачу asu (показанную раньше) на полную независимость от версий ядра:

Код: Выделить всё

bash-4.2$ uname -a
Linux notebook.localdomain 3.14.8-200.fc20.i686 #1 SMP Mon Jun 16 22:36:56 UTC 2014 i686 i686 i386 GNU/Linux
bash-4.2$ sudo insmod modpasu.ko
bash-4.2$ tree /proc/asu/
/proc/asu/
├── increment
├── multiply
└── value
0 directories, 3 files
bash-4.2$ ./cli_krnp
команда (h-подсказка): =
новое значение = 0
команда (h-подсказка): *
усиление = 100%
команда (h-подсказка): *150
усиление = 150%
команда (h-подсказка): =100
новое значение = 100
100 -100 => -50
-50 +50 => 25
25 -25 => -12
-12 +12 => 6
6 -6 => -3
-3 +3 => 1
Вложения
asu.tgz
(13.81 КБ) 218 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 18 фев 2015, 15:39

Управление памятью в ядре: kmalloc() и vmalloc()...

В примечаниях к тексту Драйверы и модули ядра Linux есть коды тестов и результаты тестирования функций kmalloc() и vmalloc() по: а). максимальному размеру "куска", который они могут выделить по запросу и б). по затратам времени (скорости) выполнения запроса на выделение памяти.

В 2-х словах:
- kmalloc() в состоянии выделить на 1-2 порядка меньше объём чем vmalloc();
- kmalloc() делает это в 50-1000 раз быстрее, чем vmalloc();

Но там просто тупо тестировался объём блока размером 2**N, при возрастающем N, когда блок уже не может быть выделен (т.е., вообще то говоря, оценка с "точностью" ;-) до 2**N / 2 ).

Задача (*) (на попрактиковаться ;-) ) : Проведите проверку максимальных блоков памяти, которые в вашей системе могут быть выделены механизмами kmalloc() и vmalloc(), соответственно. Сделайте это с более высокой точностью, чем показано приближённо в тексте (сделайте модуль, который определяет эти значения с высокой точностью).

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 18 фев 2015, 15:53

Olej писал(а):Задача (*) (на попрактиковаться ;-) ) : Проведите проверку максимальных блоков памяти, которые в вашей системе могут быть выделены механизмами kmalloc() и vmalloc(), соответственно. Сделайте это с более высокой точностью, чем показано приближённо в тексте (сделайте модуль, который определяет эти значения с высокой точностью).
Почему я поставил (*) этой задаче?
Потому, что здесь следует сделать предупреждение ... раньше чем решать задачу:
- определить точный размер блока, который может быть выделен - это значит многократно тестировать ядро блоками, которые не могут быть выделены + уменьшать размер такого блока;
- это хорошо, представляется, сделать бисекцией интервала, низший размер которого - то что можно выделить, верхний - то что уже нельзя ... и последовательно сужение этого интервала;
- такие эксперименты над ядром, при минимальной ошибке в алгоритме бисекции, будут разносить ваше ядро вдребезги ... даже при последующей перезагрузке (например, за счёт переполнения системным журналом доступного места на диске);
- поэтому алгоритм настоятельно рекомендую отладить на пробной задаче пространства пользователя, и только потом переносить его в ядро! ... (а то я вам насоветую задачу! :lol: )

Вот возможный вариант такого приложения:

Код: Выделить всё

#include <stdio.h>
#include <string.h>

typedef unsigned long long data_t;

data_t val;

inline int ok( data_t lim ) {
   return lim < val;     // здесь может быть любой критерий
}

data_t request( void ) {
   char buf[ 80 ], ctl[ 80 ];
   unsigned long long targ; 
   int n = 0;
   for(;;) {
      fprintf( stdout, "цель (число): " );
      fflush( stdout );
      fgets( buf, sizeof( buf ) - 1, stdin );
      buf[ strlen( buf ) - 1 ] = '\0';  // удалить \n
      n = sscanf( buf, "%llu%s", &targ, ctl );
      if( n != 1 ) {
         printf( "ошибка ввода!\n" );
         goto err;
      }
      sprintf( ctl, "%llu", targ );
      if( strcmp( buf, ctl ) != 0 ) {
         printf( "превышение размерности!\n" );
         goto err;
      }
      break;
   err:;
   };
   printf( "введена строка\t%s\nзадана цель\t%llu\n", buf, targ );
   return (data_t)targ;
}

int main( int argc, char **argv, char **envp ) {
   while( 1 ) {
      data_t liml = 0, limh = 1;
      val = request();
      while( 1 ) {     // найти верхнюю границу
         printf( "%llu | ", (unsigned long long)limh );
         if( !ok( limh ) ) break;
         limh *= 2;
      }
      liml = limh / 2;      
      printf( "\n" );
      while( 1 ) {     // сужение границ 
         data_t midle = ( limh + liml ) / 2;
         printf( "%llu - %llu | ", (unsigned long long)liml, (unsigned long long)limh );
         if( limh == val || ( limh - liml ) < 2 ) break;
         if( ok( midle ) ) liml = midle;
         else limh = midle;
      }
      printf( "\nзадана цель\t%llu\n", val );
      printf( "найдено число\t%llu\n", (unsigned long long)limh );
   }
   return 0;
}

Код: Выделить всё

[Olej@modules memmax]$ make iter
gcc -Wall iter.c -o iter
[Olej@modules memmax]$ ./iter 
цель (число): 77
введена строка	77
задана цель	77
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 
64 - 128 | 64 - 96 | 64 - 80 | 72 - 80 | 76 - 80 | 76 - 78 | 76 - 77 | 
задана цель	77
найдено число	77
цель (число): 123456
введена строка	123456
задана цель	123456
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 65536 | 131072 | 
65536 - 131072 | 98304 - 131072 | 114688 - 131072 | 122880 - 131072 | 122880 - 126976 | 122880 - 124928 | 122880 - 123904 | 123392 - 123904 | 123392 - 123648 | 123392 - 123520 | 123392 - 123456 | 
задана цель	123456
найдено число	123456
цель (число): 1111111111111111111
введена строка	1111111111111111111
задана цель	1111111111111111111
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 65536 | 131072 | 262144 | 524288 | 1048576 | 2097152 | 4194304 | 8388608 | 16777216 | 33554432 | 67108864 | 134217728 | 268435456 | 536870912 | 1073741824 | 2147483648 | 4294967296 | 8589934592 | 17179869184 | 34359738368 | 68719476736 | 137438953472 | 274877906944 | 549755813888 | 1099511627776 | 2199023255552 | 4398046511104 | 8796093022208 | 17592186044416 | 35184372088832 | 70368744177664 | 140737488355328 | 281474976710656 | 562949953421312 | 1125899906842624 | 2251799813685248 | 4503599627370496 | 9007199254740992 | 18014398509481984 | 36028797018963968 | 72057594037927936 | 144115188075855872 | 288230376151711744 | 576460752303423488 | 1152921504606846976 | 
576460752303423488 - 1152921504606846976 | 864691128455135232 - 1152921504606846976 | 1008806316530991104 - 1152921504606846976 | 1080863910568919040 - 1152921504606846976 | 1080863910568919040 - 1116892707587883008 | 1098878309078401024 - 1116892707587883008 | 1107885508333142016 - 1116892707587883008 | 1107885508333142016 - 1112389107960512512 | 1110137308146827264 - 1112389107960512512 | 1110137308146827264 - 1111263208053669888 | 1110700258100248576 - 1111263208053669888 | 1110981733076959232 - 1111263208053669888 | 1110981733076959232 - 1111122470565314560 | 1111052101821136896 - 1111122470565314560 | 1111087286193225728 - 1111122470565314560 | 1111104878379270144 - 1111122470565314560 | 1111104878379270144 - 1111113674472292352 | 1111109276425781248 - 1111113674472292352 | 1111109276425781248 - 1111111475449036800 | 1111110375937409024 - 1111111475449036800 | 1111110925693222912 - 1111111475449036800 | 1111110925693222912 - 1111111200571129856 | 1111111063132176384 - 1111111200571129856 | 1111111063132176384 - 1111111131851653120 | 1111111097491914752 - 1111111131851653120 | 1111111097491914752 - 1111111114671783936 | 1111111106081849344 - 1111111114671783936 | 1111111110376816640 - 1111111114671783936 | 1111111110376816640 - 1111111112524300288 | 1111111110376816640 - 1111111111450558464 | 1111111110913687552 - 1111111111450558464 | 1111111110913687552 - 1111111111182123008 | 1111111111047905280 - 1111111111182123008 | 1111111111047905280 - 1111111111115014144 | 1111111111081459712 - 1111111111115014144 | 1111111111098236928 - 1111111111115014144 | 1111111111106625536 - 1111111111115014144 | 1111111111110819840 - 1111111111115014144 | 1111111111110819840 - 1111111111112916992 | 1111111111110819840 - 1111111111111868416 | 1111111111110819840 - 1111111111111344128 | 1111111111111081984 - 1111111111111344128 | 1111111111111081984 - 1111111111111213056 | 1111111111111081984 - 1111111111111147520 | 1111111111111081984 - 1111111111111114752 | 1111111111111098368 - 1111111111111114752 | 1111111111111106560 - 1111111111111114752 | 1111111111111110656 - 1111111111111114752 | 1111111111111110656 - 1111111111111112704 | 1111111111111110656 - 1111111111111111680 | 1111111111111110656 - 1111111111111111168 | 1111111111111110912 - 1111111111111111168 | 1111111111111111040 - 1111111111111111168 | 1111111111111111104 - 1111111111111111168 | 1111111111111111104 - 1111111111111111136 | 1111111111111111104 - 1111111111111111120 | 1111111111111111104 - 1111111111111111112 | 1111111111111111108 - 1111111111111111112 | 1111111111111111110 - 1111111111111111112 | 1111111111111111110 - 1111111111111111111 | 
задана цель	1111111111111111111
найдено число	1111111111111111111
цель (число): 111111111111111111111111111111111
превышение размерности!
цель (число): ^C

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 18 фев 2015, 16:00

Olej писал(а):
Olej писал(а):Задача (*) (на попрактиковаться ;-) ) : Проведите проверку максимальных блоков памяти, которые в вашей системе могут быть выделены механизмами kmalloc() и vmalloc(), соответственно. Сделайте это с более высокой точностью, чем показано приближённо в тексте (сделайте модуль, который определяет эти значения с высокой точностью).
Почему я поставил (*) этой задаче?
Потому, что здесь следует сделать предупреждение ... раньше чем решать задачу:
Но и это ещё не всё! :lol:

В 32-бит системе всё будет нормально. Но в 64-бит системе vmalloc() может выделять очень много памяти, подключая сюда HIMEM память, и даже больше реального объёма RAM ... отбирая при этом память у ваших загруженных приложений и аварийно их выгружая...
Одним словом, выполнение такого "теста" для vmalloc() на 64-бит системе может привести к тяжёлым повреждениям системы. :!:

Не выполняйте такие тасты на 64-бит системе ... Если сильно не терпится :lol: - делайте это на виртуальной машине.
С этим нужно ещё разбираться.
(в Интернете подобные вещи обсуждаются, вызывают недоуменные вопросы, и диаметрально противоположные :lol: ответы на них)

Кстати, все эти тесты прекрасно и адекватно выполняются на виртуальных машинах в VirtualBox, где вы контролируете полностью "доступную RAM" для системы.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 18 фев 2015, 16:10

Olej писал(а): Но и это ещё не всё! :lol:
Ну и вот теперь (вы полностью предупреждены - значит вооружены :lol: ) можно и модуль обсуждать (в каком-то его теперешнем варианте):

Код: Выделить всё

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

#define LOG(...) printk( KERN_INFO "+ "__VA_ARGS__ )
#define ERR(...) printk( KERN_ERR  "+ "__VA_ARGS__ )

static int mode = 0;   // выделение памяти: 0 - kmalloc(), 2 - vmalloc()
module_param( mode, uint, S_IRUGO );

static int debug = 0;  // уровень отладочной диагностики
module_param( debug, uint, S_IRUGO );

inline int ok( size_t lim ) { // тестирование размещения
   void* addr = 0 == mode ?
                kmalloc( lim, GFP_KERNEL | __GFP_NOWARN ) :
                __vmalloc( lim, GFP_KERNEL | __GFP_NOWARN, PAGE_KERNEL );
   if( addr ) {
      if( 0 == mode ) kfree( addr );
      else vfree( addr );
   }
   return addr != NULL;
}

static int __init init( void ) {
   size_t liml = 0, limh = PAGE_SIZE;
   if( mode > 1 ) {
      ERR( "illegal mode value\n" );
      return -EINVAL;
   }
   while( 1 ) {     // найти верхнюю границу
      int res = ok( limh );
      if( debug > 1 || ( !res && debug > 0 ) ) 
         LOG( "%s :\t%lu -> %s\n", 
              0 == mode ? "kmalloc" : "vmalloc",
              (long)limh,
              res ? "OK" : "failed" );
      if( !res ) break;
      limh *= 2;
   }
   liml = limh / 2;      
   while( 1 ) {     // сужение границ 
      size_t midle = limh / 2 + liml / 2;
      int res = ok( midle );
      if( debug > 1 || ( !res && debug > 0 ) ) 
         LOG( "%s :\t%lu...%lu -> %s\n", 
              0 == mode ? "kmalloc" : "vmalloc",
              (long)liml, (long)limh,
              res ? "OK" : "failed" );
      if( limh == midle || ( midle - liml ) < 2 ) { 
         if( res ) liml = midle;
         break;
      }    
      if( res ) liml = midle;
      else limh = midle;
   }
   LOG( "maximal allocation %lu (%lX) bytes\n", (long)liml, (long)liml );
   return -1;
}

static void __exit cleanup( void ) {}

module_init( init );
module_exit( cleanup );

MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
MODULE_DESCRIPTION( "memory allocation size test" );
MODULE_LICENSE( "GPL v2" );
Его выполнение ... в одной из многих конфигураций системы и железа:

Код: Выделить всё

[olej@fedora memmax]$ sudo insmod memmax.ko
Error: could not insert module memmax.ko: Operation not permitted

[olej@fedora memmax]$ dmesg | tail -n3
[   18.183263] RPC: Registered tcp NFSv4.1 backchannel transport module.
[  517.166080] TCP: lp registered
[  779.043417] + maximal allocation 4194304 (400000) bytes

[olej@fedora memmax]$ sudo insmod memmax.ko mode=1
Error: could not insert module memmax.ko: Operation not permitted

[olej@fedora memmax]$ dmesg | tail -n3
[  811.032048] vmap allocation for size 240975872 failed: use vmalloc=<size> to increase size.
[  811.032053] vmap allocation for size 240975872 failed: use vmalloc=<size> to increase size.
[  811.032061] + maximal allocation 240967680 (E5CE000) bytes


Здесь vmalloc() в состоянии выделить на 2 порядка больше блок, чем kmalloc().
И может быть и ещё больше...
(обращаем внимание на сообщение "use vmalloc=<size> to increase size" - это относится к параметрам загрузки GRUB, синтаксис указания принципиально разный для GRUB 0.9 и GRUB 1.0, но в каждом случае при загрузке можно определить границу для размера vmalloc)
Вложения
memmax.tgz
(13.88 КБ) 208 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 25 фев 2015, 22:03

Те задачи, что обсуждались здесь, и ещё некоторые вопросы и задачи - собраны в связный текст и выложены для свободного использования: Практикум по Linux Kernel (продолжение).

Возможно, этот материал будет дополняться и обновляться.
Тогда это будет делаться (обновлением ссылок) прямо на этой странице.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 10 мар 2015, 23:21

Ещё группа задач ...
На тот большой раздел, который в сопровождающем тексте назван:
12. Расширенные возможности программирования
Это те расширенные возможности, которые не описаны нигде в мировых публикациях, по крайней мере, насколько мне известно... это всё возможности, которые найдены экспериментально за несколько лет плотного программирования для модулей ядра. ;-)

Вам не говорили, что код вашего модуля ядра может читать и писать файлы на диске?
Сейчас глянем как это происходит...

Но прежде:
Задача: Проанализируйте, в каких случаях (не так частых) модулю может понадобиться а). читать из файла, б). писать в файл.

Из частых случаев чтения — это чтение конфигураций загрузки модуля.
Почему это неудобно делать параметрами загрузки? Неудобно в тех случаях, когда конфигурационных параметров много и, самое главное, неизвестно наперёд точное их число.
Классический пример: конфигурирование синхронных каналов E1/T1 в драйверах DAHDI программных телефонных коммутаторов (Asterisk, FreeSWITCH, ...).

Запись из модуля бывает удобной (как мне это было нужно), когда нужно вести протокол работы устройства-модуля в отдельный файл, отличный от системного журнала... например, дамп какого-то особого сетевого трафика.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: практикум по Linux Kernel

Непрочитанное сообщение Olej » 10 мар 2015, 23:36

Olej писал(а): На тот большой раздел, который в сопровождающем тексте назван:
12. Расширенные возможности программирования
Это те расширенные возможности, которые не описаны нигде в мировых публикациях, по крайней мере, насколько мне известно... это всё возможности, которые найдены экспериментально за несколько лет плотного программирования для модулей ядра. ;-)
Небольшое лирическое отступление:
- почему-то бытует мнение, что возможностей программирования в модулей ядра мало, гораздо меньше, чем в юзерспейс: там библиотеки расширяющие, всякие тулзы и т.д. ...
- а API ядра а). крайне плохо описаны и б). допускают использовать только то, что экспортируется ядром (это, в общем, неверно).

На самом деле это не так!
Простое логическое рассуждение нам в том в помощь: всё, что вообще делается в Linux - это выполняется ядром, выполнением системных вызовов из юзерспейс.
Поэтому, из модуля ядра можно сделать вообще всё (например, загрузить другую операционную систему ;-) ) ... только нужно этими возможностями умело воспользоваться.

P.S. Меня всегда удивляло, как много хаерских усилий и публикаций в Интернет положено на то, чтобы перехватить, подменить, или добавить точку входа в таблицу системных вызовов. Потому что таблица системных вызовов, для предупреждения вот тех же шаловливых хакерских ручек, с ядра 2.6 не экспортируется (недоступна для внешнего связывания). Для этого в статьях исследуют offline дамп объектного кода ядра и т.п. ...
Жуть!
Это делается элементарно, Ватсон. :lol:

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 9 гостей