当前位置:  开发笔记 > 运维 > 正文

在Linux内核模块中实现轮询

如何解决《在Linux内核模块中实现轮询》经验,为你挑选了1个好方法。

我有一个简单的字符设备驱动程序,允许您从自定义硬件设备读取.它使用DMA将数据从设备的内存复制到内核空间(然后再复制到用户).

read调用非常简单.它启动DMA写入,然后等待等待队列.当DMA完成时,中断处理程序设置一个标志并唤醒等待队列.需要注意的重要一点是,我可以随时启动DMA,甚至在设备提供数据之前.DMA引擎将等待,直到有数据要复制.这很好用.我可以在用户空间中实现一个简单的阻塞读取调用,它的行为与我期望的一样.

我想实现,poll以便我可以select在用户空间中使用系统调用,允许我同时监视此设备和套接字.

我能找到的大部分资源poll都说:

    调用poll_wait可能表示状态发生变化的每个等待队列

    返回一个指示数据是否可用的位掩码

第二部分让我感到困惑.我见过的大多数示例都有一种简单的方法(指针比较或状态位)来检查数据是否可用.在我的情况下,除非我启动DMA,否则数据将永远不可用,即使我这样做,数据也不会立即可用(在设备实际拥有数据和DMA完成之前可能需要一些时间).

那怎么实现呢?该poll函数是否应该实际启动DMA以使数据最终可用?我想这会破坏我的read功能.



1> Sam Protsenk..:

放弃

嗯,这是一个很好的架构问题,它暗示了一些关于硬件和所需用户空间接口的假设.因此,让我跳出结论进行更改,并尝试猜测哪种解决方案最适合您的情况.

设计

考虑到您未提及write()操作的帐户,我将进一步假设您的硬件始终在生成新数据.如果是这样,你提到的设计可能正是令你困惑的:

read调用非常简单.它启动DMA写入,然后等待等待队列.

这正是阻止您以常规,常用(以及可能对您而言)方式使用驱动程序的原因.让我们开箱即用,首先想出用户界面(如何从用户空间使用你的驱动程序).下一个案例在这里常用且充足(从我的观点来看):

    poll() 您的设备文件等待新数据到达

    read() 您的设备文件以获取到达的数据

现在您可以看到请求(到DMA)的数据应该不是通过read()操作来启动的.正确的解决方案将是连续驾驶者读取数据(未经用户空间任何触发),并将其存储在内部,当用户请求你的驱动程序的数据消费(通过read()操作) -为用户提供内部存储的数据.如果驱动程序内部没有存储数据 - 用户可以使用poll()操作等待新数据到达.

正如您所看到的,这是众所周知的生产者 - 消费者问题.您可以使用循环缓冲区将硬件中的数据存储在驱动程序中(因此当缓冲区已满时故意丢失旧数据以防止缓冲区溢出情况).因此,生产者(DMA)写入该RX环形缓冲区的头部,而消费者(用户read()空间执行的用户)从该RX环形缓冲区的尾部读取.

代码参考

这一切情况让我想起了串行控制台 [ 1,2 ]的驱动程序.因此,请考虑在驱动程序实现中使用串行API(如果您的设备实际上串行控制台).例如,请参阅drivers/tty/serial/atmel_serial.c驱动程序.我对UART API并不是很熟悉,所以我无法准确地告诉你那里发生了什么,但乍一看它看起来并不太难,所以可能你可以从那个代码中找到一两件事你的司机设计.

如果您的驱动程序不应使用Serial API,则可以使用下一个驱动程序进行引用:

司机/炭/ virtio_console.c

司机/炭/ xillybus/xillybus_core.c

补充

在评论中回答你的问题:

如果没有可用的数据并且应该阻止,你建议read打电话吗?pollread

首先,您想决定是否要提供:

阻止I/O.

非阻塞I/O.

或两者兼而有之

让我们假设(为了争论)你想在你的驱动程序中提供这两个选项.在这种情况下,open()如果flags参数包含O_NONBLOCK标志,则应检入呼叫.来自man 2 open:

O_NONBLOCK 要么 O_NDELAY

如果可能,文件以非阻塞模式打开.无论是open()也不在其返回将导致调用进程等待的文件描述符的任何后续操作.有关处理FIFO(命名管道)的信息,另请参阅fifo(7).有关O_NONBLOCK与强制文件锁和文件租约一起使用的效果的讨论,请参阅fcntl(2).

现在,当您了解用户选择的模式时,您可以执行下一步(在您的驱动程序中):

    如果flagsin open()不包含此类标志,则可以执行阻塞read()(即,如果数据不可用,请等待DMA事务完成然后返回新数据).

    但如果O_NONBLOCKopen()标志并没有在循环缓冲区可用数据-你应该返回read()调用与EWOULDBLOCK错误代码.

来自man 2 read:

EAGAIN 要么 EWOULDBLOCK

文件描述符fd引用套接字并且已标记为nonblocking(O_NONBLOCK),并且读取将阻塞.POSIX.1-2001允许在这种情况下返回错误,并且不要求这些常量具有相同的值,因此便携式应用程序应检查这两种可能性.

您还可以阅读下一篇文章,以便更好地掌握相应的界面:

[1] POSIX操作系统的串行编程指南

[2] 串行编程HOWTO

补充2

我需要某种后台任务,不断从设备读取并填充环形缓冲区.poll现在是微不足道的 - 只是检查缓冲区中是否有任何东西,但read更难,因为它可能需要等待将某些内容发布到环形缓冲区.

例如,查看drivers/char/virtio_console.c驱动程序实现.

    在poll()函数中:do poll_wait()(等待新数据到达)

    在接收数据中断处理程序中:do wake_up_interruptible()(唤醒pollread操作)

    在read()函数中:

    如果端口没有数据:

    如果O_NONBLOCK设置了标志(在open()操作中):return -EAGAIN= -EWOULDBLOCK立即

    否则我们有阻塞读取:wait_event_freezable()等待新数据到达

    如果端口有数据:从缓冲区返回数据


另请参阅相关问题:如何将poll函数添加到内核模块代码中?.

推荐阅读
凹凸曼00威威_694
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有