Как проанализировать большой объем данных, передаваемых в модуль ядра через файл / proc? - PullRequest
10 голосов
/ 23 марта 2012

Редактировать: Я нашел seq_file, который облегчает запись большого количества данных из ядра в пространство пользователя.То, что я ищу, является противоположностью;API, который облегчает чтение большого количества данных (более одной страницы) из пространства пользователя.

Редактировать 2 : я реализую порт <stdio.h> в качестве модуля ядра, который будетбыть в состоянии открыть /proc (и позже, другие виртуальные файловые системы), аналогичные FILE s, и обрабатывать ввод и вывод аналогично <stdio.h>.Вы можете найти проект здесь .


Я нашел много вопросов о том, как ядро ​​может записывать большие объемы данных в / proc (для программ пользовательского пространства), но ничего наоборот.Позвольте мне уточнить:

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

Например, представьте, что следующие данные отправляются в модуль ядра:

12345678 81234567 78123456 67812345 5678 1234 45678123 3456 7812 23456781

, и для примера, скажем,размер страницы, с которой Linux передает обработчик / proc, составляет 20 байт (против настоящих 4 КБ).

Функция, которая считывает данные из / proc (в модуле ядра), затем видит данные так:

call 1:
"12345678 81234567 78"
call 2:
"123456 67812345 5678"
call 3:
" 1234 45678123 3456 "
call 4:
"7812 23456781"

Как вы можете видеть, когда 78 читается при первом вызове, его еще не нужно обрабатывать до следующих кадров, чтобы определить, является ли 78 целым числом или одним разрезом между

Теперь я нашел seq_file s, которые, по-видимому, предназначены только для того, когда ядро ​​хочет записать данных для пользователя, а не читать (или это может быть то, что HOWTO написано ужасно).

Что я сделал

До сих пор я пришел со следующим решением (я пишу по памяти, поэтому я могу пропустить пару проверок ошибок, но потерпите меня):

На этапе инициализации (скажем, init_module):

initialize mutex1 to 1 and mutex2 to 0
create /proc entry
call data_processor

/ proc reader:

1. down(mutex1)    /* down_interruptible of course, but let's not get into details */

2. copy_from_user to an internal buffer
   buffer_index = 0
   data_length = whatever the size is

3. strip spaces from end of buffer (except if all left from buffer is 1 space)
   if so, there_was_space_after = 1 else 0

4. up(mutex2)

Я объясню, почему позже я удаляю пробелы

* Функция 1059 *:

wait_for_next = 0
number_was_cut = 0
last_number = 0

do
{
    1. down(mutex2)

    2. if (number_was_cut && !isdigit(buffer[buffer_index]))
           break     /* turns out it wasn't really cut
                        as beginning of next buffer is ' ' */
       number_was_cut = 0
       wait_for_next = 0

    3. while (buffer_index < data_length && !isdigit(buffer_index[buffer_index]))
           ++buffer_index;    /* skip white space */

    4. while (buffer_index < data_length && isdigit(buffer[buffer_index]))
           last_number = last_number * 10 + buffer[buffer_index++] - '0';

    5. if (buffer_index >= data_length && !there_was_space_after)
           number_was_cut = 1
           wait_for_next = 1
           up(mutex1)         /* let more data come in */
       else
           up(mutex2)         /* let get_int continue */
           break
} while (wait_for_next)

return last_number

* Функция 1063 * (например):

int first_num = get_int()
int sencod_num = get_int()
for i = first_num to second_num
    do_whatever(get_int())

Объяснение: Сначала см. data_processor.Он не вмешивается в сложности чтения данных, поэтому он просто получает целые числа и делает с ними все, что захочет.Теперь давайте посмотрим / proc proc.В основном он ждет, пока data_processor вызовет get_int достаточное количество раз для всех текущих данных, которые будут использованы (шаг 1), а затем скопирует следующий буфер во внутреннюю память, позволяя data_processor продолжить (шаг 2).Затем необходимо убрать конечные пробелы, чтобы get_int можно было немного упростить (шаг 3).Наконец, он сообщает get_int, что он может начать чтение данных (шаг 4).

