Изменить: Я нашел seq_file
, который облегчает запись большого количества данных из ядра в пользовательское пространство. То, что я ищу, противоположное; API, который облегчает чтение большого количества данных (более одной страницы) из пользовательского пространства.
Изменить 2. Я реализую порт <stdio.h>
в качестве модуля ядра, который сможет открыть /proc
(а затем и другие виртуальные файловые системы), похожие на FILE
и введите вход и выход, аналогичный <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
, который, по-видимому, предназначен только тогда, когда ядро хочет писать данные пользователю, а не читать (или это может быть что 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 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_int
Функция:
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_processor
(например):
int first_num = get_int()
int sencod_num = get_int()
for i = first_num to second_num
do_whatever(get_int())
Объяснение: Во-первых, см. data_processor
. Он не участвует в осложнениях в отношении того, как считываются данные, поэтому он просто получает целые числа и делает с ними все, что захочет. Теперь давайте посмотрим /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 читателя на мьютексе? Я имею в виду, что запись данных может быть заблокирована, но я не уверен, что это обычно происходит в пространстве пользователя или в ядре.