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

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

Модератор: Olej

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

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

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

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

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

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

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

Задача: Написать модуль, который должен зафиксировать в системный журнал значение счётчика системного таймера ядра jiffies, и сразу же завершается. Постарайтесь записать код модуля как можно более кратким.

Это совсем просто:

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

#include <linux/module.h>
#include <linux/init.h>
#include <linux/jiffies.h>

int init_module( void ) {
   printk( KERN_INFO "module: jiffies on start = %lX\n", jiffies );
   return -1;
}
Вот и всё ... хотя и здесь есть место для "почему"... ;-)

Задача: Написать модуль подобный предыдущему, но чтобы он выводил значение jiffies не в системный журнал, а на терминал (попутно: почему в API ядра отсутствует функция вывода на терминал? ;-) ).

Это уже далеко не так просто (задача с * ;-) ):

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

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kallsyms.h> 
#include <linux/uaccess.h>

static unsigned long waddr = 0;

int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) {
   if( 0 == strcmp( (char*)data, sym ) ) {
      waddr = addr;
      return 1;
   }
   else return 0;
};

int init_module( void ) {
   char msg[ 120 ];
   asmlinkage long (*sys_write) ( unsigned int fd, const char __user *buf, size_t count );
   mm_segment_t fs = get_fs();
   if( 0 == kallsyms_on_each_symbol( symb_fn, (void*)"sys_write" ) ) {
      printk( "error: symbol not found" );
      return -1;
   }
   sprintf( msg, "module: jiffies on start = %lX\n", jiffies );
   sys_write = (void*)waddr;
   set_fs( get_ds() );
   sys_write( 1, msg, strlen( msg ) );
   set_fs(fs);
   return -1;
}

MODULE_LICENSE( "GPL" ); // не убирать - не сработает!
В архиве есть .hist файл с протоколом прогонов ... и в 32 бит системе, и в 64, и в ядрах и 2.6.32, и 3.14, и 3.17 ...
Вложения
jiffies.tgz
(2.8 КБ) 483 скачивания

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

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

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

Ещё 2 сходных задачи - одна попроще, а вторая - с фокусами...

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

Это совсем просто...

Общая часть (для 2-х задач, чтобы не переписывать 2 раза):

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

#include <linux/module.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>

MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
MODULE_VERSION( "6.4" );

static int minor = 0;
module_param( minor, int, S_IRUGO );

static bool debug = false;  // логический парамер
module_param( debug, bool, S_IRUGO );
#define LOG(...) if( debug ) printk( KERN_INFO "=== "__VA_ARGS__ )

static ssize_t queue_read( struct file*, char*, size_t, loff_t* );
static ssize_t queue_write( struct file*, const char*, size_t, loff_t* );

static const struct file_operations queue_fops = {
   .owner = THIS_MODULE,
   .read  = queue_read,     // чтение из /dev/queue 
   .write = queue_write     // запись в /dev/queue
};

static struct miscdevice queue_dev = {
   MISC_DYNAMIC_MINOR,      // автоматически выбираемое
   "queue",
   &queue_fops,
   .mode = 0666             // флаги устройства
};

static int __init dev_init( void );
static void __exit dev_exit( void );

module_init( dev_init );
module_exit( dev_exit );
И сам модуль этой задачи:

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

#include "common.c"

#define SIZE 1024
static char buffer[ SIZE + 1 ] = "\n";                       // статический буфер

static ssize_t queue_read( struct file *file, char *buf,     // чтение из /dev/queue 
                           size_t count, loff_t *ppos ) {
   int len = strlen( buffer );
   if( count < len ) return -EINVAL;
   if( *ppos != 0 ) {
      LOG( "read %d -> 0\n", (int)count );                    // EOF
      return 0;
   }
   if( copy_to_user( buf, buffer, len ) ) return -EINVAL;
   *ppos = len;
   LOG( "read %d -> %d\n", (int)count, len );
   return len;
}

static ssize_t queue_write( struct file *f, const char *buf, // запись в /dev/queue
                            size_t count, loff_t *ppos ) {
   int len = count < SIZE ? count : SIZE;
   if( copy_from_user( buffer, buf, len ) ) return -EINVAL;
   buffer[ len ] = '\0';
   LOG( "write %d -> %d\n", (int)count, len );
   return len;
}

