将数据从内核模块保存到用户空间

将数据从内核模块保存到用户空间

我已经研究内核编程有一段时间了,想用一些定制硬件创建这个简单的数据采集接口。为了可移植性和可重用性,我在我的 Raspberry Pi 上完成了所有工作。

该项目的挑战性部分是拥有一个连接到 GPIO 的高速 ADC(并行),并拥有一个使用来自 ADC 的硬件中断来获取每个样本并将其存储在缓冲区内的内核模块,然后可以通过 chardevice 访问该缓冲区。

我当前的设置(有效)如下:

  • 我有一个用户空间 C 程序,它通过 SPI 控制我的硬件。如果我发送所需的命令,它就会开始采集模拟数据并将其发送到 ADC。
  • 每当 ADC 完成转换时,它就会在 GPIO 上将相应的信号设置为“低”,并且我会在内核模块内收到中断(绑定到该 GPIO)。 ISR 收集其他 12 个 GPIO(它是一个 12 位 ADC)的值并将其放入缓冲区,然后通过 /dev/mydevice 访问该缓冲区。
  • 我有另一个单独的用户空间程序,它运行一个永无止境的 while 循环,从 /dev/mydevice 读取数据,然后写入“out_data.dat”(一个用户空间文件)。
  • 通过这种粗略的设置(加载 2 个用户空间程序和内核模块),我每秒可以将超过 130 000 个样本写入我的文件中(不会丢失任何内容)。

我现在想看看我能跑多快,有两件事需要考虑:

  1. 我上面概述的设置是否是“通常”方式,如何完成这样的事情?我到处都读到内核不建议直接文件 I/O,所以我不这样做。当然,在 ISR 期间应该可以将其写入某个“永久”位置。在我看来,这似乎是一个常见问题,试图使用中断将数据从某些硬件传输到计算机中。

  2. 在不更改上面的设置的情况下,有什么方法可以禁用其他中断以使其尽可能顺利吗?在数据采集过程中,我实际上不需要任何东西,只需要某种方式来阻止它。任何其他中断(无线、监视器刷新等)都可以禁用,因为数据采集仅运行几分钟。之后,一切都会恢复,并且可以运行要求更高的 python 代码来分析和可视化数据(至少这是我的简单看法)。

答案1

对于用户空间数据收集程序来说,无限循环有什么问题呢?只要您使用poll系统调用,它应该是高效的:https://stackoverflow.com/questions/30035776/how-to-add-poll-function-to-the-kernel-module-code/44645336#44645336

永久数据存储

我不确定最好的方法是什么,为什么不直接在民意调查中从用户区写入文件呢?我想您担心的是,如果到达的数据太多,数据就会丢失,是这样吗?

但我怀疑在这种情况下,限制因素是内核到用户态的通信,而是永久存储设备的缓慢,所以我认为在用户态上执行它不会产生任何影响。无论如何,仅内核的解决方案有一个引人注目的问题:https://stackoverflow.com/questions/1184274/how-to-read-write-files-within-a-linux-kernel-module我认为你不会在这里得到更好的解决方案。

禁用中断

您确定这会产生任何影响,特别是考虑到可能会出现瓶颈?我预计,如果您的设备实际上产生大量中断,那么在任何情况下这些中断都会主导任何其他中断。是否值得冒破坏其他硬件状态的风险?您的硬件设备的规格是否表明它实际上可以提供比您当前拥有的更大的数据带宽?

我自己不知道该怎么做,但如果你想要答案,最好的办法是提出一个标题为“如何禁用 Linux 内核模块的所有中断?”的单独问题。 LDD2提到的cli()功能http://www.xml.com/ldd/chapter/book/ch09.html但似乎它已被弃用:https://notes.shichao.io/lkd/ch7/#no-more-global-cli该文本随后建议local_irq_disablelocal_irq_save

我还会尝试用您找到的任何方法来禁用中断,并在进一步寻找是否存在好的方法之前看看它是否会变得更有效。

在模拟器上,快速:

static int myinit(void)
{
    pr_info("hello init\n");
    unsigned long flags;
    local_irq_save(flags);
    return 0;
}

失败并显示:

returned with disabled interrupts

显然来自 v4.16 do_one_initcall,因此有一个专门的错误处理!

然后我天真地尝试从工作线程中执行此操作:

static int work_func(void *data)
{
    unsigned long flags;
    local_irq_save(flags);
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

但我仍然无法观察到任何效果,因此中断必须由其他东西启用,这可以从以下位置推断出来:

watch -n 1 grep i8042 /proc/interrupts

它不断更新 tty 或 muse/键盘中断。

与其他入口点(例如 fops)相同,或者如果我尝试原始asm("cli").我们需要一些更有教养的方法。

相关内容