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

Часть 29. Система /sys. Cоздание модуля, использующего возможности /sys

Comments

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

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

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

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

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

В этой статье мы на практике рассмотрим вопросы использования системы /sys при разработке модулей ядра.

Интерфейс /sys

Попробуем создать модуль, осуществляющий чтение и запись из атрибута-имени, им же самим созданного в системе /sys. Большая часть действий, выполняемых этим модулем, нам уже известна из примеров, в которых для аналогичных целей использовались системы /dev и /proc. Однако необходимо рассмотреть процесс регистрации имён в /sys. Исходный код модуля можно найти в файле ххх.с в архиве sys.tgz в разделе «Материалы для скачивания».

Листинг 1. Создание путевого имени в /sys.
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/parport.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/version.h>

#define LEN_MSG 160
static char buf_msg[ LEN_MSG + 1 ] = "Hello from module!\n";

/* <linux/device.h>
LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
struct class_attribute {
   struct attribute attr;
   ssize_t (*show)(struct class *class, struct class_attribute *attr, char *buf);
   ssize_t (*store)(struct class *class, struct class_attribute *attr,
                    const char *buf, size_t count);
};
LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
struct class_attribute {
   struct attribute attr;
   ssize_t (*show)(struct class *class, char *buf);
   ssize_t (*store)(struct class *class, const char *buf, size_t count);
};
*/

/* Метод show() из sysfs API.
   Вызываетcя при вызове метода метод show() конкретного файла в системе sysfs */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
