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

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

Модератор: Olej

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

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

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

Работаем с обработчиками прерываний...

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

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

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

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

#define SHARED_IRQ 1
#define NAME_LEN   12
#define IRQ_ID     321

static int irq = SHARED_IRQ;           // контролируемая IRQ 
module_param( irq, int, 0 );
static unsigned int delay = 1;         // интервал индикации, сек.
module_param( delay, uint, 0 );

static atomic64_t counter = ATOMIC_LONG_INIT( 0 );
static volatile long unsigned int next; 

static irqreturn_t handler( int irq, void *id ) {
   atomic64_inc( &counter );
   if( time_after( jiffies, next ) ) { // прошло больше интервала
      LOG( "%ld => %lld\n", jiffies, atomic64_read( &counter ) );
      next = jiffies + delay;
   }
   return IRQ_NONE;
}

int init_module( void ) {
   char dev[ NAME_LEN ];
   int ret;
   sprintf( dev, "handler-%02d", irq );
   if( ( ret = request_irq( irq, handler, IRQF_SHARED, dev, (void*)IRQ_ID ) ) != 0 ) 
      ERR( "can't install handler for IRQ=%d\n", irq );
   else {
      LOG( "module installed\n" );
      delay *= HZ;
      next = jiffies + delay;          // отметка старта
   }
   return ret;
}

void cleanup_module( void ) {
   synchronize_irq( irq );
   free_irq( irq, (void*)IRQ_ID );
   LOG( "module removed: registered %llu events\n", atomic64_read( &counter ) );
}

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

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

bash-4.2$ uname -r
3.14.8-200.fc20.i686
bash-4.2$ cat /proc/cpuinfo | grep 'model name' | wc -l
2
bash-4.2$ cat /proc/cpuinfo | grep 'model name' | head -n1
model name      : Genuine Intel(R) CPU           T2300  @ 1.66GHz

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

-bash-4.2$  watch 'cat /proc/interrupts | grep i915'
Every 2,0s: cat /proc/interrupts | grep i915                              Sat Feb  7 13:43:11 2015

 16:      75572     187050   IO-APIC-fasteoi   i915, enp2s14

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

bash-4.2$ sudo insmod irqs.ko irq=16 delay=5
[sudo] password for Olej:
bash-4.2$ dmesg | tail -n10
[ 9182.522817] === 8882522 => 8430
[ 9183.528192] === 8883528 => 8582
[ 9184.533577] === 8884533 => 8736
[ 9185.371191] === module removed: registered 8864 events
[25756.938801] === module installed
[25761.940308] === 25461940 => 751
[25766.945543] === 25466945 => 1507
[25771.950789] === 25471950 => 2273
[25776.956032] === 25476956 => 3027
[25781.961263] === 25481961 => 3777
bash-4.2$ sudo rmmod irqs.ko

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

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

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

Olej писал(а):Работаем с обработчиками прерываний...

Задача: Установите свой дополнительный обработчик прерываний (на любой IRQ, например сетевой карты, численное значение IRQ указывайте параметром загрузки модуля). Модуль должен диагностировать полученные прерывания в журнал и подсчитывать их число.
Задача: То же, что и предыдущее задание, но отображайте динамически число обслуженных прерываний в собственное путевое имя в /proc.

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

#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/interrupt.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 SHARED_IRQ 1
#define NAME_LEN   12
#define IRQ_ID     321

static int irq = SHARED_IRQ;           // контролируемая IRQ 
module_param( irq, int, 0 );

static atomic64_t counter = ATOMIC_LONG_INIT( 0 );
static volatile long unsigned int next; 

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
static irqreturn_t handler( int irq, void *id ) {
   atomic64_inc( &counter );
   return IRQ_NONE;
}

static struct proc_dir_entry *own_proc_dir;
#endif

static ssize_t node_read( struct file *file, char *buf,
                          size_t count, loff_t *ppos ) {
   static unsigned int read_count = 1;
   if( ( ++read_count & 1 ) != 0 ) {
      *ppos = 0;
      return 0;                        // EOF
   }   
   else {
      char buf_msg[ 30 ];
      sprintf( buf_msg, "%llu\n", atomic64_read( &counter ) ); 
      if( copy_to_user( buf, buf_msg, strlen( buf_msg ) ) ) return -EFAULT;
      return ( *ppos = strlen( buf_msg ) );
   }
}

static const struct file_operations node_fops = {
   .owner = THIS_MODULE,
   .read  = node_read,                 // чтение из /proc/irq_counter/counter
};

#define NAME_DIR  "irq_counter" 
#define NAME_NODE "counter"

