Разработка модулей ядра Linux

Часть 27. Система /proc. Особенности использования

Comments

Серия контента:

Этот контент является частью # из серии # статей: Разработка модулей ядра Linux

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Разработка модулей ядра Linux

Следите за выходом новых статей этой серии.

В этой статье мы продолжаем знакомиться с возможностями использования системы /proc для разработки модулей ядра. Использование /proc предоставляет новые возможности для управления модулем и позволяет отказаться от устаревшего подхода с использованием вызовов ioctl().

Модули, использующие /proc

Следующий пример (файл mod_procr2.c в архиве proc.tgz в разделе «Материалы для скачивания») делает в точности то же самое, что и модуль из предыдущей статьи, но использует для этого более простой вызов create_proc_read_entry(). Этот вызов просто скрывает суть происходящего, так как является inline-оболочкой для create_proc_entry(), и определён в файле <linux/proc_fs.h>.

static inline struct proc_dir_entry *create_proc_read_entry( 
   const char *name, mode_t mode, struct proc_dir_entry *base,
   read_proc_t *read_proc, void * data ) {
...
}

Вот так выглядит упрощённая запись модуля (изменения коснулись только функции инициализации).

Листинг 1. Упрощённая запись предыдущего модуля.
#include "mod_proc.h"

ssize_t proc_node_read( char *buffer, char **start, off_t off,
                        int count, int *eof, void *data ) {
// ... аналогично предыдущему примеру ...
};

static int __init proc_init( void ) {                                       
   if( create_proc_read_entry( NAME_NODE, 0, NULL, proc_node_read, NULL ) == 0 ) {
      printk( KERN_ERR "can't create /proc/%s\n", NAME_NODE );
      return -ENOMEM;
   }
   printk( KERN_INFO "module : success!\n");
   return 0;
}

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

Испытаем полученный модуль:

$ sudo insmod mod_procr2.ko
$ cat /proc/mod_node
Hello from module!

Третий пример (файл mod_proc.c в архиве proc.tgz) показывает модуль, создающий в /proc имя, которое может и читаться, и писаться. На этот раз мы используем не специальный вызов (типа read_proc_t), а точки входа таблицы указателей файловых операций для этого имени (аналогично тому, как это делалось в драйверах интерфейса /dev).

Листинг 2. Модуль, использующий таблицу файловых операций.
#include "mod_proc.h"

static ssize_t node_read( struct file *file, char *buf,
                          size_t count, loff_t *ppos ) {
   static int odd = 0;
   printk( KERN_INFO "read: %d\n", count );
   if( 0 == odd ) {
      int res = copy_to_user( (void*)buf, &buf_msg, strlen( buf_msg ) );
      odd = 1;
      //buf — это адресное пространство пользователя 
      put_user( '\n', buf + strlen( buf_msg ) );
      res = strlen( buf_msg ) + 1;
      printk( KERN_INFO "return bytes :  %d\n", res );
      return res;
   }
   odd = 0;
   printk( KERN_INFO "EOF\n" );
   return 0;
}

static ssize_t node_write( struct file *file, const char *buf,
                           size_t count, loff_t *ppos ) {
   int res, len = count < LEN_MSG ? count : LEN_MSG;
   printk( KERN_INFO "write: %d\n", count );
   res = copy_from_user( &buf_msg, (void*)buf, len );
   if( '\n' == buf_msg[ len -1 ] ) buf_msg[ len -1 ] = '\0';
   else buf_msg[ len ] = '\0';
   printk( KERN_INFO "put bytes = %d\n", len );
   return len;
}

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

static int __init proc_init( void ) {
   int ret; 
   struct proc_dir_entry *own_proc_node; 
   own_proc_node = create_proc_entry(NAME_NODE, S_IFREG | S_IRUGO | S_IWUGO, NULL); 
   if( NULL == own_proc_node ) {
      ret = -ENOMEM;
      printk( KERN_ERR "can't create /proc/%s\n", NAME_NODE );
      goto err_node;
   }
   own_proc_node->uid = 0;
   own_proc_node->gid = 0;
   own_proc_node->proc_fops = &node_fops;
   printk( KERN_INFO "module : success!\n");
   return 0;
err_node:
   return ret;
}

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

