编辑:我发现seq_file
这可以轻松将大量数据从内核写入用户空间。我正在寻找的是相反的;一种有助于从用户空间读取大量数据(超过一页)的 API。
编辑 2:我正在实现一个<stdio.h>
内核模块的端口,它能够打开/proc
(以及后来的其他虚拟文件系统)类似于FILE
s 并处理类似于<stdio.h>
. 您可以在此处找到该项目。
我发现了很多关于内核如何将大量数据写入 /proc(供用户空间程序使用)的问题,但反之则没有。让我详细说明:
这个问题基本上是关于输入被标记化的算法(例如到int
s 或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_file
s 显然只适用于内核想要向用户写入数据而不是读取数据(或者可能是 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 读取器是一件可怕的事情吗?我的意思是,写入数据可能会阻塞,但我不确定这是否通常发生在用户空间或内核空间中。