Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux
Назад Внешние интерфейсы модуля Вперед

Интерфейс /sys

Одно из главных «приобретений» ядра, начинающееся от версий 2.6 — это появление единой унифицированной модель представления устройств в Linux. Главные составляющие, сделавшие возможным её существование, это файловая система sysfs и дуальный к ней (поддерживаемый ею) пакет пользовательского пространства udev. Модель устройств— это единый механизм для представления устройств и описания их топологии в системе. Декларируется множество преимуществ, которые обусловлены созданием единого представления устройств:

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

Файловая система sysfs возникла первоначально из нужды поддерживать последовательность действий в динамическом управлении электропитанием (иерархия устройств при включении-выключении) и для поддержки горячего подключения устройств (то есть в обеспечение последнего пункта перечисления). Но позже модель оказалась гораздо плодотворнее. Сама по себе эта система является весьма сложной и объёмной, и о ней одной можно и нужно писать отдельную книгу. Но в контексте нашего рассмотрения нас интересует, в первую голову, возможность создания интерфейса из модуля к файловым именам, в файловой системе /sys. Эта возможность весьма напоминает то, как модуль создаёт файловые имена в подсистеме /proc.

Базовым понятием модели представления устройств являются объекты struct kobject (определяется в файле <linux/kobject.h>). Тип struct kobjec по смыслу аналогичен абстрактному базовому классу Object в объектно-ориентированных языков программирования, как С# и Java. Этот тип определяет общую функциональность, такую как счетчик ссылок, имя, указатель на родительский объект, что позволяет создавать объектную иерархию.

Зачастую объекты struct kobject сами по себе не создаются и не используются, они встраиваются в другие структуры данных, после чего те приобретают свойства, присущие struct kobject, например, такие, как встраиваемость в иерархию объектов. Вот как это выражается в определении уже известной нас структуры представления символьного устройства:

	   struct cdev { 
	      struct kobject          kobj; 
	      struct module          *owner; 
	      struct file_operations *ops; 
	      ...
	   };

Во внешнем представлении в /sys, в интересующем нас смысле, каждому объекту struct kobject соответствует каталог, что видно и из самого определения:

	   struct kobject { 
	   ...
	      struct kobj_type *ktype;
	      struct dentry    *dentry; 
	   };

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

int kobject_add( struct kobject*kobj );

Но это не придётся делать явно нам в примерах ниже, просто по той простой причине, что используемые для регистрации имён в /sys высокоуровневые вызовы API (class_create()) делают это за нас.

Таким образом, объекты struct kobject естественным образом отображаются в каталоги пространства имён /sys. Файловая система sysfs это дерево каталогов без файлов. А как создать файлы в этих каталогах, в содержимое которых отображаются данные ядра? Каждый объект struct kobject (каталог) содержит (через свой компонент struct kobj_type) массив структур struct attribute :

	   struct kobj_type {
	   ...
	      struct sysfs_ops *sysfs_ops;
	      struct attribute **default_attrs;
	   }

Вот каждая такая структура (определена в <linux/sysfs.h>) и является определением одного файла, содержащегося в рассматриваемом каталоге:

	   struct attribute {
	   ...
	      char   *name        /* имя атрибута-файла */;
	      mode_t  mode struct /* права доступа к файлу */;
	   }

А показанная там же структура таблицы операций (struct sysfs_ops) содержит два поля — определения функций show(...) и store(...), соответственно, чтения и записи символьного поля данных ядра, отображаемых этим файлом (и сами функции и их прототипы показаны в примере ниже).



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

  • информация терминальных имён /proc — комплексная, обычно содержит большие объёмы текстовой информации, иногда это таблицы, и даже с заголовками, проясняющими смысл столбцов таблицы;
  • информацию терминальных имён /sys (атрибутов) рекомендуется оформлять в виде а). простых, б). символьных значений, в). представляющих значения, соответствующие скалярным типам данных языка C (int, long, char[]);

Сравним:

$ cat /proc/partitions | head -n5

	major minor  #blocks  name
	  33     0   10022040 hde
	  33     1    3783276 hde1
	  33     2          1 hde2

$ cat /sys/devices/audio/dev

14:4

$ cat /sys/bus/serio/devices/serio0/set

2

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

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

xxx.c :

	#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);
	};
	*/
	
	/* sysfs show() method. Calls the show() method corresponding to the individual sysfs file */
	#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 );
	}
	
	/* sysfs store() method. Calls the store() method corresponding to the individual sysfs file */
	#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" );

Примечание: В первых строках кода (в виде комментариев) приведены варианты определений (взято из хэдер-файлов), отличающихся даже не между версиями ядра, а между близкими подверсиями ядра: код проверялся в версиях 2.6.32 и 2.6.35 - это лишний раз говорит о волатильности API ядра, и, особенно, ещё не устоявшейся подсистемы sysfs.

Тестируем код:

$ sudo insmod ./xxx.ko

$ ls -l /sys/class/x-class

-rw-rw-rw- 1 root root 4096 Мар 30 21:54 xxx

$ ls -lR /sys/class/x-class

/sys/class/x-class:

-rw-rw-rw- 1 root root 4096 Мар 30 21:54 xxx

$ cat /sys/class/x-class/xxx

Hello from module!

$ echo 12345 > /sys/class/x-class/xxx

$ cat /sys/class/x-class/xxx

12345

$ cat /sys/class/x-class/xxx

12345

$ ls /sys/module/xxx/

holders initstate notes refcnt sections srcversion

$ sudo rmmod xxx

$ cat /sys/class/x-class/xxx

cat: /sys/class/x-class/xxx: Нет такого файла или каталога

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


Предыдущий раздел: Оглавление Следующий раздел:
Интерфейс /proc   Сеть