Как разобрать большой объем данных, передаваемых в модуль ядра через файл /proc?

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

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


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

Этот вопрос в основном касается алгоритма, с помощью которого ввод токенизируется (например, в ints или смесь 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_files, которые, по-видимому, предназначены только для случаев, когда ядро ​​хочет записатьданные пользователю, а не прочитать(или может случиться так, что HOWTO написано ужасно).

Что я сделал

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

На этапе инициализации ( say 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)

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

get_intfunction:

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

data_processorfunction (например):

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

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

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

Проблема

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

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

Вопросы

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

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

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

10
задан Flimzy 29 May 2018 в 17:35
поделиться