int init_module( 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 dev[ NAME_LEN ];
   int ret;
   struct proc_dir_entry *own_proc_node;
   sprintf( dev, "handler-%02d", irq );
   if( ( ret = request_irq( irq, handler, IRQF_SHARED, dev, (void*)IRQ_ID ) ) != 0 ) {
      ERR( "can't install handler for IRQ=%d\n", irq );
      goto err_irq;
      return ret;
   }      
   own_proc_dir = create_proc_entry( NAME_DIR, S_IFDIR | S_IRWXUGO, NULL );
   if( NULL == own_proc_dir ) {
      ret = -ENOENT;
      ERR( "can't create directory /proc/%s", NAME_DIR );
      goto err_dir;
   }
   own_proc_dir->uid = own_proc_dir->gid = 0;
   own_proc_node = create_proc_entry( NAME_NODE, S_IFREG | S_IRUGO | S_IWUGO, own_proc_dir );
   if( NULL == own_proc_node ) {
      ret = -ENOENT;
      ERR( "can't create node /proc/%s/%s\n", NAME_DIR, NAME_NODE );
      goto err_node;
   }
   own_proc_node->uid = own_proc_node->gid = 0;
   own_proc_node->proc_fops = &node_fops;
   LOG( "register node /proc/%s/%s\n", NAME_DIR, NAME_NODE );
   return 0;
err_node:
   remove_proc_entry( NAME_DIR, NULL );
err_dir:
   synchronize_irq( irq );
   free_irq( irq, (void*)IRQ_ID );
err_irq:
   return ret;
#endif
}

void cleanup_module( void ) {
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 9, 0))
   remove_proc_entry( NAME_NODE, own_proc_dir );
   remove_proc_entry( NAME_DIR, NULL );
   synchronize_irq( irq );
   free_irq( irq, (void*)IRQ_ID );
   LOG( "module removed: registered %llu events\n", atomic64_read( &counter ) );
#endif
}

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

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

[olej@nvidia ~]$ uname -a
Linux nvidia.localdomain 2.6.42.12-1.fc15.i686.PAE #1 SMP Tue Mar 20 16:19:17 UTC 2012 i686 i686 i386 GNU/Linux
[olej@nvidia irq]$ cat /etc/system-release
RFRemix release 15.1 (Lovelock)
[olej@nvidia irq]$ cat /proc/interrupts
           CPU0       CPU1
  0:        119          9   IO-APIC-edge      timer
  1:          1          1   IO-APIC-edge      i8042
  8:          1          0   IO-APIC-edge      rtc0
  9:          0          0   IO-APIC-fasteoi   acpi
 12:          1          3   IO-APIC-edge      i8042
 16:        549       3021   IO-APIC-fasteoi   uhci_hcd:usb5, nvidia
 17:         94         53   IO-APIC-fasteoi   snd_hda_intel
 18:        632        635   IO-APIC-fasteoi   ata_piix, uhci_hcd:usb4
 19:         13         16   IO-APIC-fasteoi   uhci_hcd:usb3
 23:      12680      18414   IO-APIC-fasteoi   ata_piix, ehci_hcd:usb1, uhci_hcd:usb2
 43:     971706         54   PCI-MSI-edge      eth0
 44:        254        256   PCI-MSI-edge      snd_hda_intel
NMI:         17         10   Non-maskable interrupts
LOC:   29175407   29090864   Local timer interrupts
SPU:          0          0   Spurious interrupts
PMI:         17         10   Performance monitoring interrupts
IWI:          0          0   IRQ work interrupts
RES:     917292    1324299   Rescheduling interrupts
CAL:    6437324    6593348   Function call interrupts
TLB:       4060       4617   TLB shootdowns
TRM:          0          0   Thermal event interrupts
THR:          0          0   Threshold APIC interrupts
MCE:          0          0   Machine check exceptions
MCP:          9          9   Machine check polls
ERR:          0
MIS:          0

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

[olej@nvidia irq]$ cat /proc/interrupts | grep 16:
 16:        569       3384   IO-APIC-fasteoi   uhci_hcd:usb5, nvidia
[olej@nvidia irq]$ cat /proc/interrupts | grep 16:
 16:        569       3387   IO-APIC-fasteoi   uhci_hcd:usb5, nvidia
[olej@nvidia irq]$ cat /proc/interrupts | grep 16:
 16:        569       3393   IO-APIC-fasteoi   uhci_hcd:usb5, nvidia
[olej@nvidia irq]$ sudo insmod irqcntp.ko irq=16
[olej@nvidia irq]$ cat /proc/irq_counter/counter
24
[olej@nvidia irq]$ cat /proc/irq_counter/counter
29
[olej@nvidia irq]$ cat /proc/irq_counter/counter
36
[olej@nvidia irq]$ sudo rmmod irqcntp.ko
[olej@nvidia irq]$ dmesg | tail -n3
[ 2837.100377] === can't install handler for IRQ=43
[ 3004.641678] === register node /proc/irq_counter/counter
[ 3052.178023] === module removed: registered 53 events
Обе задачи на обработчики IRQ - в одном архиве.
Там же логи (.hist) прогона на 5 разных компьютерах, разных дистрибутивах и версиях ядра.
Вложения
irq.tgz
(8.02 КБ) 243 скачивания

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

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

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

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

