如何解析通过/ proc文件传递给内核模块的大量数据?

Sha*_*baz 10 c linux buffer kernel procfs

编辑:我发现seq_file很容易从内核向用户空间写入大量数据.我正在寻找的是相反的; 一个API,便于从用户空间读取大量数据(多个页面).

编辑2:我正在实现一个<stdio.h>作为内核模块的端口,它能够打开/proc(以及后来的其他虚拟文件系统),类似于FILEs并处理类似的输入和输出<stdio.h>.你可以在这里找到这个项目.


我发现了很多关于内核如何将大量数据写入/ proc(用于用户空间程序)的问题,但没有其他方面.让我详细说明:

这个问题基本上是关于输入被标记化的算法(例如ints或混合int和字符串等),假设数据可能在多个缓冲区之间被打破.

例如,假设以下数据被发送到内核模块:

12345678 81234567 78123456 67812345 5678 1234 45678123 3456 7812 23456781
Run Code Online (Sandbox Code Playgroud)

并且为了这个例子,假设Linux提供/ proc处理程序的页面大小是20字节(相对于真正的4KB).

从/ proc(在内核模块中)读取数据的函数然后查看数据:

call 1:
"12345678 81234567 78"
call 2:
"123456 67812345 5678"
call 3:
" 1234 45678123 3456 "
call 4:
"7812 23456781"
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,当78在第一次调用中读取时,它应该在下一帧之前处理,以便它决定78帧之间是整数还是一帧.

现在我发现seq_files显然只是在内核想要数据写入用户而不是读取时(或者可能是HOWTO写得非常糟糕).

我做了什么

到目前为止,我已经提出了以下解决方案(我是从记忆中写的,所以我可能会错过几次错误检查,但请耐心等待):

在初始化阶段(比如说init_module):

initialize mutex1 to 1 and mutex2 to 0
create /proc entry
call data_processor
Run Code Online (Sandbox Code Playgroud)

/ proc读者:

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)
Run Code Online (Sandbox Code Playgroud)

我将解释为什么我以后剥离空格

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
Run Code Online (Sandbox Code Playgroud)

data_processor 功能(例如):

int first_num = get_int()
int sencod_num = get_int()
for i = first_num to second_num
    do_whatever(get_int())
Run Code Online (Sandbox Code Playgroud)

说明:首先,看看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读取器是一件可怕的事吗?我的意思是,编写数据可能会阻塞,但我不确定这是否通常发生在用户空间或内核空间中.

Sha*_*baz 1

我最终决定写一些适当的东西来解决这个问题。

基奥

kio简而言之,将是stdio.h内核模块的 C 标准的移植。它将支持/proc/sys/dev文件系统的读取和写入模式,无论是文本还是二进制。kio严格遵循标准,但进行了一些细微的调整以确保内核空间的安全。

当前状态:

  • /proc可以创建文件
  • 读取功能已实现
  • 写入功能已实现
  • 用户一次只能打开一次文件