Edit:我已经发现seq_file
这简化了将大量数据从内核写入用户空间的过程。我正在寻找的是相反的;一种有助于从用户空间读取大量数据(多于一页)的 API。
Edit 2:我正在实施一个端口<stdio.h>
作为能够打开的内核模块/proc
(以及后来的其他虚拟文件系统)类似于FILE
s 并处理输入和输出类似于<stdio.h>
。你可以找到该项目here.
我发现了很多关于内核如何将大量数据写入 /proc(供用户空间程序使用)的问题,但没有找到相反的方法。让我详细说明一下:
这个问题基本上是关于输入被标记化的算法(例如int
或混合物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显然仅适用于内核想要的情况write数据给用户而不是read(或者可能是 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)。
The get_int
函数首先等待数据到达(步骤 1),(暂时忽略步骤 2)它会跳过任何不需要的字符(步骤 3),然后开始读取数字(步骤 4)。读取数字的结束有两种可能;到达缓冲区末尾(在这种情况下,如果 /proc reader 没有删除任何空格,则数字could在帧之间剪切)或满足空白。在前一种情况下,它需要通知 /proc reader 读入更多数据,并等待另一个周期将剩余的数字附加到当前的数字,在后一种情况下,它返回数字(步骤 5)。如果从上一帧继续,请检查新帧是否以数字开头。如果不是,那么之前的数字实际上是一个整数,应该返回。否则,需要继续将数字附加到最后一个数字(步骤 2)。
Problem
这种方法的主要问题是过于复杂。当情况变得更加复杂时get_string
添加,或者读取的整数可能是十六进制等。基本上,你必须重新发明sscanf
!注意,sscanf
可以在这个简单示例的步骤 4 中使用get_int
而不是while
循环(或也与get_string
,但是当也可以输入十六进制时,情况会变得更加棘手(想象一下十六进制数字在 0 和 x0212ae4 之间被切割)。即便如此,它也只是取代了第 4 步get_int
其余的东西应该仍然保留。
实际上,它给我带来了很多错误和大量测试来完善所有特殊情况。这是它在我看来不优雅的另一个原因。
问题
我想知道是否有更好的方法来处理这个问题。我知道使用共享内存可能是一种选择,但我正在寻找用于此任务的算法(更多是出于好奇,因为我已经有了我的工作解决方案)。进一步来说:
- Linux内核中是否有一个已经实现的方法可以像普通的C一样对待
FILE
您可以从中获取数据并处理将数据分解为页面本身?
- 如果不是,我是否让事情变得过于复杂并且我是否错过了一个明显的简单解决方案?
- 我相信
fscanf
面临着类似的问题。这件事是如何处理的呢?
附带问题:我在互斥锁上阻止 /proc 读取器是一件可怕的事情吗?我的意思是,写入数据可能会阻塞,但我不确定这通常发生在用户空间还是内核空间。