А вот пожалте ;-) :

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

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/pci.h>

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

static char* path;           // имя каталога в /sys/class/...
module_param( path, charp, 0 );

static long value = 0;

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
static ssize_t node_show( struct class *class, struct class_attribute *attr, char *buf ) {
#else
static ssize_t node_show( struct class *class, char *buf ) {
#endif
   sprintf( buf, "%ld\n", value );  
   LOG( "read %ld: %s", (long)strlen( buf ), buf );
   return strlen( buf );
}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
static ssize_t node_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) {
#else
static ssize_t node_store( struct class *class, const char *buf, size_t count ) {
#endif
   char buf_msg[ 30 ], *endp;
   long res;
   strncpy( buf_msg, buf, count );
   if( '\n' == buf_msg[ count - 1 ] ) buf_msg[ count - 1 ] = '\0';
   else buf_msg[ count ] = '\0';
   LOG( "write %ld: %s\n", count, buf_msg );                 // запрос обновление
   if( 0 == strlen( buf_msg ) ) return -EINVAL;              // уже был возврат ошибки
   res = simple_strtol( buf_msg, &endp, 10 );
   if( strlen( buf_msg ) != endp - buf_msg ) return -EINVAL; // не преобразуемая строка
   value = res;
   LOG( "write: update %ld\n", value );
   return count;
}

CLASS_ATTR( node, ( S_IWUSR | S_IRUGO ), &node_show, &node_store );

static struct class *data_class;

static int __init proc_init( void ) {
   char name[ 40 ] = "data";                                 // каталог по умолчанию
   int ret;
   if( NULL != path ) {
      if( strchr( path, '/' ) != NULL ) return -EINVAL;
      else strcpy( name, path );
   }
   data_class = class_create( THIS_MODULE, name );
   if( IS_ERR( data_class ) ) {
      ERR( "cat'n create directory /sys/class/%s\n", name );
      return -ENOENT;
   }
   ret = class_create_file( data_class, &class_attr_node );
   if( ret != 0 ) {
      ERR( "can't create node /sys/class/%s/node\n", name );
      class_destroy( data_class );
      return -ENOENT;
   }
   LOG( "register node /sys/class/%s/node\n", name );
   return 0;
}

static void __exit proc_exit( void ) {
   class_remove_file( data_class, &class_attr_node );
   class_destroy( data_class );
   LOG( "module removed\n" );
} 

module_init( proc_init );
module_exit( proc_exit ); 
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
Чтение такого имени в /sys куда проще, чем в /proc.
А вот с записью - придётся повозиться ... точнее не с самой записью, а с отработкой ошибки формата записи.

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

[Olej@modules sys]$ sudo insmod sysdat.ko path=test
[sudo] password for Olej: 
[Olej@modules sys]$ dmesg | tail -n2
[10344.859456] ! module removed
[11023.614186] ! register node /sys/class/test/node
[Olej@modules sys]$ tree /sys/class/test/
/sys/class/test/
└── node
0 directories, 1 file
[Olej@modules sys]$ ls -l /sys/class/test/
итого 0
-rw-r--r--. 1 root root 4096 фев 10 13:44 node
[Olej@modules sys]$ cat /sys/class/test/node 
0
[Olej@modules sys]$ echo 12345 > /sys/class/test/node 
bash: /sys/class/test/node: Отказано в доступе
[Olej@modules sys]$ echo 12345 | sudo tee 1>/dev/null /sys/class/test/node 
[Olej@modules sys]$ cat /sys/class/test/node 
12345
[Olej@modules sys]$ dmesg | tail -n6
[10344.859456] ! module removed
[11023.614186] ! register node /sys/class/test/node
[11106.764781] ! read 2: 0
[11160.990802] ! write 6: 12345
[11160.990807] ! write: update 12345
[11163.833296] ! read 6: 12345
[Olej@modules sys]$ echo 12z45 | sudo tee 1>/dev/null /sys/class/test/node 
tee: /sys/class/test/node: Недопустимый аргумент
[Olej@modules sys]$ dmesg | tail -n6
[11106.764781] ! read 2: 0
[11160.990802] ! write 6: 12345
[11160.990807] ! write: update 12345
[11163.833296] ! read 6: 12345
[11224.343583] ! write 6: 12z45
[11224.343588] ! write 1: 
[Olej@modules sys]$ sudo rmmod sysdat.ko 
[Olej@modules sys]$ cat /sys/class/test/node 
cat: /sys/class/test/node: Нет такого файла или каталога
[Olej@modules sys]$ cat /sys/class/test
cat: /sys/class/test: Нет такого файла или каталога
Обратите внимание на права доступа: запись в /sys/class/... допускается только для root.
По крайней мере, я на знаю как это сделать для ординарного пользователя.

Попробуйте вы сделать ;-) .
Только не обольщайтесь сразу ;-) : проверку mode в макросе CLASS_ATTR() ввели только после ядра 3.15 ! ... до этого и я такое умел. :lol:
Вложения
sys.tgz
(2.23 КБ) 238 скачиваний

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

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

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

