我正在开发一个Linux驱动程序,并且发现在某些情况下,copy_to_user()花费的时间比预期的要长得多。我想它可能在等待mm->mmap_sem semaphore,也许?在不利的情况下,似乎也有额外的CPU活动。
我希望就如何进一步调查这一问题和(或)如何处理这一问题提出建议。
更详细的情况:
该平台有一个I7,两个物理核运行在2.5GHz,32位X86构建为Linux2.6.32。驱动程序通过PCI 接受数据,并通过字符设备将其提供给用户空间。测试过程以高度优先级运行,读取到我认为是分页的缓冲区。其目标是支持相当高的数据速率,目前每秒几百兆位,最终达到1 1Gbps,至少维持几秒钟。
我的测试包括在大约一秒钟内读取数据的25 My 。通过正确的数据集,驱动程序可以可靠地接收100 data 或400 data。对于略有不同的100 fails 数据,它偶尔会失败。无论是驱动程序还是测试工具都不关心数据的内容,所以我希望它不会产生任何影响。在抵达时间和突发性方面可能存在统计上的差异,但在阅读时间戳清单时,没有什么大到足以突出的。
问题的直接原因是接收缓冲区溢出(约1MB)。这是由于进程读取太慢,这反过来又是copy_to_user()花费了很长时间的结果。副本通常传输几百字节。有了良好的100 this 输入,此函数返回的速度相当快,通常在微秒内由循环计数判断。对于有问题的100 this 数据,有些调用长达10毫秒,这种情况可以多次发生,而不仅仅是一次性调用。
I7Z tool (链接在这里)表示CPU活动的差异。当处理良好的100 most数据时,一个核心大部分时间都在功率状态C1上,另一个核心主要在C6 (低功耗)中。对于坏数据,一个核心主要处于C0 (最活跃的状态),另一个则可能花费在C1上的时间从0%到70%,其余的大部分花费在C6上。所以看起来它是在做正常的处理,加上很多额外的C0。可能转了很多圈?
驱动程序和测试工具相关部分的伪代码摘要:
pseudo_interrupt_handler()
{
if(DMA finished) {
advance head;
wake_up_interruptible();
}
if(new data && no DMA in progress) {
start DMA into head;
}
}
pseudo_file_read(filp, user buf, size)
{
wait_event_interruptible(head != tail);
while(head != tail && space in user buf) {
copy_to_user(from tail);
advance tail;
}
return total copied;
}
pseudo_test_process()
{
buffer = malloc(25MB);
write to each page in buffer; // page in
while(buffer not full) {
read(STDIN_FILENO, position in buffer, 4000B);
advance position in buffer
}
}我目前没有令人满意的解决办法。我可以在驱动程序中缓冲更多的数据,但这只能帮助临时延迟。这一问题似乎正在系统地减缓事态发展,并限制了总体数据速率。如果没有其他解决方案,mmap是可能的,但这需要对现有应用程序软件进行大量更改。
最新情况(1月)。谢谢你的建议。我现在正在解决这个问题,对缓冲区进行一些扩大和重组。我们现在使用更小数量的更大的copy_to_user()操作,所以一小部分慢速操作的影响较小。
最新情况(6月)。正如建议的那样,我已经实现了一个mmap()接口,它确实解决了这个问题。不再有copy_to_user()瓶颈。
发布于 2013-12-04 13:11:57
copy_to_user()和copy_from_user()总是涉及复制数据,这在本质上是一个缓慢的过程(当然,这取决于观点)。为了获得最大的性能,唯一的方式是“mmap”。请参阅下面这里的摘录!
内存映射是在不涉及显式复制的用户空间和内核空间之间传输数据的唯一方法,也是处理大量数据的最快方法。
https://stackoverflow.com/questions/20365114
复制相似问题