Функция get_int сначала ожидает поступления данных (шаг 1) (пока игнорирует шаг 2), она пропускаетлюбые нежелательные символы (шаг 3), а затем начинает читать номер (шаг 4).Конец чтения числа двумя способами;достигнут конец буфера (в этом случае, если читатель / proc не удалил пробелы, тогда число может быть сокращено между кадрами) или пробел встречается.В первом случае он должен подать сигнал / считыватель proc, чтобы прочитать больше данных и дождаться следующего цикла, чтобы добавить оставшуюся часть номера к текущему, а в последнем случае он возвращает номер (шаг 5).Если продолжить с последнего кадра, проверьте, начинается ли новый кадр с номера или нет.Если нет, то предыдущий номер был целым числом и должен быть возвращен.В противном случае необходимо продолжать добавлять цифры к последнему номеру (шаг 2).

Проблема

Основная проблема этого метода в том, что он слишком сложен . Это становится намного сложнее, когда добавляется get_string, или целое число чтения может быть шестнадцатеричным и т. Д. По сути, вам нужно заново изобрести sscanf! Обратите внимание, что sscanf может использоваться в этом простом примере на шаге 4 в get_int вместо цикла while (или также с get_string, но это становится более сложным, когда возможен ввод шестнадцатеричных символов (представьте число сокращается между 0 и x0212ae4). Несмотря на это, он просто заменяет шаг 4 из get_int, а остальная часть материала должна остаться.

На самом деле я получил много ошибок и тяжелых испытаний, чтобы усовершенствовать все особые случаи. Это еще одна причина, почему это не выглядит элегантно для меня.

Вопросы

Я хотел бы знать, есть ли лучший способ справиться с этим. Я знаю, что использование общей памяти могло бы быть вариантом, но я ищу алгоритм для этой задачи (больше из любопытства, так как у меня уже есть мое рабочее решение). Более конкретно:

  • Есть ли уже реализованный метод в ядре Linux, который можно рассматривать как обычный C FILE, из которого вы можете получать данные, и он обрабатывает разбиение данных на сами страницы?
  • Если нет, то я слишком усложняю вещи и упускаю очевидное простое решение?
  • Я считаю, fscanf сталкивается с аналогичной проблемой. Как это обрабатывается?

Дополнительный вопрос: это ужасно, что я блокирую читателя / proc на мьютекс? Я имею в виду, что запись данных может быть блокирующей, но я не уверен, что это обычно происходит в пространстве пользователя или в пространстве ядра.

Ответы [ 3 ]

1 голос
/ 27 сентября 2012

Я наконец решил написать что-то правильное, чтобы решить эту проблему.

kio

kio, вкратце, будет портом стандарта C stdio.hдля модулей ядра.Он будет поддерживать файловые системы /proc, /sys и /dev в режимах чтения и записи, как текстовые, так и двоичные.kio строго следует стандарту, но имеет небольшие изменения для обеспечения безопасности в пространстве ядра.

Текущий статус:

  • /proc могут быть созданы файлы
  • Реализованы функции чтения
  • Реализованы функции записи
  • Файлы могут открываться пользователями только один раз за один раз
1 голос
/ 23 марта 2012

Интерфейс request_firmware () может быть вам интересен;все это буферизируется ядром перед передачей вам.

В противном случае, возможно, интерфейс двоичных атрибутов sysfs более полезен, чем proc?

0 голосов
/ 10 сентября 2012

Реализация отдельной функции get_token (), которая:

  1. Читает из внутреннего буфера, пока не найдет пробел, а затем возвращает текст.
  2. Когда в концебуфер, считывает больше данных из пространства пользователя в буфер.

Таким образом, ваша другая логика синтаксического анализа значительно упрощается (не нужно анализировать целые числа вручную и т. д.).Просто вызовите sscanf () или strtol () для возвращаемых строк.

Ограничение состоит в том, что вам нужно наложить максимальную длину на один токен, чтобы он мог поместиться в буфере.

...