Olej писал(а):
Olej писал(а):Работаем с обработчиками прерываний...

Задача: Установите свой дополнительный обработчик прерываний (на любой IRQ, например сетевой карты, численное значение IRQ указывайте параметром загрузки модуля). Модуль должен диагностировать полученные прерывания в журнал и подсчитывать их число.
Задача: То же, что и предыдущее задание, но отображайте динамически число обслуженных прерываний в собственное путевое имя в /proc.

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

#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/pci.h>  // здесь описание class_create_file()

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

#define SHARED_IRQ 1
#define NAME_LEN   12
#define IRQ_ID     321

static int irq = SHARED_IRQ;           // контролируемая IRQ 
module_param( irq, int, 0 );

static atomic64_t counter = ATOMIC_LONG_INIT( 0 );
static volatile long unsigned int next; 

static irqreturn_t handler( int irq, void *id ) {
   atomic64_inc( &counter );
   return IRQ_NONE;
}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
static ssize_t node_read( struct class *class, struct class_attribute *attr, char *buf ) {
#else
static ssize_t node_read( struct class *class, char *buf ) {
#endif
   char buf_msg[ 30 ];
   sprintf( buf_msg, "%lu\n", (long)atomic64_read( &counter ) ); 
   strcpy( buf, buf_msg );
   printk( "read %ld\n", (long)strlen( buf ) );
   return strlen( buf );
}

#define NAME_DIR  "irq_counter" 
//#define NAME_NODE counter

CLASS_ATTR( counter, S_IRUGO, &node_read, NULL );

static struct class *irq_class;

int init_module( void ) {
   char dev[ NAME_LEN ];
   int ret;
   sprintf( dev, "handler-%02d", irq );
   if( ( ret = request_irq( irq, handler, IRQF_SHARED, dev, (void*)IRQ_ID ) ) != 0 ) {
      ERR( "can't install handler for IRQ=%d\n", irq );
      goto err_irq;
   }      
   irq_class = class_create( THIS_MODULE, NAME_DIR );
   if( IS_ERR( irq_class ) ) {
      ERR( "can't create directory /sys/class/%s\n", NAME_DIR );
      goto err_dir;
   }
   ret = class_create_file( irq_class, &class_attr_counter );
/* <linux/device.h>
   extern int __must_check class_create_file(struct class *class, const struct class_attribute *attr); */
   if( ret != 0 ) {
      ERR( "can't create node /sys/class/%s/counter\n", NAME_DIR );
      goto err_node;
   }
   LOG( "register node /sys/class/%s/counter\n", NAME_DIR );
   return 0;
err_node:
   class_destroy( irq_class );
err_dir:
   synchronize_irq( irq );
   free_irq( irq, (void*)IRQ_ID );
err_irq:
   return ret;
}

void cleanup_module( void ) {
/* <linux/device.h>
extern void class_remove_file(struct class *class, const struct class_attribute *attr); */
   class_remove_file( irq_class, &class_attr_counter );
   class_destroy( irq_class );
   synchronize_irq( irq );
   free_irq( irq, (void*)IRQ_ID );
   LOG( "module removed: registered %lu events\n", (long)atomic64_read( &counter ) );
}

MODULE_LICENSE( "GPL v2" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
Вот на таком оборудовании (ядро 3.18):

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

[Olej@modules irq]$ cat /proc/cpuinfo | grep 'model name'
model name	: Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
model name	: Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
model name	: Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
model name	: Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
[Olej@modules sys]$ cat /proc/interrupts | grep 28:
 28:       3854       2131        702     452434   PCI-MSI-edge      0000:00:1f.2
[Olej@modules sys]$ cat /proc/interrupts | grep 30:
 30:         34          9    2560285          5   PCI-MSI-edge      em1
[Olej@modules sys]$ cat /proc/interrupts | grep 31:
 31:        190     244345         53         33   PCI-MSI-edge      i915
Выполняем:

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

Olej@modules irq]$ sudo insmod irqcnts.ko irq=31
[sudo] password for Olej:
[Olej@modules class]$ tree /sys/class/irq_counter
/sys/class/irq_counter
└── counter
0 directories, 1 file
[Olej@modules irq]$ dmesg | tail -n3
[42659.561313] write 9
[42659.561316] write 1
[45136.141843] === register node /sys/irq_counter/counter
[Olej@modules irq]$ sudo insmod irqcnts.ko irq=31
[sudo] password for Olej:
[Olej@modules irq]$ dmesg | tail -n3
[42659.561313] write 9
[42659.561316] write 1
[45136.141843] === register node /sys/irq_counter/counter
[Olej@modules irq]$ ls -l /sys/class/irq_counter/counter
-r--r--r--. 1 root root 4096 фев  9 22:05 /sys/class/irq_counter/counter
[Olej@modules irq]$ cat /sys/class/irq_counter/counter
1026
[Olej@modules irq]$ cat /sys/class/irq_counter/counter
1042
[Olej@modules irq]$ cat /sys/class/irq_counter/counter
1053
[Olej@modules irq]$ cat /sys/class/irq_counter/counter
1073
[Olej@modules irq]$ sudo rmmod irqcnts.ko
[Olej@modules irq]$ dmesg | tail -n7
[42659.561316] write 1
[45136.141843] === register node /sys/irq_counter/counter
[45263.315607] read 5
[45265.661246] read 5
[45267.550385] read 5
[45269.407494] read 5
[45283.303995] === module removed: registered 1142 events
Обращаем внимание: это работает для любых самых новых ядер (3.18), а для /proc с ядром >3.9 придётся ох как повозиться! :lol:

Все и всякие обработчики IRQ опять же - все в одном архиве...
Вложения
irq.tgz
(11.63 КБ) 253 скачивания

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

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

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

Olej писал(а): Задача: А теперь такое же имя, но не в /proc, а в /sys/class ... куда можно было бы писать-читать числовое значение в модуле.
Olej писал(а): Обратите внимание на права доступа: запись в /sys/class/... допускается только для root.
По крайней мере, я на знаю как это сделать для ординарного пользователя.

Попробуйте вы сделать ;-) .
Только не обольщайтесь сразу ;-) : проверку mode в макросе CLASS_ATTR() ввели только после ядра 3.15 ! ... до этого и я такое умел. :lol:
Задача: А теперь проделайте то же, что в предыдущей задаче, но теперь используя непосредственно
struct kobject, а также группы атрибутов (файловых имён) - struct attribute_group.
Сразу предусмотрите возможность нескольких идентичных по назначению имён в /sys/kernel/...
Как сделать это число имён наиболее динамичный (наиболее безболезненно изменяемым).
Сравните сложность этого и предыдущего способов.

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

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>

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