Обратите внимание, функция чтения node_read() в этом случае принципиально отличается от аналогичной функции с тем же именем в предыдущих примерах: не только своей реализацией, но и прототипом вызова, и тем, как она возвращает свои результаты. Проверим созданный модуль.

$ sudo insmod mod_proc.ko
$ ls -l /proc/mod_*
-rw-rw-rw- 1 root root 0 Июл  2 20:47 /proc/mod_node 
$ dmesg | tail -n1
module : success! 
$ cat /proc/mod_node
Hello from module!
$ echo новая строка >  /proc/mod_node 
$ cat /proc/mod_node 
новая строка 
$ cat /proc/mod_node 
новая строка 
$ dmesg | tail -n10
write: 24 
put bytes = 24 
read: 32768 
return bytes :  24 
read: 32768 
EOF 
read: 32768 
return bytes :  24 
read: 32768 
EOF 
$ sudo rmmod mod_proc 
$ cat  /proc/mod_node 
cat: /proc/mod_node: Нет такого файла или каталога

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

Предположим, что требуется создать в /proc не отдельное имя, а собственную иерархию имён, как у любого системного каталога:

$ tree /proc/driver 
/proc/driver 
├── nvram 
├── rtc 
└── snd-page-alloc 
0 directories, 3 files

Для этого потребуется только слегка расширить функцию инициализации предыдущего модуля и привести в соответствие с ней функцию выгрузки. Подобный пример можно найти в файле mod_proct.c в архиве proc.tgz. Таким образом можно создать иерархию сколь угодно высокой сложности и любой глубины вложенности (в листинге 3 показана только изменённая часть предыдущего примера):

Листинг 3. Модуль, создающий иерархию имён в /proc.
...
static struct proc_dir_entry *own_proc_dir; 

static int __init proc_init( void ) { 
   int ret; 
   struct proc_dir_entry *own_proc_node; 
   own_proc_dir = create_proc_entry( NAME_DIR, S_IFDIR | S_IRWXUGO, NULL ); 
   if( NULL == own_proc_dir ) { 
      ret = -ENOMEM; 
      printk( KERN_ERR "can't create /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 = -ENOMEM; 
      printk( KERN_ERR "can't create /proc/%s\n", NAME_NODE ); 
      goto err_node; 
   } 
   own_proc_node->uid = own_proc_node->gid = 0; 
   own_proc_node->proc_fops = &node_fops; 
   printk( KERN_INFO "module : success!\n"); 
   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  ); 
   printk(KERN_INFO "/proc/%s removed\n", NAME_NODE ); 
}

Обратите внимание на то, как просто создать в /proc имя, соответствующее каталогу или файлу. Результат зависит от выбора единственного бита в флагах создания: S_IFDIR или S_IFREG. Как обычно протестируем полученный модуль.

$ sudo insmod ./mod_proct.ko 
$ cat /proc/modules | grep mod_ 
mod_proct 1454 0 - Live 0xf8722000 
$ ls -l /proc/mod* 
-r--r--r-- 1 root root 0 Июл  2 23:24 /proc/modules 
/proc/mod_dir: 
итого 0 
-rw-rw-rw- 1 root root 0 Июл  2 23:24 mod_node 
$ tree /proc/mod_dir 
/proc/mod_dir 
└── mod_node 
0 directories, 1 file 
$ cat /proc/mod_dir/mod_node 
Hello from module! 
$ echo 'new string' > /proc/mod_dir/mod_node 
$ cat /proc/mod_dir/mod_node 
new string 
$ sudo rmmod mod_proct

Так как API ядра предоставляет два альтернативных набора функций для реализации ввода вывода, то возникает закономерный вопрос: какая функция из этих двух альтернатив будет отрабатываться (имеет приоритет), если определены обе? Для ответа был сделан модуль mod_2.c (он является линейной комбинацией показанных примеров, и поэтому не приводится, но включён в архив proc.tgz), и вот что показывает его использование:

$ sudo insmod ./mod_2.ko 
$ cat /proc/mod_node 
функция из таблицы файловых операций

Заключение

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


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=838698
ArticleTitle=Разработка модулей ядра Linux: Часть 27. Система /proc. Особенности использования
publish-date=10022012