static ssize_t x_show( struct class *class, 
                       struct class_attribute *attr, char *buf ) {
#else
static ssize_t x_show( struct class *class, char *buf ) {
#endif
   strcpy( buf, buf_msg );
   printk( "read %d\n", strlen( buf ) );
   return strlen( buf );
}

/* Метод store() из sysfs API.
   Вызываетcя при вызове метода store() конкретного файла в системе sysfs */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
static ssize_t x_store( struct class *class, struct class_attribute *attr,
                        const char *buf, size_t count ) {
#else
static ssize_t x_store( struct class *class, const char *buf, size_t count ) {
#endif
   printk( "write %d\n" , count );
   strncpy( buf_msg, buf, count );
   buf_msg[ count ] = '\0';
   return count;
}

/* <linux/device.h>
#define CLASS_ATTR(_name, _mode, _show, _store) \
struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store) */
CLASS_ATTR( xxx, 0666, &x_show, &x_store );

static struct class *x_class;

int __init x_init( void ) {
   int res;
   x_class = class_create( THIS_MODULE, "x-class" );
   if( IS_ERR( x_class ) ) printk( "bad class create\n" );
   res = class_create_file( x_class, &class_attr_xxx );
/* <linux/device.h>
extern int __must_check class_create_file(
             struct class *class, const struct class_attribute *attr); */
   printk( "'xxx' module initialized\n" );
   return 0;
}

void x_cleanup( void ) {
/* <linux/device.h>
extern void class_remove_file(struct class *class, const struct class_attribute *attr); */
   class_remove_file( x_class, &class_attr_xxx );
   class_destroy( x_class );
   return;
}

module_init( x_init );
module_exit( x_cleanup );
MODULE_LICENSE( "GPL" );

В коде показанного модуля выполняются следующие действия. Во-первых, создаётся класс struct class (и соответствующая ему структура struct kobject), отображаемый вызовом class_create() в создаваемом каталоге с именем "x-class". Далее создаётся переменная class_attr_xxx, содержащая атрибутную запись типа struct class_attribute.

Эта переменная создаётся макросом CLASS_ATTR(), поэтому имя переменной образуется (текстовой конкатенацией) из параметра макровызова. Поначалу это может казаться необычным, но все операции, связанные с /sys, выполняются подобным образом. Этот же макровызов определяет и имена (адреса) функций обработчиков show() и store() для этого атрибута.

Созданная атрибутная запись увязывается с ранее созданным классом вызовом class_create_file(). Именно на этом этапе будет создано файловое имя в каталоге "x-class". Само имя, под которым будет отображаться файл, определилось на предыдущем шаге при вызове CLASS_ATTR(). Функция обработчик show() будет вызываться при выполнении запросов на чтение файлового имени /sys/class/x-class/xxx, а store(), соответственно, на запись.

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

При таком групповом создании имена обрабатывающих функций (3-й и 4-й параметры макровызовов CLASS_ATTR()) также формируются как конкатенация параметров вызова. Другой практикой уточнения, к какому атрибуту относится запрос, является динамический анализ параметра attr, полученного функциями show() и store() (правда эта возможность доступна только в версиях ядра, начиная с 2.6.32).

Отличительной особенностью, проявляющейся при работе с подсистемой /sys, является высокая волатильность API, доступного для использования в коде: прототипы функций, структуры данных, макроопределения и всё прочее. Это подчёркивается тем, что в закомментированных фрагментах листинга 1 приведены определения, которые могут различаться в разных версиях ядра (определения взяты из заголовочных файлов).

Теперь можно проверить созданный модуль:

$ sudo insmod xxx.ko 
$ lsmod | head -n2 
Module                  Size  Used by 
xxx                     1047  0 
$ ls -lR /sys/class/x-class 
/sys/class/x-class: 
итого 0 
-rw-rw-rw- 1 root root 4096 Янв 27 23:34 xxx 
$ tree /sys/class/x-class 
/sys/class/x-class 
└── xxx 
0 directories, 1 file 
$ ls -l /sys/module/xxx/ 
итого 0 
drwxr-xr-x 2 root root    0 Янв 27 23:57 holders 
-r--r--r-- 1 root root 4096 Янв 27 23:57 initstate 
drwxr-xr-x 2 root root    0 Янв 27 23:57 notes 
-r--r--r-- 1 root root 4096 Янв 27 23:57 refcnt 
drwxr-xr-x 2 root root    0 Янв 27 23:57 sections 
-r--r--r-- 1 root root 4096 Янв 27 23:57 srcversion 
$ dmesg | tail -n18 | grep -v ^audit 
'xxx' module initialized 
$ cat /sys/class/x-class/xxx 
Hello from module! 
$ dmesg | tail -n15 | grep -v ^audit 
read 19

Как можно убедиться по форме вывода, операция чтения со стороны пользователя действительно выполняется функцией show(), и названия функций show() и store() отражают направления передачи данных именно со стороны внешнего наблюдателя (из пространства пользователя). Для разработчика кода модуля они носят в точности противоположный смысл. Рассмотрим другие операции.

$ echo это новое содержимое > /sys/class/x-class/xxx 
$ cat /sys/class/x-class/xxx 
это новое содержимое 
$ dmesg | tail -n10 | grep -v ^audit 
write 39 
read 39 
$ sudo rmmod xxx

В случае, когда необходимо создать целую группу файловых имён, связанных с модулем, поведение которых сходно и отличается лишь деталями (например, привязкой к различным переменным внутри модуля), для определения функций show() и store() обычно используют параметризуемые макроопределения. Хотя полученный в результате код крайне трудно сопровождать, подобный подход, тем не менее, используется повсеместно. Для конкретизации сказанного перепишем предыдущий модуль так, чтобы он создавал три независимых точки входа. В листинге 2 показаны только отличия от предыдущего примера, а полный код можно найти в файле xxm.c в архиве sys.tgz.

Листинг 2. Создание обширных групп имён
...
#define LEN_MSG 160 

// определения функций обработчиков
#define IOFUNCS( name )                                                         \ 
static char buf_##name[ LEN_MSG + 1 ] = "не инициализировано "#name"\n";        \ 
static ssize_t SHOW_##name( struct class *class, struct class_attribute *attr,  \ 
                            char *buf ) {                                       \ 
   strcpy( buf, buf_##name );                                                   \ 
   printk( "read %d\n", strlen( buf ) );                                        \ 
   return strlen( buf );                                                        \ 
}                                                                               \ 
static ssize_t STORE_##name( struct class *class, struct class_attribute *attr, \ 
                             const char *buf, size_t count );                   \ 
   printk( "write %d\n", count );                                               \ 
   strncpy( buf_##name, buf, count );                                           \ 
   buf_##name[ count ] = '\0';                                                  \ 
   return count;                                                                \ 
} 
IOFUNCS( data1 ); 
IOFUNCS( data2 ); 
IOFUNCS( data3 ); 

// определение атрибутных записей
#define OWN_CLASS_ATTR( name ) \ 
struct class_attribute class_attr_##name = \ 
__ATTR( name, 0666, &SHOW_##name, &STORE_##name ) 
static OWN_CLASS_ATTR( data1 ); // создаётся class_attr_data1
static OWN_CLASS_ATTR( data2 ); // создаётся class_attr_data2
static OWN_CLASS_ATTR( data3 ); // создаётся class_attr_data3
...

int __init x_init(void) { 
   ...
   res = class_create_file( x_class, &class_attr_data1 ); 
   res = class_create_file( x_class, &class_attr_data2 ); 
   res = class_create_file( x_class, &class_attr_data3 ); 
   ...
} 

void x_cleanup(void) { 
   class_remove_file( x_class, &class_attr_data1 ); 
   class_remove_file( x_class, &class_attr_data2 ); 
   class_remove_file( x_class, &class_attr_data3 ); 
   ...
}

В подобных конструкциях могут создаваться весьма обширные коллекции файловых имён, но в обсуждаемом примере создаётся всего три имени.

$ sudo insmod xxm.ko 
$ tree /sys/class/x-class 
/sys/class/x-class 
├── data1 
├── data2 
└── data3 
0 directories, 3 files 
$ cat /sys/class/x-class/data1 
не инициализировано data1 
$ cat /sys/class/x-class/data2 
не инициализировано data2 
$ cat /sys/class/x-class/data3 
не инициализировано data3 
$ echo строка 1 > /sys/class/x-class/data1 
$ echo строка 2 > /sys/class/x-class/data2 
$ echo строка 3 > /sys/class/x-class/data3 
$ cat /sys/class/x-class/data1 
строка 1 
$ cat /sys/class/x-class/data2 
строка 2 
$ cat /sys/class/x-class/data3 
строка 3

Заключение

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


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


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=840726
ArticleTitle=Разработка модулей ядра Linux: Часть 29. Система /sys. Cоздание модуля, использующего возможности /sys
publish-date=10162012