static char* path;                                // имя каталога в /sys/kernel/...
module_param( path, charp, 0 );

// предварительные описания:
static ssize_t show( struct kobject *kobj, struct kobj_attribute *attr, char *buf );
static ssize_t store( struct kobject *kobj, struct kobj_attribute *attr,
                      const char *buf, size_t count );

#define NODES 3
char* title[ NODES ] = { "node1", "node2", "node3" };
static struct kobj_attribute attributes[ NODES ]; // атрибуты-файлы

// Create a group of attributes so that we can create and destroy them all
static struct attribute *attrs[ NODES + 1 ] = {
   &attributes[ 0 ].attr,
   &attributes[ 1 ].attr,
   &attributes[ 2 ].attr,
   NULL,   // need to NULL terminate the list of attributes
};

/*
 * An unnamed attribute group will put all of the attributes directly in
 * the kobject directory.  If we specify a name, a subdirectory will be
 * created for the attributes with the directory being the name of the
 * attribute group.
 */
static struct attribute_group attr_group = {
   .attrs = attrs,
};

static int *target( struct kobj_attribute *attr ) {
   int i;
   static int value[ NODES ] = { 0, 0, 0 };       // данные упакованы вовнутрь
   for( i = 0; i < NODES; i++ )
      if( strcmp( attr->attr.name, attributes[ i ].attr.name ) == 0 )
         return value + i;
   return NULL;
}

static ssize_t show( struct kobject *kobj, struct kobj_attribute *attr, char *buf ) {
   int *pv = target( attr );
   return  NULL == pv ? -EINVAL : sprintf( buf, "%d\n", *pv );;
}

static ssize_t store( struct kobject *kobj, struct kobj_attribute *attr,
                          const char *buf, size_t count) {
   int num, *pv = target( attr );
   if( NULL == pv ) return -EINVAL;
   num =  sscanf( buf, "%d", pv );
   return 0 == num ? -EINVAL : count;
}

static struct kobject *data_kobj;

static int __init proc_init( void ) {
   char name[ 40 ] = "data";                      // каталог по умолчанию
   int i, ret;
   if( NULL != path ) {
      if( strchr( path, '/' ) != NULL ) return -EINVAL;
      else strcpy( name, path );
   }
   // Create a simple kobject with the name
   data_kobj = kobject_create_and_add( name, kernel_kobj );
   if( !data_kobj ) {
      ERR( "cat'n create directory /sys/kernel/%s\n", name );
      return -ENOMEM;
   }
   // Create the files associated with this kobject
   for( i = 0; i < NODES; i++ ) {                 // заполнение имён файлов
      struct kobj_attribute kobj = {
         .attr = { .name = title[ i ], .mode = 0666 },
         .show = show,
         .store = store
      };
      attributes[ i ] = kobj;
   }
   ret = sysfs_create_group( data_kobj, &attr_group );
   if( ret ) {
      ERR( "can't create nodes /sys/kernel/%s/*\n", name );
      kobject_put( data_kobj );
   }
   else LOG( "register nodes /sys/kernel/%s/*\n", name );
   return ret;
}

