Содержание


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

Часть 26. Система /proc. Обзор возможностей и создание модуля

Comments

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

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

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

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

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

Возможность обмена информацией через именованные псевдо-файлы в /proc и /sys не является исключительной особенностью драйверов, поэтому ей могут воспользоваться и модули, не осуществляющие поддержку устройств, и само монолитное ядро Linux, например, для определения установленных уровней диагностических вызовов ядра:

$ cat /proc/sys/kernel/printk 
8	4	1	7

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

Интерфейсы /proc и /sys

Интерфейс к файловым именам /proc (файловая система procfs) и более поздний интерфейс к именам /sys (файловая система sysfs) рассматриваются как канал передачи диагностической (из) и управляющей (в) информации для модуля. Такой способ взаимодействия с модулем может полностью заменить вызов ioctl(), который уже считается устаревшим и потенциально опасным (из-за отсутствия контроля типизации).

В настоящее время сложилась тенденция переносить многие управляющие функции из /proc в /sys, так как отображения путевых имён модулей в эти две подсистемы во многом совпадают по назначению и возможностям. Содержимое имён-псевдофайлов в обоих системах является только текстовым отображением некоторых внутренних данных ядра. Но следует учесть и ряд отличий между /proc и /sys:

  • Файловая система /proc является стандартной принадлежностью всех UNIX систем (Free/Open/Net BSD, Solaris, и т.д.), а её наличие и принципы использования оговариваются стандартом POSIX 2; а файловая система /sys является «порождением» Linux и используется только в этой системе.
  • Так сложилось, что немногочисленные диагностические файлы в /proc содержат большие объёмы текстовой информации (часто оформленные как таблицы), в то время, как в /sys присутствует гораздо больше имён, но каждое из них содержит информацию только об единичном значении (в символьном представлении!), часто соответствующем одной элементарной переменной языка C: int, long, и т.д.

Выполним обращение к информации, хранящейся в /proc:

$ cat /proc/cpuinfo
processor	: 0 
vendor_id	: GenuineIntel 
cpu family	: 6 
...
$ wc -l /proc/cpuinfo 
58 cpuinfo

В этом файле содержится информация о процессорах системы в количестве 58 строк текста. А вот образец информации, полученной из системы /sys.

$ tree /sys/module/cpufreq 
/sys/module/cpufreq 
└── parameters 
    ├── debug 
    └── debug_ratelimit 
1 directory, 2 files 
$ cat /sys/module/cpufreq/parameters/debug
0 
$ cat /sys/module/cpufreq/parameters/debug_ratelimit 
1

Различия в формате представления информации, используемом в той или иной файловой системе, породили часто встречающееся заблуждение, что интерфейс в /proc предназначен только для чтения, а интерфейс /sys для чтения и записи. Это совершенно неверно, так как оба интерфейса допускают и чтение, и запись.

После краткого обзора свойств и возможностей управляющих интерфейсов мы перейдем к примерам кода модулей, реализующих эти интерфейсы, и первым будет рассмотрен интерфейс /proc.

Модуль, использующий возможности /proc

Интерфейс /proc рассматривается на примерах из архива proc.tgz, который находится в разделе «Материалы для скачивания». Так как мы будем использовать несколько однотипных модулей, то общую часть определений следует вынести в отдельный файл, как показано в листинге 1.

Листинг 1. Общие определения модулей.
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <asm/uaccess.h>

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

static int  __init proc_init( void );      // предварительные определения
static void __exit proc_exit( void );
module_init( proc_init );
module_exit( proc_exit );

#define NAME_DIR  "mod_dir"
#define NAME_NODE "mod_node"
#define LEN_MSG 160                        // длина буфера и сам буфер обмена
static char buf_msg[ LEN_MSG + 1 ] = "Hello from module!";

Основную работу по созданию и уничтожению имени в /proc выполняет пара вызовов, определенных в файле <linux/proc_fs.h>.

struct proc_dir_entry *create_proc_entry( const char *name, mode_t mode, 
                                          struct proc_dir_entry *parent ); 
void remove_proc_entry( const char *name, struct proc_dir_entry *parent );

В результате создаётся сложная структура, особый интерес в которой для нас представляют поля, приведенные ниже:

struct proc_dir_entry { 
...
        const char *name; 
        mode_t mode; 
...
        uid_t uid; 
        gid_t gid; 
...
        const struct file_operations *proc_fops; 
...
        read_proc_t *read_proc; 
        write_proc_t *write_proc; 
...
};

