我有一个简单的字符设备驱动程序,允许您从自定义硬件设备读取。它使用DMA将数据从设备的内存复制到内核空间(然后直到用户)。
read
电话非常简单。它启动DMA写入,然后等待等待队列。当DMA完成时,中断处理程序设置一个标志并唤醒等待队列。需要注意的重要一点是,我可以随时启动DMA,甚至在设备提供数据之前。 DMA引擎将等待,直到有数据要复制。这很好用。我可以在用户空间中实现一个简单的阻塞读取调用,它的行为与我期望的一样。
我想实现poll
,以便我可以在用户空间中使用select
系统调用,允许我同时监视此设备和套接字。
我可以在poll
上找到的大多数resources说:
poll_wait
第二部分让我感到困惑。我见过的大多数示例都有一种简单的方法(指针比较或状态位)来检查数据是否可用。在我的情况下,除非我启动DMA ,否则数据将永远不可用,甚至一旦我这样做,数据就不会立即可用(在设备实际拥有数据之前可能需要一些时间) DMA完成)。
如何实施呢? poll
函数是否应该实际启动DMA以使数据最终可用?我想这会破坏我的read
功能。
答案 0 :(得分:8)
嗯,这是一个很好的架构问题,它暗示了一些关于硬件和所需用户空间接口的假设。因此,让我跳出结论进行更改,并尝试猜测哪种解决方案最适合您的情况。
考虑到您尚未提及write()
操作的帐户,我将进一步假设您的硬件始终生成新数据。如果是这样,你提到的设计可能正是让你感到困惑的原因:
read
电话非常简单。它启动DMA写入,然后等待等待队列。
这正是阻止您以常规,常用(以及可能对您而言)方式使用驱动程序的原因。让我们开箱即用,首先想出用户界面(如何从用户空间使用你的驱动程序)。下一个案例在这里经常使用和充分(从我的观点来看):
poll()
您的设备文件等待新数据到达read()
您的设备文件以获取到达的数据现在您可以看到请求(到DMA)的数据应该由read()
操作启动而不是。正确的解决方案是在驱动程序中连续读取数据(无需从用户空间触发)并在内部存储,并在用户向驱动程序请求数据消耗时(read()
操作) - 为用户提供内部存储的数据。如果驱动程序内部没有存储数据 - 用户可以使用poll()
操作等待新数据到达。
正如您所看到的,这是众所周知的producer-consumer problem。您可以使用circular buffer将硬件中的数据存储在驱动程序中(因此当缓冲区已满时故意丢失旧数据以防止< em>缓冲区溢出情况)。因此,生产者(DMA)写入该RX环形缓冲区的 head ,而消费者(用户空间的用户执行read()
)从 tail 读取那个RX环缓冲区。
这种情况让我想起串行控制台 [1,2]驱动程序。因此,请考虑在驱动程序实现中使用Serial API(如果您的设备实际上 是串行控制台)。例如,请参阅drivers/tty/serial/atmel_serial.c驱动程序。我对UART API并不是很熟悉,所以我无法准确地告诉你那里发生了什么,但乍一看它看起来并不太难,所以可能你可以从你的驱动程序设计代码中找出一两件事。
如果您的驱动程序不应使用串行API,则可以使用下一个驱动程序作为参考:
在评论中回答你的问题:
如果没有可用数据且
read
应该阻止,您建议poll
调用read
吗?
首先,您想决定是否要提供:
我们假设(为了争论)你想在你的驱动程序中提供这两个选项。在这种情况下,如果open()
参数包含flags
标记,则应检入O_NONBLOCK
。来自man 2 open
:
O_NONBLOCK
或O_NDELAY
如果可能,文件以非阻塞模式打开。对于返回的文件描述符,
open()
或任何后续操作都不会导致调用进程等待。有关FIFO(命名管道)的处理,另请参阅fifo(7)
。有关O_NONBLOCK
与强制文件锁和文件租约相结合的影响的讨论,请参阅fcntl(2)
。
现在,当您了解用户选择的模式时,您可以进行下一步(在您的驱动程序中):
flags
中的open()
不包含此类标记,则可以阻止read()
(即,如果数据不可用,请等待DMA事务完成然后返回新数据)。O_NONBLOCK
标志中有open()
并且循环缓冲区中没有可用数据 - 您应该从read()
调用EWOULDBLOCK
错误代码返回。 来自man 2 read
:
EAGAIN
或EWOULDBLOCK
文件描述符
fd
引用套接字并且已被标记为非阻塞(O_NONBLOCK
),并且读取将阻止。 POSIX.1-2001允许在这种情况下返回错误,并且不要求这些常量具有相同的值,因此便携式应用程序应检查这两种可能性。
您可能还想阅读下一篇文章,以便更好地掌握相应的界面:
[1] Serial Programming Guide for POSIX Operating Systems
我需要某种后台任务,它不断从设备读取并填充环形缓冲区。
poll
现在很简单 - 只需检查缓冲区中是否有任何内容,但read
更难,因为它可能需要等待某些内容发布到环形缓冲区。
例如,查看drivers/char/virtio_console.c驱动程序实现。
poll_wait()
(等待新数据到达)wake_up_interruptible()
(唤醒poll
和read
操作)O_NONBLOCK
标志(在open()
操作中):立即返回-EAGAIN
= -EWOULDBLOCK
wait_event_freezable()
以等待新数据到达答案 1 :(得分:0)
与特殊设备(字符或块1)上的read
和其他函数一样,poll
函数可以某种方式实现,其表现出行为 you欲望即可。 实现的唯一限制是poll()
将通过waitqueue“唤醒”,但这不会限制可能的行为。
此外,poll
的行为不需要与read
/ write
绑定! [同样,仅在特殊设备的情况下才是这样。]
因此,只需将这种行为强加于您认为对您的任务更有用的poll
。
我练习的一个例子(可能适用于您的情况):
我的同事通过角色设备实现消息的循环缓冲。可以通过mmap()
阅读消息。 poll
当至少有一页被消息填充时“醒来”。