static void __exit proc_exit( void ) {
   kobject_put( data_kobj );
   LOG( "module removed\n" );
}

module_init( proc_init );
module_exit( proc_exit );
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Greg Kroah-Hartman <greg@kroah.com>" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

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

[Olej@modules sys]$ sudo insmod syskdat.ko
[sudo] password for Olej:
[Olej@modules sys]$ ls /sys/kernel/data/
node1  node2  node3
[Olej@modules sys]$ tree /sys/kernel/data/
/sys/kernel/data/
├── node1
├── node2
└── node3
0 directories, 3 files
[Olej@modules sys]$ ls -l /sys/kernel/data/
итого 0
-rw-rw-rw-. 1 root root 4096 фев 11 00:39 node1
-rw-rw-rw-. 1 root root 4096 фев 11 00:39 node2
-rw-rw-rw-. 1 root root 4096 фев 11 00:39 node3
[Olej@modules sys]$ cat /sys/kernel/data/*
0
0
0
[Olej@modules sys]$ echo 11 > /sys/kernel/data/node1
[Olej@modules sys]$ echo 22 > /sys/kernel/data/node2
[Olej@modules sys]$ echo 33 > /sys/kernel/data/node3
[Olej@modules sys]$ cat /sys/kernel/data/*
11
22
33
[Olej@modules sys]$ sudo rmmod syskdat.ko
Вложения
sys.tgz
(4.87 КБ) 261 скачивание

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

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

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

Olej писал(а):Под "практикум" имеется в виду подборка задач для самостоятельного изготовления, на которых можно попрактиковаться в написании kernel-кода (т.е. оформляемого как модули, конечно).
Мне сегодня по поводу вот этих ... экзерсисов ;-) - написал один из тим-лидов крупной международной софтверной компании:
В итоге, получится первый в мире самоучитель по Linux Kernel :)

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

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

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

Olej писал(а):
Olej писал(а): Задача: Создайте модель системы авторегулирования в /proc:
Вложение asu.tgz больше недоступно
Olej писал(а): Задача: Для предыдущей задачи сделать приложение, которое будет циклически, с фиксированным интервалом времени, формировать разницу и записывать корректирующее значение в /proc/.../increment, до тех пор, пока /proc/.../value не установится в 0.
Подсказка: Для упрощения обеих предыдущих задач сделайте сначала в отдельном юзерспейс-приложении модель циклического авторегулирования:
Задача: А теперь то же самое, но группа имён, точки подключения - не в /proc, а в /sys ... в /sys/class/* или /sys/kernel/* ... - там, где вы чувствуете себя увереннее :lol:

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

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include "common.h"

// предварительные описания операций:
static ssize_t show( struct kobject *kobj, struct kobj_attribute *attr, char *buf );
static ssize_t store( struct kobject *kobj, struct kobj_attribute *attr,
                      const char *buf, size_t count );

static struct kobj_attribute attributes[ NPTS ] = {
   { .attr = { .name = "value", .mode = 0666 },
     .show = show, .store = store },
   { .attr = { .name = "increment", .mode = 0222 },
     .store = store },
   { .attr = { .name = "multiply", .mode = 0666 },
     .show = show, .store = store }
};

// Create a group of attributes so that we can create and destroy them all
static struct attribute *attrs[ NPTS + 1 ] = {
   &attributes[ 0 ].attr,
   &attributes[ 1 ].attr,
   &attributes[ 2 ].attr,
   NULL,   // need to NULL terminate the list of attributes
};

/* An unnamed attribute group will put all of the attributes directly in
 * the kobject directory.  If we specify a name, a subdirectory will be
 * created for the attributes with the directory being the name of the
 * attribute group.  */
static struct attribute_group attr_group = {
   .attrs = attrs,
};

static long *target( struct kobj_attribute *attr ) {
   static long increment;     // корректирующее воздействие спрятали вовнутрь
   static long* const pv[ NPTS ] = { &value, &increment, &multy };
   int i;
   for( i = 0; i < NPTS; i++ )
      if( strcmp( attr->attr.name, attributes[ i ].attr.name ) == 0 )
         return pv[ i ];
   return NULL;
} 

static ssize_t show( struct kobject *kobj, struct kobj_attribute *attr, char *buf ) {
   ssize_t ret;
   long *pv = target( attr ); 
   if( 0 == strcmp( attr->attr.name, points[ 1 ] ) ) return -EINVAL;
   if( NULL == pv ) return -EINVAL;
   ret = sprintf( buf, "%ld\n", *pv );
   LOG( "read %s: %s", attr->attr.name, buf );
   return ret;
}

static ssize_t store( struct kobject *kobj, struct kobj_attribute *attr,
                      const char *buf, size_t count) {
   long num, *pv = target( attr );
   if( NULL == pv ) return -EINVAL;
   num = sscanf( buf, "%ld", pv );
   if( 0 == num ) return -EINVAL;
   LOG( "write %s: %ld\n", attr->attr.name, *pv );
   if( 0 == strcmp( attr->attr.name, points[ 1 ] ) ) // increment
      value += ( *pv * multy ) / 100;
   return count;
}