static int __init dev_init( void ) {
   int ret;
   if( minor != 0 ) queue_dev.minor = minor;
   ret = misc_register( &queue_dev );
   if( ret ) {
      printk( KERN_ERR "=== unable to register misc device\n" );
      goto end;
   }
   printk( KERN_INFO "=== register device 10:%d\n", queue_dev.minor );
end:
   return ret;
}

static void __exit dev_exit( void ) {
   printk( KERN_INFO "=== deregister device 10:%d\n", queue_dev.minor );
   misc_deregister( &queue_dev );
}
Вот, собственно, и вся задача:

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

[Olej@modules queue]$ sudo insmod queues.ko debug=1
[Olej@modules queue]$ cat /dev/queue

[Olej@modules queue]$ echo 098765 > /dev/queue
[Olej@modules queue]$ cat /dev/queue
098765
[Olej@modules queue]$ sudo rmmod queues
Вложения
common.c
(1.11 КБ) 460 скачиваний
queues.c
(1.39 КБ) 456 скачиваний

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

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

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

Olej писал(а):Ещё 2 сходных задачи - одна попроще, а вторая - с фокусами...
А вот со второй задачей придётся попотеть... :lol:
Задача: Напишите драйвер символьного устройства, которое работает по принципу очереди: записываемые данные помещаются в конец очереди, а считываемые берутся из головы, и удаляются из очереди. Для организации очереди используйте ядерную структуру struct list_head. Предусмотрите изменяющиеся, в том числе и весьма большие, объёмы данных, помещённых в очередь. Предполагаем, что драйвер предназначен для любых бинарных данных — проверьте
работоспособность записью с контрольным считыванием самого файла модуля .ko.

Код на этот случай:

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

#include <linux/list.h>
#include <linux/vmalloc.h>
#include "common.c"

//#define SIZE 1024 * 20
//#define SIZE 1024 * 3
#define SIZE 7
typedef struct {             // блок циклического буфера
   struct list_head link;
   unsigned int     wpos, rpos;
   char             data[ SIZE ];
} data_t;

LIST_HEAD( queue );

data_t* new( void ) {        // новый блок в циклический буфер
   data_t *elem = (data_t*)vmalloc( sizeof( data_t ) );
   LOG( "allocate new block %p\n", elem );
   elem->wpos = elem->rpos = 0;
   list_add_tail( &elem->link, &queue );
   return elem;
}

void erase( void ) {         // удалить отработанный блок из циклического буфера
   data_t *head;
   if( list_empty( &queue ) ) return;
   head = list_entry( queue.next, data_t, link );
   LOG( "erase head block %p\n", head );
   list_del( queue.next );   
   vfree( head );
}

unsigned int len( void ) {   // число блоков в циклическом буфере
   unsigned int num = 0;
   struct list_head *iter;
   if( list_empty( &queue ) ) return 0;
   list_for_each( iter, &queue ) num++;
   return num;  
}

//----------------------------------------------------------------------------------

static ssize_t queue_read( struct file *file, char *buf,     // чтение из /dev/queue 
                           size_t count, loff_t *ppos ) {
   data_t* rblock = list_entry( queue.next, data_t, link );
   ssize_t sumlen = 0;
   int free, len;
   if( list_empty( &queue ) || rblock->rpos >= rblock->wpos ) {
      LOG( "read %u -> 0\n", (int)count );                  // EOF
      return 0;
   }
   do {
      rblock = list_entry( queue.next, data_t, link );
      free = rblock->wpos - rblock->rpos;
      len = count < free ? count : free;
      if( copy_to_user( buf, rblock->data + rblock->rpos, len ) )
         return -EINVAL;
      LOG( "read %u -> %u\n", (int)count, len );
      sumlen += len;
      *ppos += len;
      rblock->rpos += len;
      buf += len;
      if( rblock->rpos >= rblock->wpos ) {
         if( rblock->rpos >= SIZE ) {
            erase();
            if( list_empty( &queue ) ) break;
         }
         else break;
      }
      if( count <= len ) break;
      count -= len;
   } while( count != 0 );
   return sumlen;
}

