编辑:我已经找到了可以轻松将大量内核数据写入用户空间的seq_file
。现在我正在寻找相反的功能;一种API可以方便地从用户空间读取大量数据(超过一页)。
编辑2:我正在实现一个内核模块的端口,类似于<stdio.h>
,能够打开/proc
(以及后续的其他虚拟文件系统),并处理输入和输出,就像使用<stdio.h>
一样。您可以在此处找到该项目。
我发现了很多关于内核如何向/proc写入大量数据(供用户空间程序使用)的问题,但没有关于相反情况的内容。让我详细解释一下:
这个问题基本上是关于输入被分解为什么算法(例如int
或int
和字符串的混合),假设数据可能在多个缓冲区之间被分隔。
例如,想象一下以下数据正在被发送到内核模块:
12345678 81234567 78123456 67812345 5678 1234 45678123 3456 7812 23456781
为了举例说明,假设Linux向/proc处理程序提供的页面大小为20个字节(而不是真实的4KB)。
从内核模块中读取数据的函数会将数据看作如下形式:
call 1:
"12345678 81234567 78"
call 2:
"123456 67812345 5678"
call 3:
" 1234 45678123 3456 "
call 4:
"7812 23456781"
正如你所看到的,在第一次调用时读取了
78
,但在下一个帧决定它是整数还是分割在帧之间之前,不应该对其进行处理。现在我找到了似乎仅用于内核想向用户写入数据而非读取数据的
seq_file
(或者可能是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 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
可以在get_int
的第4步(或在get_string
中也可以使用,但当输入为十六进制时,情况会更加棘手(想象一下16进制数字被切割在0和x0212ae4之间)。即使如此,它只是替换了get_int
的第4步,其余的部分仍然应该保持不变。
实际上,我花费了很多时间进行测试和修复所有特殊情况的错误。这也是为什么它对我来说看起来不太优雅的另一个原因。
问题
我想知道是否有更好的方法来处理这个问题。我知道使用共享内存可能是一种选择,但我正在寻找这个任务的算法(更多是出于好奇心,因为我已经有了工作解决方案)。更具体地说:
- Linux内核中是否已经有一个实现好的方法,可以像普通的C
FILE
一样处理数据,并自行将数据分成页面? - 如果没有,我是否过于复杂化了问题,错过了明显的简单解决方案?
- 我认为
fscanf
也面临着类似的问题。那么这个问题是如何解决的呢?
附带问题:我在互斥锁上阻塞/proc读取器是否很糟糕?我的意思是,写入数据可能会阻塞,但我不确定这通常发生在用户空间还是内核空间。
request_firmware
获取整个文件,但它并不是用于初始化数据的。我对了解更多关于sysfs很感兴趣。我确实在使用它处理其他事情,但据我所读,他们说“使用sysfs处理小数据”,即使我使用它处理大数据,这是否会有相同的问题,即以分页的方式提供数据给您? - Shahbaz