static struct kobject *asu_kobj;

static int __init proc_init( void ) {
   int ret;
   if( multiplier != 0 ) multy = multiplier;
   // Create a simple kobject with the name
   asu_kobj = kobject_create_and_add( directory, kernel_kobj );
   if( !asu_kobj ) {
      ERR( "cat'n create directory /sys/kernel/%s\n", directory );
      return -ENOMEM;
   }
   // Create the files associated with this kobject
   ret = sysfs_create_group( asu_kobj, &attr_group );
   if( ret ) {
      ERR( "can't create nodes /sys/kernel/%s/*\n", directory );
      kobject_put( asu_kobj );
   }
   else LOG( "register nodes /sys/kernel/%s/*\n", directory );
   return ret;
}

static void __exit proc_exit( void ) {
   kobject_put( asu_kobj );
   LOG( "module removed\n" );
}

module_init( proc_init );
module_exit( proc_exit ); 
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Greg Kroah-Hartman <greg@kroah.com>" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
Там же, в архиве, 2 диалоговых клиента (для /proc и для /sys) ... которые генерятся из единого файла кода, только путеввые имена подставляются соответствующие:

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

[olej@nvidia asu]$ sudo insmod modpasu.ko
[olej@nvidia asu]$ tree /proc/asu/
/proc/asu/
├── increment
├── multiply
└── value

0 directories, 3 files
[olej@nvidia asu]$ ./cli_krnp
команда (h-подсказка): *170
усиление = 170%
команда (h-подсказка): =100
новое значение = 100
100 -100 => -70
-70 +70 => 49
49 -49 => -34
-34 +34 => 23
23 -23 => -16
-16 +16 => 11
11 -11 => -7
-7 +7 => 4
4 -4 => -2
-2 +2 => 1
команда (h-подсказка): q

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

[olej@nvidia asu]$ sudo insmod modsasu.ko
[olej@nvidia asu]$ tree /sys/kernel/asu/
/sys/kernel/asu/
├── increment
├── multiply
└── value

0 directories, 3 files
[olej@nvidia asu]$ ./cli_krns
команда (h-подсказка): *170
усиление = 170%
команда (h-подсказка): =100
новое значение = 100
100 -100 => -70
-70 +70 => 49
49 -49 => -34
-34 +34 => 23
23 -23 => -16
-16 +16 => 11
11 -11 => -7
-7 +7 => 4
4 -4 => -2
-2 +2 => 1
команда (h-подсказка): q
Обе системы (при том, что ч точки зрения ядра Linux они не имеют ничего общего) работают идентично.
Вложения
asu.tgz
(13.48 КБ) 255 скачиваний

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

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

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

Уважаемые коллеги по форуму!

Я вижу довольно большое число скачиваний архивов ( 17-18-19... и это за 10 дней).
Т.е. вопросы по этой теме представляют некоторый интерес...

Формулируйте сюда интересующие вас вопросы, формулировки...
И вы тут же получите по ним варианты решений.

P.S. Это предложение остаётся в силе только до тех пор, пока идёт активная работа над этим практикумом.

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

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

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

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

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

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

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

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

#include "mod_proc.h"
#include "fops_rw.c" // чтение-запись для /proc/mod_dir/mod_node

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

static struct proc_dir_entry *own_proc_dir; //, *own_proc_node;

static int __init proc_init( void ) {
   int ret;
   struct proc_dir_entry *own_proc_node;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
   own_proc_dir = create_proc_entry( NAME_DIR, S_IFDIR | S_IRWXUGO, NULL );
   if( NULL == own_proc_dir ) {
      ret = -ENOENT;
      ERR( "can't create directory /proc/%s\n", NAME_DIR );
      goto err_dir;
   }
   own_proc_dir->uid = own_proc_dir->gid = 0;
   own_proc_node = create_proc_entry( NAME_NODE, S_IFREG | S_IRUGO | S_IWUGO, own_proc_dir );
   if( NULL == own_proc_node ) {
      ret = -ENOENT;
      ERR( "can't create node /proc/%s/%s\n", NAME_DIR, NAME_NODE );
      goto err_node;
   }
   own_proc_node->uid = own_proc_node->gid = 0;
   own_proc_node->proc_fops = &node_fops;
#else
   own_proc_dir = proc_mkdir( NAME_DIR, NULL );
   if( NULL == own_proc_dir ) {
      ret = -ENOENT;
      ERR( "can't create directory /proc/%s\n", NAME_NODE );
      goto err_dir;
   }
   own_proc_node = proc_create( NAME_NODE, S_IFREG | S_IRUGO | S_IWUGO, own_proc_dir, &node_fops );
   if( NULL == own_proc_node ) {
      ret = -ENOENT;
      ERR( "can't create node /proc/%s/%s\n", NAME_DIR, NAME_NODE );
      goto err_node;
   }
#endif
   LOG( "/proc/%s/%s installed\n", NAME_DIR, NAME_NODE );
   return 0;
err_node:
   remove_proc_entry( NAME_DIR, NULL );
err_dir:
   return ret;
}

