практикум по Linux Kernel
Модератор: Olej
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
практикум по Linux Kernel
Под "практикум" имеется в виду подборка задач для самостоятельного изготовления, на которых можно попрактиковаться в написании kernel-кода (т.е. оформляемого как модули, конечно).
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
Задача: Написать модуль, который должен зафиксировать в системный журнал значение счётчика системного таймера ядра jiffies, и сразу же завершается. Постарайтесь записать код модуля как можно более кратким.
Это совсем просто:
Вот и всё ... хотя и здесь есть место для "почему"...
Задача: Написать модуль подобный предыдущему, но чтобы он выводил значение jiffies не в системный журнал, а на терминал (попутно: почему в API ядра отсутствует функция вывода на терминал? ).
Это уже далеко не так просто (задача с * ):
В архиве есть .hist файл с протоколом прогонов ... и в 32 бит системе, и в 64, и в ядрах и 2.6.32, и 3.14, и 3.17 ...
Это совсем просто:
Код: Выделить всё
#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" ); // не убирать - не сработает!
- Вложения
-
- jiffies.tgz
- (2.8 КБ) 493 скачивания
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
Ещё 2 сходных задачи - одна попроще, а вторая - с фокусами...
Задача: Напишите драйвер символьного устройства (любой способ регистрации), который может писать
в статический буфер (достаточно большого размера, скажем 1024) и затем читать оттуда записанное
значение. Добейтесь, чтобы устройство допускало чтение и запись для любых пользователей (0666).
Предполагаем, что драйвер предназначен только для символьных (ASCIZ) данных.
Это совсем просто...
Общая часть (для 2-х задач, чтобы не переписывать 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
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
А вот со второй задачей придётся попотеть...Olej писал(а):Ещё 2 сходных задачи - одна попроще, а вторая - с фокусами...
Задача: Напишите драйвер символьного устройства, которое работает по принципу очереди: записываемые данные помещаются в конец очереди, а считываемые берутся из головы, и удаляются из очереди. Для организации очереди используйте ядерную структуру 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
P.S. Операции чтения-записи в варианте queuel.c сделаны очень грязно - первое, что стало работоспособным, с огромными перестраховками: в условиях завершения операций для счётчиков передачи стоит >=, хотя > там при нормальной работе возникать не может, только = ... это перестраховка для завершения циклов при любых условиях.
Так что если это кого заинтересует - там есть над чем поработать в улучшение.
P.P.S. Особенно обратите внимание на POSIX тестировании (cp, cat, ...) данными диной больше 65536 - это принципиальная граница для проверок.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
Кроме настоящих задач, на решение которых можно убить ... и пару дней, как для последней показанной задачи, интересны могут быть в практикуме и простенькие вопросы, на которые вовсе не так просто полно ответить ... совсем не безобидные вопросы:
Задача: Найдите и перечислите как можно больше (оцениваем по числу) отличий в программировании приложений
пользователя и модулей ядра. ... а также и некоторых неочевидных сходных вещей.
Задача: Попытайтесь сформулировать все преимущества и недостатки (табличка + и -) микроядерной архитектуры
по сравнению с моноядерной.
Задача: Для своей любимой процессорной архитектуры (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, в некотором смысле...
Задача: В продолжение предыдущего вопроса: а есть ли языковые системы, обеспечивающие автономную работу, не используя интерфейс библиотеки C (т.е. POSIX API в POSIX системе )?
Насколько я знаю, такой язык 1 - это Go ... это то, что я знаю (IMHO), не исключено, что их больше...
P.S. Можно формально сказать, что эти вопросы (некоторые или все) не относятся напрямую к программированию ядра ОС. Но только не имея ясности с подобными вопросами ... нечего делать вам в ядерном программировании.
Задача: Найдите и перечислите как можно больше (оцениваем по числу) отличий в программировании приложений
пользователя и модулей ядра. ... а также и некоторых неочевидных сходных вещей.
Задача: Попытайтесь сформулировать все преимущества и недостатки (табличка + и -) микроядерной архитектуры
по сравнению с моноядерной.
Задача: Для своей любимой процессорной архитектуры (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, в некотором смысле...
Задача: В продолжение предыдущего вопроса: а есть ли языковые системы, обеспечивающие автономную работу, не используя интерфейс библиотеки C (т.е. POSIX API в POSIX системе )?
Насколько я знаю, такой язык 1 - это Go ... это то, что я знаю (IMHO), не исключено, что их больше...
P.S. Можно формально сказать, что эти вопросы (некоторые или все) не относятся напрямую к программированию ядра ОС. Но только не имея ясности с подобными вопросами ... нечего делать вам в ядерном программировании.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
Задача: Почему мы в качестве тестовых приложений для драйверов устройств (символьных, блочных) мы должны предпочитать стандартные команды Linux (echo, cat, cp, ... fdisk, mkfs, mount, ...), а не свои собственные тестовые программы.Olej писал(а):Кроме настоящих задач, на решение которых можно убить ... и пару дней, как для последней показанной задачи, интересны могут быть в практикуме и простенькие вопросы, на которые вовсе не так просто полно ответить ... совсем не безобидные вопросы:
Привести как можно больше аргументов или примеров.
Задача: Оцените порядок интервала времени, на котором происходит переполнение счётчика системных тиков ядра jiffies.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
Задача: Напишите модуль, который создаёт иерархию имён в /proc, как минимум глубиной больше или равной 2 (а вообще то говоря, произвольной глубины), чтобы в эту ноду можно было писать целочисленное значение, и считывать его оттуда. Запись не численного значения должна возвращать ошибку и не изменять значения.
Любопытные здесь моменты:
1. как промежуточные каталоги в иерархии, так и конечные "точки подключения", терминальные имена - создаются одним и тем же вызовом create_proc_entry(), поэтому всё может быть сделано в одном цикле.
2. всё это работает до ядра 3.9 (включительно), после этого механизм работы с /proc радикально меняется.
Код: Выделить всё
#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 КБ) 455 скачиваний
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
Olej писал(а):Задача: Напишите модуль, который создаёт иерархию имён в /proc, как минимум глубиной больше или равной 2 (а вообще то говоря, произвольной глубины), чтобы в эту ноду можно было писать целочисленное значение, и считывать его оттуда. Запись не численного значения должна возвращать ошибку и не изменять значения.
Задача: А теперь сделайте это же, но для ядра 3.10 или старше
P.S. Я, кстати, не знаю пока, не разбирался, что они там намудрили ... но работа с procfs поменялась принципиально.
P.P.S. по опыту, очень грубо ... такое впечатление, что в среднем вы можете рассчитывать, что ваш модуль (драйвер) будет благополучно собираться на протяжении ближайших, приблизительно, 10-ти последовательных версий ядра: если писали для 2.6.22 - то до 2.6.32, если писали для 2.6.38 - то до 3.6 ...
Статистика по развитию ядра говорит, что версии ядра обновляются со средним интервалом 12 недель. Получается, что если вы разрабатываете поставочный модуль ядра, то можете рассчитывать, что он останется актуальным ~120 недель, или около 2.5 лет. На большее рассчитывать вряд ли приходится.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: практикум по Linux Kernel
А теперь на базе этого кина, мы можем постоить следующее кино ... интересная такая задача, т.е. даже целая группа задач: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 писал(а): Задача: Создайте модель системы авторегулирования в /proc:
Это локальная система: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
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя