Редактировать: Я нашел seq_file
, который упрощает запись большого количества данных из ядра в пользовательское пространство. . Я ищу противоположное; API, который облегчает чтение большого количества данных (более одной страницы) из пользовательского пространства.
Редактировать 2: я реализую порт
в качестве модуля ядра, который сможет открывать /proc
(а позже и другие виртуальные файловые системы) аналогичны FILE
s и обрабатывают ввод и вывод аналогично
. Вы можете найти проект здесь.
Я нашел МНОГО вопросов о том, как ядро может записывать большие объемы данных в /proc (для программ пользовательского пространства), но не наоборот. Позвольте мне уточнить:
Этот вопрос в основном касается алгоритма, с помощью которого ввод токенизируется (например, в int
s или смесь 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 написано ужасно).
На данный момент я пришел к следующему решению (пишу по памяти, поэтому могу пропустить пару проверок ошибок, но терпите меня):
На этапе инициализации ( 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_int
function:
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
function (например):
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
, а остальное должно оставаться .
На самом деле я получил много ошибок и тщательное тестирование, чтобы усовершенствовать все особые случаи. Это еще одна причина, почему мне это не кажется элегантным.
Я хотел бы знать, есть ли лучший способ справиться с этим. Я знаю, что использование общей памяти может быть вариантом, но я ищу алгоритм для этой задачи (больше из любопытства, так как у меня уже есть рабочее решение). Более конкретно:
ФАЙЛ
, из которого вы можете брать данные, и он сам обрабатывает разбиение данных на страницы?fscanf
сталкивается с похожей проблемой. Как это обрабатывается этим?Дополнительный вопрос: это ужасно, что я блокирую чтение /proc на мьютексе? Я имею в виду, что запись данных может блокировать, но я не уверен, что это обычно происходит в пространстве пользователя или в пространстве ядра.