static ssize_t queue_write( struct file *f, const char *buf, // запись в /dev/queue
                            size_t count, loff_t *ppos ) {
   data_t *wblock = list_empty( &queue ) ? new() : list_entry( queue.prev, data_t, link );
   ssize_t sumlen = 0;
   int free, len;
   do {
      free = SIZE - wblock->wpos;
      len = count < free ? count : free;
      if( copy_from_user( wblock->data + wblock->wpos, buf, len ) )
         return -EINVAL;
      LOG( "write %u -> %u\n", (int)count, len );
      count -= len;
      sumlen += len;
      wblock->wpos += len;
      *ppos += len;
      buf += len;
      if( wblock->wpos >= SIZE ) {
         wblock = new();
      };
   } while( count != 0 );
   return sumlen;
}

static int __init dev_init( void ) {
   int ret;
   if( minor != 0 ) queue_dev.minor = minor;
   ret = misc_register( &queue_dev );
   if( ret ) {
      printk( KERN_ERR "=== unable to register misc device\n" );
      goto end;
   }
   printk( KERN_INFO "=== register device 10:%d\n", queue_dev.minor );
   LOG( "block size = %d\n", SIZE );
end:
   return ret;
}

static void __exit dev_exit( void ) {
   while( !list_empty( &queue ) ) erase();
   printk( KERN_INFO "=== deregister device 10:%d\n", queue_dev.minor );
   misc_deregister( &queue_dev );
}
Работа:

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

[Olej@modules queue]$ sudo insmod queuel.ko debug=1
[sudo] password for Olej:
[Olej@modules queue]$ dmesg | tail -n2
[ 7455.007045] === register device 10:57
[ 7455.007048] === block size = 7
[Olej@modules queue]$ cp queuel.ko /dev/queue
[Olej@modules queue]$ cp /dev/queue queuel2.ko
[Olej@modules queue]$ echo $?
0
[Olej@modules queue]$ ls -l queuel*.ko
-rw-rw-r--. 1 Olej Olej 189118 фев  4 13:59 queuel2.ko
-rw-rw-r--. 1 Olej Olej 189118 фев  4 13:58 queuel.ko
[Olej@modules queue]$ diff queuel.ko queuel2.ko
[Olej@modules queue]$ echo $?
0
[Olej@modules queue]$ sudo rmmod queuel.ko
И архив этих 2-х задач, со сборкой и протоколом отладки и тестирования...

P.S. Операции чтения-записи в варианте queuel.c сделаны очень грязно - первое, что стало работоспособным, с огромными перестраховками: в условиях завершения операций для счётчиков передачи стоит >=, хотя > там при нормальной работе возникать не может, только = ... это перестраховка для завершения циклов при любых условиях.
Так что если это кого заинтересует - там есть над чем поработать в улучшение.

P.P.S. Особенно обратите внимание на POSIX тестировании (cp, cat, ...) данными диной больше 65536 - это принципиальная граница для проверок. ;-)
Вложения
queuel.c
(3.54 КБ) 463 скачивания
queue.tgz
(6.55 КБ) 470 скачиваний

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

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

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

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

Задача: Найдите и перечислите как можно больше (оцениваем по числу) отличий в программировании приложений
пользователя и модулей ядра. ... а также и некоторых неочевидных сходных вещей.

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

Задача: Для своей любимой процессорной архитектуры (MIPS, ARM, ... хотя бы для AMD_x64) напишите приложение юзерспейс, выполняющее системный вызов (например write(), getpid(), ...) непосредственным вызовом программного прерывания (int 80h - для i686 и AMD_x86). Сделайте то же самое используя вызов syscall().

Задача: А можно ли системный вызов (любой), которые предназначены для процессов юзерспейс, выполнить из кода (модуля) ядра? Какие при этом возникают сложности в кодировании?

Задача: Легко показать (ldd ...), что скомпилированный код C++ всегда будет использовать POSIX API, стандартную библиотеку языка C libc.so, как интерфейс к системным вызовам ... Т.е. без наличия, скажем, библиотеки C, если бы такое было возможным, программы C++ были бы неработоспособны.
Покажите, как эту же библиотеку используют:
- виртуальная машина Java ...
- проделайте это, по возможности, для а). Oracle JDK, б). Open JDK, стоящем в Linux по умолчанию;
- для интерпретатора Python.
... т.е. все они только надстройка над библиотекой C, в некотором смысле... :lol:

