Редактировать: Я нашел 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 на мьютекс? Я имею в виду, что запись данных может быть блокирующей, но я не уверен, что это обычно происходит в пространстве пользователя или в пространстве ядра.