10

编辑:我发现seq_file这可以轻松将大量数据从内核写入用户空间。我正在寻找的是相反的;一种有助于从用户空间读取大量数据(超过一页)的 API。

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


我发现了很多关于内核如何将大量数据写入 /proc(供用户空间程序使用)的问题,但反之则没有。让我详细说明:

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

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

12345678 81234567 78123456 67812345 5678 1234 45678123 3456 7812 23456781

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

从 /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 写得很糟糕)。

我做了什么

到目前为止,我提供了以下解决方案(我是凭记忆写的,所以我可能会错过一些错误检查,但请耐心等待):

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

initialize mutex1 to 1 and mutex2 to 0
create /proc entry
call data_processor

/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)

稍后我会解释为什么我要去掉空格

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 reader 发出信号以读取更多数据并等待另一个循环将剩余的数字附加到当前的数字上,在后一种情况下,它返回数字(步骤 5)。如果从最后一帧继续,请检查新帧是否以数字开头。如果不是,那么之前的数字实际上是一个整数,应该返回。否则,它需要继续将数字附加到最后一个数字(步骤 2)。

问题

这种方法的主要问题是过于复杂。添加时会变得更加复杂get_string,或者读取的整数可能是十六进制等。基本上,您必须重新发明sscanf!请注意,sscanf在这个简单的示例中,可以在步骤 4 中使用,get_int而不是while循环(或者也可以使用get_string,但是当十六进制输入也是可能的(想象十六进制数在 0 和 x0212ae4 之间切割)时会变得更加棘手。即便如此,它只是替换了第 4 步get_int,其余的东西应该仍然存在。

实际上,它给我带来了许多错误和繁重的测试,以完善所有特殊情况。这也是它在我看来并不优雅的另一个原因。

问题

我想知道是否有更好的方法来处理这个问题。我知道使用共享内存可能是一种选择,但我正在为这项任务寻找一种算法(更多的是出于好奇,因为我已经有了我的工作解决方案)。进一步来说:

  • Linux 内核中是否有一种已经实现的方法可以像普通 C 语言一样处理FILE,您可以从中获取数据并自行处理将数据分解为页面的过程?
  • 如果不是,我是否让事情过于复杂,我是否错过了一个明显简单的解决方案?
  • 我相信fscanf面临类似的问题。这是如何处理的?

附带问题:我在互斥体上阻止 /proc 读取器是一件可怕的事情吗?我的意思是,写入数据可能会阻塞,但我不确定这是否通常发生在用户空间或内核空间中。

4

3 回答 3

1

你可能对 request_firmware() 接口感兴趣;整个事情在交给你之前就被内核缓冲了。

否则,也许 sysfs 二进制属性接口比 proc 更有用?

于 2012-03-22T23:51:36.770 回答
1

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

基奥

kio简而言之,将是 Cstdio.h内核模块标准的一个端口。它将在读取和写入模式下支持/proc,/sys和文件系统中的任何一个,无论是文本还是二进制。严格遵循标准,但进行了细微调整以确保内核空间的安全性。/devkio

当前状态:

  • /proc可以创建文件
  • 读取功能实现
  • 写函数实现
  • 文件一次只能由用户打开一次
于 2012-09-27T15:06:34.333 回答
0

实现一个单独的函数 get_token(),它:

  1. 从内部缓冲区读取,直到找到一个空格,然后返回到目前为止的文本。
  2. 在缓冲区结束时,从用户空间读取更多数据到缓冲区。

这样,您的其他解析逻辑就大大简化了(不必手动解析整数等)。只需对返回的字符串调用 sscanf() 或 strtol()。

限制是您需要对单个令牌施加最大长度,以便它可以放入缓冲区。

于 2012-09-10T18:17:07.343 回答