Задача: В продолжение предыдущего вопроса: а есть ли языковые системы, обеспечивающие автономную работу, не используя интерфейс библиотеки C (т.е. POSIX API в POSIX системе :lol: )?
Насколько я знаю, такой язык 1 - это Go ... это то, что я знаю (IMHO), не исключено, что их больше... ;-)

P.S. Можно формально сказать, что эти вопросы (некоторые или все) не относятся напрямую к программированию ядра ОС. Но только не имея ясности с подобными вопросами ... нечего делать вам в ядерном программировании. :lol:

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

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

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

Olej писал(а):Кроме настоящих задач, на решение которых можно убить ... и пару дней, как для последней показанной задачи, интересны могут быть в практикуме и простенькие вопросы, на которые вовсе не так просто полно ответить ... совсем не безобидные вопросы:
Задача: Почему мы в качестве тестовых приложений для драйверов устройств (символьных, блочных) мы должны предпочитать стандартные команды Linux (echo, cat, cp, ... fdisk, mkfs, mount, ...), а не свои собственные тестовые программы.
Привести как можно больше аргументов или примеров.

Задача: Оцените порядок интервала времени, на котором происходит переполнение счётчика системных тиков ядра jiffies.

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

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

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

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

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

#include <linux/module.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__ )

static char* path;                                   // путевое имя от /proc
module_param( path, charp, 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
};

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
static struct proc_dir_entry *last_proc_node = NULL; // нижний домен имени
#endif

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
static void erase( struct proc_dir_entry *node ) {   // удаление иерархии 
   struct proc_dir_entry *parent;
   do {
      parent = node->parent;
      LOG( "remove entry %s - parent %s\n", node->name, node->parent->name );
      remove_proc_entry( node->name, parent );
      node = parent;
   } while( strcmp( parent->name, "/proc" ) != 0 ); 
}
#endif

static int __init proc_init( void ) {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0))
   ERR( "it's work only for kernel LE 3.9\n" );
   return -ECANCELED;
#else
   char name[ 40 ] = "data";                          // имя по умолчанию
   struct proc_dir_entry *parent_proc_node = NULL;
   if( NULL != path ) {
      char *beg = path, *fin;
      while( 1 ) {
         if( ( fin = strchr( beg, '/' ) ) != NULL ) { // каталог
            strncpy( name, beg, fin - beg );
            name[ fin - beg ] = '\0';     
            last_proc_node = create_proc_entry( name, S_IFDIR | S_IRWXUGO, parent_proc_node );
         }
         else {                                       // терминал
            strcpy( name, beg );
            last_proc_node = create_proc_entry( name, S_IFREG | S_IRUGO | S_IWUGO, parent_proc_node );
         }
         if( NULL == last_proc_node ) {
            ERR( "can't create %s %s\n",
                 ( fin != NULL ? "directory" : "node" ), name );
            erase( parent_proc_node );
            return -ENOENT;
         }
         LOG( "create %s: %s\n",
              ( fin != NULL ? "directory" : "node" ), name );
         last_proc_node->uid = last_proc_node->gid = 0;
         if( NULL == fin ) {
            last_proc_node->proc_fops = &node_fops;
            break; 
         }
         else {
            parent_proc_node = last_proc_node;
            beg = fin + 1;
         }
      } 
   }
   LOG( "module installed\n" );
   return 0;
#endif
}

static void __exit proc_exit( void ) {
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
   erase( last_proc_node );
   LOG( "module removed\n" );
#endif
} 

module_init( proc_init );
module_exit( proc_exit );

MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

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

[olej@fedora proc]$ sudo insmod procdat.ko path=Xdir/Ydir/point

[olej@fedora proc]$ dmesg | tail -n5
[ 5562.160565] ! module removed
[ 6989.724243] ! create directory: Xdir
[ 6989.724248] ! create directory: Ydir
[ 6989.724250] ! create node: point
[ 6989.724250] ! module installed
  
[olej@fedora proc]$ cat /proc/Xdir/Ydir/point
0

[olej@fedora proc]$ echo 1111 > /proc/Xdir/Ydir/point

[olej@fedora proc]$ cat /proc/Xdir/Ydir/point
1111

[olej@fedora proc]$ sudo rmmod procdat.ko
Любопытные здесь моменты:
1. как промежуточные каталоги в иерархии, так и конечные "точки подключения", терминальные имена - создаются одним и тем же вызовом create_proc_entry(), поэтому всё может быть сделано в одном цикле.
2. всё это работает до ядра 3.9 (включительно), после этого механизм работы с /proc радикально меняется.
Вложения
proc.tgz
(5.39 КБ) 445 скачиваний

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

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

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

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

