чтение-запись данных ядра через /proc
Модератор: Olej
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
чтение-запись данных ядра через /proc
Эта тема переползла вот отсюда: viewtopic.php?f=3&t=1549&start=80#p3065 - +/- 5 дней того обсуждения влево-вправо
Речь идёт о программировании модулей ядра Linux и реализации в этих модулях неоднозначной операции read_proc_t ... а заодно и write_proc_t.
Речь идёт о программировании модулей ядра Linux и реализации в этих модулях неоднозначной операции read_proc_t ... а заодно и write_proc_t.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
Сделал я такой маленький заготовец-архивчик, тестовая площадка для отработки вариантов: Здесь можно добавлять новые варианты read_proc_t, компилируя их как-то так:Olej писал(а):Речь идёт о программировании модулей ядра Linux и реализации в этих модулях неоднозначной операции read_proc_t ... а заодно и write_proc_t.
Код: Выделить всё
$ make VARIANT=2
...
Достаточно любопытно ... и достаточно неожиданно:
1. то, что уже рассматривалось, и из-за прототы я не стал его менять:
Код: Выделить всё
static char msg_short[ LEN_MSG + 1 ] =
".........1.........2.........3.........4.........5.........6\n";
static ssize_t proc_node_read( char *buffer, char **start, off_t off,
int count, int *eof, void *data ) {
int len = 0;
char *buf_msg;
LOG( "read: %d (buffer=%p, off=%ld, start=%p)", count, buffer, off, *start );
#if VARIANT == 0
// тупо копируем весь буфер, пока return не станет <= off
buf_msg = msg_short;
len = strlen( buf_msg );
strcpy( buffer, buf_msg );
LOG( "copy bytes: %d", len );
#endif
LOG( "return bytes: %d%s", len, *eof != 0 ? " ... EOF" : "" );
return len;
};
Код: Выделить всё
[olej@notebook variants]$ ./insm
Module Size Used by
mod_procr_3 1264 0
mod_procr_2 1224 0
mod_procr_1 1228 0
mod_procr_0 1192 0
fuse 48375 2
/proc/mod_node_0 /proc/mod_node_1 /proc/mod_node_2 /proc/mod_node_3
[olej@notebook variants]$ ./mcat 40 mod_node_0
read + 40 bytes, input buffer: .........1.........2.........3.........4
read + 21 bytes, input buffer: .........1.........2.........3.........4.........5.........6
read + 00 bytes, input buffer: .........1.........2.........3.........4.........5.........6
[olej@notebook variants]$ dmesg | tail -n15 | grep !
! read: 40 (buffer=f4042000, off=0, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 40 (buffer=f4042000, off=40, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 19 (buffer=f4042000, off=61, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 40 (buffer=f4042000, off=61, start=(null))
! copy bytes: 61
! return bytes: 61
Здесь вообще никак не сигнализируется конец данных (конец файла) из кода реализации!
Решение о конце данных принимает скрытая реализация в ядре поддержки read_proc_t на основании того, что: возвращаемое значение (return) < (или <=?) чем переданное при вызове ранее значение off!
Совершенно дикая логика реализатора...
Остальные 3 варианта я нарисую чуть позже...
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
Olej писал(а):Остальные 3 варианта я нарисую чуть позже...
Код: Выделить всё
static ssize_t proc_node_read( char *buffer, char **start, off_t off,
int count, int *eof, void *data ) {
int len = 0;
char *buf_msg;
LOG( "read: %d (buffer=%p, off=%ld, start=%p)", count, buffer, off, *start );
#elif VARIANT == 1
// ... на 1 шаг раньше ( return == off ) установлен eof
buf_msg = msg_short;
if( off < strlen( buf_msg ) ) {
len = strlen( buf_msg );
strcpy( buffer, buf_msg );
LOG( "copy bytes: %d", len );
}
*eof = off + count >= strlen( buf_msg ) ? 1 : 0;
#endif
LOG( "return bytes: %d%s", len, *eof != 0 ? " ... EOF" : "" );
return len;
};
Код: Выделить всё
[olej@notebook variants]$ ./mcat 40 mod_node_1
read + 40 bytes, input buffer: .........1.........2.........3.........4
read + 21 bytes, input buffer: .........1.........2.........3.........4.........5.........6
read + 00 bytes, input buffer: .........1.........2.........3.........4.........5.........6
[olej@notebook variants]$ dmesg | tail -n20 | grep !
! read: 40 (buffer=f1e6e000, off=0, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 40 (buffer=f1e6e000, off=40, start=(null))
! copy bytes: 61
! return bytes: 61 ... EOF
! read: 40 (buffer=f1e6e000, off=61, start=(null))
! return bytes: 0 ... EOF
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
Дальше - большеOlej писал(а):Остальные 3 варианта я нарисую чуть позже...
Мы можем для всех последовательных чтений делать копирование всех данных всего 1 раз (это следствие того, что передача результата запроса в userspace делается скрыто ... и это очень плохо!):
Код: Выделить всё
// 1-но копирование всего буфера на 1-м запросе
buf_msg = msg_short;
len = strlen( buf_msg );
if( 0 == off ) {
strcpy( buffer, buf_msg );
LOG( "copy bytes: %d", len );
}
*eof = off + count >= strlen( buf_msg ) ? 1 : 0;
Код: Выделить всё
[olej@notebook variants]$ ./mcat 40 mod_node_2
read + 40 bytes, input buffer: .........1.........2.........3.........4
read + 21 bytes, input buffer: .........1.........2.........3.........4.........5.........6
read + 00 bytes, input buffer: .........1.........2.........3.........4.........5.........6
[olej@notebook variants]$ dmesg | tail -n7 | grep !
! read: 40 (buffer=f242b000, off=0, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 40 (buffer=f242b000, off=40, start=(null))
! return bytes: 61 ... EOF
! read: 40 (buffer=f242b000, off=61, start=(null))
! return bytes: 61 ... EOF
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
И наконец, мы можем копировать на каждом запросе ровно столько, сколько у нас запрашивали, но при этом в return возвращать ... "хитрое значение" ... превышающее off:Olej писал(а):Дальше - большеOlej писал(а):Остальные 3 варианта я нарисую чуть позже...
Код: Выделить всё
// копирование только count байт на каждом запросе - аккуратно меняем return!
buf_msg = msg_short;
if( off >= strlen( buf_msg ) ) {
*eof = 1;
}
else {
int cp;
len = strlen( buf_msg + off );
cp = min( count, len );
strncpy( buffer + off, buf_msg + off, cp );
LOG( "copy bytes: %d", cp );
len = off + cp;
*eof = off + cp >= strlen( buf_msg );
}
Код: Выделить всё
[olej@notebook variants]$ ./mcat 40 mod_node_3
read + 40 bytes, input buffer: .........1.........2.........3.........4
read + 21 bytes, input buffer: .........1.........2.........3.........4.........5.........6
read + 00 bytes, input buffer: .........1.........2.........3.........4.........5.........6
[olej@notebook variants]$ dmesg | tail -n8 | grep !
! read: 40 (buffer=f1e6e000, off=0, start=(null))
! copy bytes: 40
! return bytes: 40
! read: 40 (buffer=f1e6e000, off=40, start=(null))
! copy bytes: 21
! return bytes: 61 ... EOF
! read: 40 (buffer=f1e6e000, off=61, start=(null))
! return bytes: 0 ... EOF
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
1. Во всех вариантах главным решающим действием является правильная подгонка возвращаемого значения ... под то, что придумали разработчики read_proc_t !
И это признак того, насколько отвратительно оно реализовано.
2. И это всё до тех пор, пока общий объм данных, подлежащих передаче, не превышает 3 Kb.
А дальше всё становится ещё хуже...
И это признак того, насколько отвратительно оно реализовано.
2. И это всё до тех пор, пока общий объм данных, подлежащих передаче, не превышает 3 Kb.
А дальше всё становится ещё хуже...
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
Начиная с того, что, наверное, уместно здесь процитировать (чтоб было под рукой) маловнятное описание из "Драйверы устройств Linux, Третья редакция" - http://dmilvdv.narod.ru/Translate/LDD3/ ... rying.html :Olej писал(а): 2. И это всё до тех пор, пока общий объм данных, подлежащих передаче, не превышает 3 Kb.
А дальше всё становится ещё хуже...
Ну и кто здесь что понял?Когда процесс читает из вашего файла в /proc, ядро выделяет страницу памяти (то есть PAGE_SIZE байт), куда драйвер может записать данные для возврата в пространство пользователя. Этот буфер передаётся в вашу функцию, которая представляет собой метод, названный read_proc:
int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);
Указатель page является буфером, куда вы будете записывать ваши данные; start используется функцией, чтобы сказать, где на странице были записаны интересующие данные (подробнее об этом позже); offset и count имеют такое же значение, как и для метода read. Аргумент eof указывает на целое число, которое должно быть установлено драйвером, чтобы сигнализировать, что у него нет больше данных для возвращения, в то время как data является указателем на специфические данные драйвера, которые можно использовать для внутренней бухгалтерии.
Эта функция должна возвращать количество байт данных, фактически размещённых в буфере page, так же как делает метод read для других файлов. Другие выходные значения - это *eof и *start. eof - это простой флаг, а вот использование значения start является несколько более сложным; его целью является помощь в реализации больших (более одной страницы) /proc файлов.
Параметр start имеет несколько нетрадиционное использование. Его целью является указать, где (в пределах страницы) находятся данные, которые будут возвращены пользователю. Когда вызывается метод proc_read, *start будет NULL. Если вы оставите его NULL, ядро предполагает, что данные были помещены на страницу, как если бы offset был 0; другими словами, оно предполагает простую версию proc_read, которая размещает всё содержимое виртуального файла на страницу, не обращая внимания на параметр смещения. Вместо этого, если вы установите *start в значение не-NULL, ядро предполагает, что данные, на которые указывает *start, принимают во внимание offset и готовы быть возвращены непосредственно к пользователю. В общем, простые методы proc_read, которые возвращают крошечные объёмы данных, просто игнорируют start. Более сложные методы устанавливают *start к page и размещают данные только начиная с запрошенного здесь смещения.
Давно существует другой важный вопрос с файлами /proc, которые также призван решить start. Иногда между последовательными вызовами read изменяется ASCII представление структур данных ядра, так что читающий процесс может обнаружить противоречивые данные от одного вызова к другому. Если *start установлен как небольшое целое значение, вызывающий использует его для увеличения filp->f_pos независимо от объёма возвращаемых данных, тем самым делая f_pos внутренним номером записи вашей процедуры read_proc. Если, например, ваша функция read_proc возвращает информацию из большого массива структур и пять из этих структур были возвращены в первом вызове, *start мог бы быть установлен в 5. Следующий вызов предоставляет то же значение, как offset; драйвер теперь знает, что надо начинать возвращать данные с шестой структуры в массиве. Это признается как "взлом" его авторами и это можно увидеть в fs/proc/generic.c.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
Olej писал(а):1. Во всех вариантах главным решающим действием является правильная подгонка возвращаемого значения (>off, off+count) ... под то, что придумали разработчики read_proc_t !
И это признак того, насколько отвратительно оно реализовано.
2. И это всё до тех пор, пока общий объм данных, подлежащих передаче, не превышает 3 Kb.
А дальше всё становится ещё хуже...
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
А я тем временем доделал write_proc_t, до которого раньше как-то руки не доходили.
Теперь это выглядит так:
ну, и в инициализации модуля:
всё, что касается parameter можно было и не писать - прямого отношения к записи в /proc это не имеет ... но писать в /proc чаще всего приходится, чтобы установить некоторое целочисленное значение в ядре, вот и хотелось проверить как? ведь это API ядра, здесь с atoi() не побалуешь, нет здесь такого
- это разные VARIANT (относительно read_proc_t).
Теперь это выглядит так:
Код: Выделить всё
static int parameter = 999;
static ssize_t proc_node_write( struct file *file, const char __user *buffer,
unsigned long count, void *data ) {
char *buf_msg = msg_short;
unsigned long len = min( count, LEN_MSG );
LOG( "write: %ld (buffer=%p)", count, buffer );
if( copy_from_user( buf_msg, buffer, len ) )
return -EFAULT;
*(buf_msg + len) = '\0';
sscanf( buf_msg, "%d", ¶meter );
LOG( "parameter set to %d", parameter );
return count;
}
Код: Выделить всё
own_proc_node->read_proc = proc_node_read;
own_proc_node->write_proc = proc_node_write;
Код: Выделить всё
[olej@notebook variants]$ ./build
...
mod_proc_0.ko mod_proc_1.ko mod_proc_2.ko mod_proc_3.ko
[olej@notebook variants]$ ./insm
Module Size Used by
mod_proc_3 1514 0
mod_proc_2 1474 0
mod_proc_1 1486 0
mod_proc_0 1442 0
fuse 48375 2
/proc/mod_node_0 /proc/mod_node_1 /proc/mod_node_2 /proc/mod_node_3
[olej@notebook variants]$ dmesg | tail -n100 | grep !
! /proc/mod_node_0 installed
! /proc/mod_node_1 installed
! /proc/mod_node_2 installed
! /proc/mod_node_3 installed
- это разные VARIANT (относительно read_proc_t).
Код: Выделить всё
[olej@notebook variants]$ cat /proc/mod_node_0
.........1.........2.........3.........4.........5.........6
[olej@notebook variants]$ echo 235135 > /proc/mod_node_0
[olej@notebook variants]$ cat /proc/mod_node_0
235135
[olej@notebook variants]$ dmesg | tail -n30 | grep !
! read: 3072 (buffer=f265b000, off=0, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 3072 (buffer=f265b000, off=61, start=(null))
! copy bytes: 61
! return bytes: 61
! read: 3072 (buffer=f265b000, off=61, start=(null))
! copy bytes: 61
! return bytes: 61
! write: 7 (buffer=b789f000)
! parameter set to 235135
! read: 3072 (buffer=f265b000, off=0, start=(null))
! copy bytes: 7
! return bytes: 7
! read: 3072 (buffer=f265b000, off=7, start=(null))
! copy bytes: 7
! return bytes: 7
! read: 3072 (buffer=f265b000, off=7, start=(null))
! copy bytes: 7
! return bytes: 7
- Вложения
-
- variants.02.tgz
- (3.78 КБ) 661 скачивание
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: чтение-запись данных ядра через /proc
Я подготовил ещё изменённый вариант для тестирования и такой возможности (см. архив).Olej писал(а):2. И это всё до тех пор, пока общий объм данных, подлежащих передаче, не превышает 3 Kb.
А дальше всё становится ещё хуже...
1. теперь для читающего-пишущего модуля можно заказать буфер данных сколь угодно большого размера:
Код: Выделить всё
[olej@notebook variants]$ make VARIANT=1
...
VARIANT=1
[olej@notebook variants]$ sudo insmod mod_proc_0.ko size=1000000
[olej@notebook variants]$ dmesg | tail -n30 | grep !
! /proc/mod_node_0 installed, buffer size 1000000
2. туда можно гонять данные ... туда-сюда, хоть символьные, хоть бинарные...
Код: Выделить всё
[olej@notebook variants]$ cp -v mod_proc.c /proc/mod_node_0
`mod_proc.c' -> `/proc/mod_node_0'
[olej@notebook variants]$ cp -v /proc/mod_node_0 mod_proc.dubl
`/proc/mod_node_0' -> `mod_proc.dubl'
[olej@notebook variants]$ ls -l mod_proc.*
-rw-r--r-- 1 olej olej 1546 Мар 13 11:03 mod_proc.c
-rw-rw-r-- 1 olej olej 1546 Мар 13 11:05 mod_proc.dubl
-rw-r--r-- 1 olej olej 334 Мар 13 10:49 mod_proc.h
[olej@notebook variants]$ diff -a mod_proc.c mod_proc.dubl
[olej@notebook variants]$ cmp mod_proc.c mod_proc.dubl
[olej@notebook variants]$ echo $?
0
При больших данных всё становится гораздо более грустно
Код: Выделить всё
[olej@notebook variants]$ cp -v mod_proc_0.ko /proc/mod_node_0
`mod_proc_0.ko' -> `/proc/mod_node_0'
[olej@notebook variants]$ cp -v /proc/mod_node_0 mod_proc_0.kd
`/proc/mod_node_0' -> `mod_proc_0.kd'
[olej@notebook variants]$ ls -l mod_proc_0.*
-rw-rw-r-- 1 olej olej 2 Мар 13 11:09 mod_proc_0.kd
-rw-rw-r-- 1 olej olej 121262 Мар 13 10:49 mod_proc_0.ko
- Вложения
-
- variants.03.tgz
- (4.12 КБ) 638 скачиваний
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость