Библиотека сайта rus-linux.net
| Цилюрик О.И. Модули ядра Linux | ||
| Назад | Внешние интерфейсы модуля | Вперед |
Интерфейс /proc
Интерфейс к файловым именам /proc (procfs) и более поздний интерфейс к именам /sys (sysfs) рассматривается как канал передачи диагностической (из) и управляющей (в) информации для модуля. Такой способ взаимодействия с модулем может полностью заменить средства вызова ioctl() для устройств, который устаревший и считается опасным. В настоящее время сложилась тенденция многие управляющие функции переносить их /proc в /sys, отображения путевых имён модулем в эти две подсистемы по своему назначению и возможностям являются очень подобными. Содержимое имён-псевдофайлов в обоих системах является только текстовым отображением некоторых внутренних данных ядра. Но нужно иметь в виду и ряд отличий между ними:
- Файловая система /proc является общей, «родовой» принадлежностью всех UNIX систем (Free/Open/Net BSD, Solaris, QNX, MINIX 3, ...), её наличие и общие принципы использования оговариваются стандартом POSIX 2; а файловая система /sys является сугубо Linux «изоретением» и используется только этой системой.
- Так сложилось по традиции, что немногочисленные диагностические файлы в /proc содержат зачастую большие таблицы текстовой информации, в то время, как в /sys создаётся много больше по числу имён, но каждое из них даёт только информацию об ограниченном значении, часто соответствующем одной элементарной переменной языка C: int, long, ...
Сравним:
$ cat /proc/cpuinfo
processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 14 model name : Genuine Intel(R) CPU T2300 @ 1.66GHz stepping : 8 cpu MHz : 1000.000 ...
$ wc -l 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.tgz. Мы будем собирать несколько однотипных модулей, поэтому общую часть определений снесём в отдельный файл:
mod_proc.h :
#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!";
Файл сборки общий для всех модулей :
Makefile :
CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) DEST = /lib/modules/$(CURRENT)/misc EXTRA_CFLAGS += -std=gnu99 TARGET1 = mod_procr TARGET2 = mod_procr2 TARGET3 = mod_proc TARGET4 = mod_proct obj-m := $(TARGET1).o $(TARGET2).o $(TARGET3).o $(TARGET4).o default: $(MAKE) -C $(KDIR) M=$(PWD) modules ...
Основную работу по созданию и уничтожению имени в /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;
...
};
Смысл всех этих полей станет понятным без объяснений из рассмотрения примеров построения модулей.
Первый пример (архив proc.tgz) показывает создание интерфейса к модулю в /proc доступного только для чтения из пользовательских программ (наиболее частый случай):
mod_procr.c :
#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* - ищите и заимствуйте в <linux/stat.h>.
Испытания:
$ make
...
$ 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
Примечание: Обратите внимание на характерную длину блока чтения в этой реализации, она будет отличаться в последующих реализациях.
Несколько последовательно выполняемых операций:
$ cat /proc/mod_node
Hello from module!
$ cat /proc/mod_node
Hello from module!
$ cat /proc/mod_node
Hello from module!
$ sudo rmmod mod_procr
$ ls -l /proc/mod_*
ls: невозможно получить доступ к /proc/mod_*: Нет такого файла или каталога
Второй пример делает то же самое, но более простым и более описанным в литературе способом create_proc_read_entry() (но этот способ просто скрывает суть происходящего, но делает в точности то же самое):
mod_procr2.c :
#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 );
}
Примечание (важно!): create_proc_read_entry() пример того, что API ядра, доступный программисту, намного шире, чем список экспортируемых имён в /proc/kallsyms или /boot/System.map-2.6.*, это происходит за счёт множества inline определений (как и в этом случае):
$ cat /proc/kallsyms | grep create_proc_
c0522237 T create_proc_entry
c0793101 T create_proc_profile
$ cat /proc/kallsyms | grep create_proc_read_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 ) {
...
}
Возвращаемся к испытаниям полученного модуля:
$ sudo insmod ./mod_procr2.ko
$ echo $?
0
$ cat /proc/mod_node
Hello from module!
$ cat /proc/mod_node
Hello from module!
$ sudo rmmod mod_procr2
$ cat /proc/mod_node
cat: /proc/mod_node: Нет такого файла или каталога
Третий пример показывает модуль, который создаёт имя в /proc, которое может и читаться и писаться; для этого используется не специальный вызов (типа read_proc_t), а структура указатели файловых операций в таблице операций (аналогично тому, как это делалось в драйверах интерфейса /dev):
mod_proc.c :
#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;
put_user( '\n', buf + strlen( buf_msg ) ); // buf — это адресное пространство пользователя
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 :
...
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
| Предыдущий раздел: | Оглавление | Следующий раздел: |
| Блочные устройства | Интерфейс /sys |