Задача: А теперь сделайте это же, но для ядра 3.10 или старше :cry:

P.S. Я, кстати, не знаю пока, не разбирался, что они там намудрили ... но работа с procfs поменялась принципиально.

P.P.S. по опыту, очень грубо ... такое впечатление, что в среднем вы можете рассчитывать, что ваш модуль (драйвер) будет благополучно собираться на протяжении ближайших, приблизительно, 10-ти последовательных версий ядра: если писали для 2.6.22 - то до 2.6.32, если писали для 2.6.38 - то до 3.6 ...
Статистика по развитию ядра говорит, что версии ядра обновляются со средним интервалом 12 недель. Получается, что если вы разрабатываете поставочный модуль ядра, то можете рассчитывать, что он останется актуальным ~120 недель, или около 2.5 лет. На большее рассчитывать вряд ли приходится. :cry:

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

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

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

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

Задача: Создайте модель системы авторегулирования в /proc:
1. Есть некоторое регулируемое значение (это одна переменная, /proc/.../value);
2. Есть корректирующая переменная (это одна переменная, /proc/.../increment) — при записи в эту переменную регулируемое значение корректируется (суммируется) с записанным значением, умноженным на коэффициент петлевого усиления (константу).
3. Коэффициент петлевого усиления считаем ещё одним именем в /proc, по чтению-записи: /proc/.../multiply
4. Последовательно считывая (cat) value — формируем значение коррекции как разницу желаемого и имеемого, и записываем (echo) в /proc/.../increment, с целью привести /proc/.../value к условному значению 0.
5. Значение /proc/.../value должно допускать запись для начальной инициализации, коэффициент петлевого усиления задаём как параметр при старте модуля.... но можно редактировать и записью в /proc/.../multiply
6. Устройство корректирует регулируемое значение на полученный шаг корректировки;
7. Если в результате записи п.5 разбалансировка всё ещё не нулевая — возвращаемся на п.4.

Задача: Для предыдущей задачи сделать приложение, которое будет циклически, с фиксированным интервалом времени, формировать разницу и записывать корректирующее значение в /proc/.../increment, до тех пор, пока /proc/.../value не установится в 0.
Подсказка: Для упрощения обеих предыдущих задач сделайте сначала в отдельном юзерспейс-приложении модель циклического авторегулирования:

Задача: Проанализируйте, как такие именованные «точки данных» могут использоваться в качестве базы для построения специализированной SCADA системы.

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

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

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

Olej писал(а): Задача: Создайте модель системы авторегулирования в /proc:
asu.tgz
(9.82 КБ) 452 скачивания
Olej писал(а): Задача: Для предыдущей задачи сделать приложение, которое будет циклически, с фиксированным интервалом времени, формировать разницу и записывать корректирующее значение в /proc/.../increment, до тех пор, пока /proc/.../value не установится в 0.
Подсказка: Для упрощения обеих предыдущих задач сделайте сначала в отдельном юзерспейс-приложении модель циклического авторегулирования:
Это локальная система:

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

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/asu$ ./cli_locl
команда (h-подсказка): *160
усиление = 160%
команда (h-подсказка): =100
новое значение = 100
100 -100 => -60
-60 +60 => 36
36 -36 => -21
-21 +21 => 12
12 -12 => -7
-7 +7 => 4
4 -4 => -2
-2 +2 => 1
команда (h-подсказка): q
А это на базе модуля ядра:

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

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/asu$ sudo insmod modpasu.ko
[sudo] password for olej:

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/asu$ ./cli_kern
команда (h-подсказка): *160
усиление = 160%
команда (h-подсказка): =100
новое значение = 100
100 -100 => -60
-60 +60 => 36
36 -36 => -21
-21 +21 => 12
12 -12 => -7
-7 +7 => 4
4 -4 => -2
-2 +2 => 1
команда (h-подсказка): q

olej@atom:~/2015-WORK/PRACTIS.Kernel/PRACTIS.Tasks/TASKS/asu$ sudo rmmod modpasu.ko
Как легко видеть - результаты идентичные.

Ответить

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

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

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