static void __exit proc_exit( void ) {
   remove_proc_entry( NAME_NODE, own_proc_dir );
   remove_proc_entry( NAME_DIR, NULL );
   LOG( "/proc/%s/%s removed\n", NAME_DIR, NAME_NODE );
} 

Там ещё несколько маленьких включаемых файлов ... реализация операций node_read(), node_write() - но это всё элементарно (в архиве всё есть)...

Работа всего этого хозяйства ... убеждаемся, что это работает в любом ядре:

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

[Olej@modules proc]$ uname -a
Linux modules.localdomain 3.18.5-101.fc20.x86_64 #1 SMP Mon Feb 2 20:58:23 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
bash-4.2$ sudo insmod mod_proct.ko
bash-4.2$ tree /proc/mod_dir/
/proc/mod_dir/
└── mod_node
0 directories, 1 file
bash-4.2$ cat /proc/mod_dir/mod_node 
.........1.........2.........3.........4.........5
bash-4.2$ echo 12345 > /proc/mod_dir/mod_node 
bash-4.2$ cat /proc/mod_dir/mod_node 
12345

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

[olej@nvidia proc]$ uname -a
Linux nvidia.localdomain 2.6.42.12-1.fc15.i686.PAE #1 SMP Tue Mar 20 16:19:17 UTC 2012 i686 i686 i386 GNU/Linux
[olej@nvidia proc]$ sudo insmod mod_proct.ko
[olej@nvidia proc]$ cat /proc/mod_dir/mod_node
.........1.........2.........3.........4.........5
[olej@nvidia proc]$ echo 09876 > /proc/mod_dir/mod_node
[olej@nvidia proc]$ cat /proc/mod_dir/mod_node
09876
Вложения
proc.tgz
(8.69 КБ) 247 скачиваний

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

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

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

Olej писал(а): Задача: Разобраться как там обстоят дела с procfs в ядре 3.10 и старше (в публикациях об этом не найдёте - слишком рано).
А вот с этим будет посложнее ;-) ... потому как это можно понять только изрядно покопавшись в исходниках ядра, да ещё сравнивая разные версии ядра (вот тут нам в помощь ресурс Linux Cross Reference ... потому что я вообще не понимаю как без такого инструмента можно бы решать подобные задачи).

Итак:
1. Вместо старого создания как каталогов так и терминальных имён в /proc:

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

own_proc_dir = create_proc_entry( NAME_DIR, S_IFDIR | S_IRWXUGO, NULL );
own_proc_node = create_proc_entry( NAME_NODE, S_IFREG | S_IRUGO | S_IWUGO, own_proc_dir );
теперь используется, раздельно для каталогов и файлов:

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

own_proc_dir = proc_mkdir( NAME_DIR, NULL );
own_proc_node = proc_create( NAME_NODE, S_IFREG | S_IRUGO | S_IWUGO, own_proc_dir, &node_fops );
2. Ещё есть там мелкие дополнения, связанные с удалением всего поддерева имён (интересно, что это появляется с ядра 3.9, а не 3.10):

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

remove_proc_subtree( name, NULL );
3. Но всё это ... цветочки :lol: !
А ягодки состоят в том что (я, например, потому и убил на это много времени работы):
- они фундаментальное для /proc определение struct proc_dir_entry (вокруг которой всё и крутится) из заголовочного файла <linux/proc_fs.h> перенесли внутрь в файл реализации Linux/fs/proc/internal.h в дереве исходных кодов ядра (по крайней мере находится он в поддереве реализации)...
- а в <linux/proc_fs.h> оставлены только методы доступа к struct proc_dir_entry, и то по указателю, естественно...
- после компиляции ядра и при написании кода модулей ядра - структура struct proc_dir_entry вашему коду неизвестна, и адресоваться в ней к полям структуры - нельзя!
... т.е. подсмотреть в исходниках ядра как там сейчас выглядит struct proc_dir_entry (а выглядит она не так, как в более ранних ядрах) вы можете, а вот использовать это в коде - не можете.

Это как если бы вы в каком-то своём коде приложения писали что-то такое:
- файл XXX.h (и это доступно потребителю в исходниках заголовочных файлов)

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

struct proc_dir_entry;
extern struct proc_dir_entry *proc_mkdir( const char *, struct proc_dir_entry * );
- файл XXX.с (а это откомпилировалось, и пропало для потребителя, вместе с исходным кодом реализации)

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

struct proc_dir_entry {
...
};
struct proc_dir_entry *proc_mkdir( const char *name, struct proc_dir_entry *parent ){
...
};
Это, конечно, похоже на объектно-ориентированное программирование, на инкапсуляцию в C++ ... но доставит ещё неприятностей разработчикам модулей ядра!

Ответить

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

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

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