Смысл всех перечисленных полей станет понятным при рассмотрении примеров модулей. Отметим только наличие в структуре полей read_proc и write_proc, а одновременно с ними уже известной нам таблицы файловых операций proc_fops. Исходя из этого можно предположить, что для описания операций ввода-вывода для /proc существуют два альтернативных способа. И это предположение окажется правдой!

В первом примере в файле mod_procr.c показывается создание интерфейса к модулю в /proc, который будет доступен из пользовательских программ в режиме «только для чтения», так как этот вариант использования встречается чаще всего.

Листинг 2. Модуль, создающий имя в /proc
#include "mod_proc.h"

// в точности списан прототип read_proc_t из <linux/proc_fs.h>:
ssize_t proc_node_read( char *buffer, char **start, off_t off,
                        int count, int *eof, void *data ) {
   static int offset = 0, i;
   printk( KERN_INFO "read: %d\n", count );
   for( i = 0; offset <= LEN_MSG && '\0' != buf_msg[ offset ]; offset++, i++ )
      *( buffer + i ) = buf_msg[ offset ];       // buffer не в пространстве пользователя!
   *( buffer + i ) = '\n';                       // дополним переводом строки
   i++;
   if( offset >= LEN_MSG || '\0' == buf_msg[ offset ] ) {
      offset = 0;
      *eof = 1;                                  // возвращаем признак EOF
   }
   else *eof = 0;
   printk( KERN_INFO "return bytes: %d\n", i );
   if( *eof != 0 ) printk( KERN_INFO "EOF\n" );
   return i;
};
// в литературе утверждается, что для /proc нет API записи, аналогично API чтения,
// но сейчас в <linux/proc_fs.h> есть описание типа (аналогичного типу read_proc_t)
// typedef int (write_proc_t)( struct file *file, const char __user *buffer,
//                             unsigned long count, void *data );

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->read_proc = proc_node_read;
   printk( KERN_INFO "module : success!\n");
   return 0;
   err_node:       // обычная для модулей практика использования goto по ошибке
   return ret; 
   }

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

Флаги прав доступа к файлу вида S_I* (известные и из пользовательского API) можно найти в файле <linux/stat.h>. Отметим только, что название структуры struct proc_dir_entry, создаваемой при регистрации имени функцией create_proc_entry(), не должно вводить в заблуждение: это не обязательно каталог. Будет ли в результате вызова создан каталог или терминальное файловое имя определяется значением флагов (параметр mode). 3-ий параметр вызова parent - это ранее созданный родительский каталог в /proc, внутри которого и будет создано имя, если же он равен NULL, то имя будет создано непосредственно в /proc. Всё это хорошо видно в коде примеров.

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

$ sudo insmod ./mod_procr.ko
$ dmesg | tail -n1
module : success!
$ ls -l /proc/mod_*
-r--r--r-- 1 root root 0 Мар 26 18:14 /proc/mod_node
$ cat /proc/mod_node
Hello from module!
$ dmesg | tail -n7
module : success!
read: 3072
return bytes: 19
EOF
read: 3072
return bytes: 19
EOF

Обратите внимание на характерную длину блока чтения в этой реализации - 3072, отличающуюся от той (32767), которая запрашивается при чтении через таблицу файловых операций. Это говорит о том, что чтение обеспечивается функцией read_proc() в созданной структуре struct proc_dir_entry. Эта функция имеет совершенно другой прототип (чем в таблице файловых операций), по-другому возвращает признак конца файла (потока данных) и использует в качестве выходного буфера адрес пространства ядра, откуда данные в пространство пользователя будут скопированы позже средствами системы. Это принципиальные различия существующих альтернатив, которые назывались выше.

Примечание: В прилагаемом архиве представлен каталог ./variant, (помимо основного ./proc), в котором показаны несколько различных вариантов вызова функции read_proc_t. Различия между этими вариантами довольно существенны, но их детальное обсуждение потребовало бы дополнительного объёма изложения, поэтому они могут быть рассмотрены на предлагаемых в каталоге примерах. Там же в примере показана и реализация операции записи в модуль посредством функции write_proc_t, которая (в отличие от read_proc_t) очень похожа на обсуждающуюся в следующей статье операцию записи, реализуемую через таблицу файловых операций.

Заключение

В этой статье был представлен краткий обзор возможностей и особенностей систем /proc и /sys и, как всегда в рамках нашего цикла, исходный код модуля, выполняющего регистрацию устройства в системе /proc. В следующей статье мы продолжим изучение возможностей системы /proc с точки зрения разработчика модулей ядра.


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


Похожие темы


Комментарии

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

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