免责声明
好的,这是一个很好的架构问题,并且它暗示了您的硬件和期望的用户空间界面的一些假设。因此,让我尝试一下猜测在您的情况下哪种解决方案最好。
设计
考虑到您没有提到write()
操作,我进一步假设您的硬件一直在产生新数据。如果是这样,您提到的设计可能正是困扰您的原因:
read
调用非常简单。它启动DMA写入,然后等待等待队列。
这正是阻止您以常规、常用(并且可能是您所期望的)方式使用驱动程序的原因。让我们跳出思维定势,首先想出期望的用户界面(您希望如何从用户空间使用驱动程序)。下面的情况在这里是常用的并且足够的(从我的角度来看):
poll()
您的设备文件以等待新数据到达
read()
您的设备文件以获取到达的数据
现在您可以看到,数据请求(到DMA)不应该由
read()
操作启动。正确的解决方案是在驱动程序中连续读取数据(没有来自用户空间的任何触发),并将其存储在内部,当用户要求您的驱动程序提供已存储在内部的数据以进行
read()
操作时-向用户提供数据。如果驱动程序内部没有存储数据-用户可以使用
poll()
操作等待新数据到达。
正如您所看到的,这是众所周知的
生产者-消费者问题。您可以使用
循环缓冲区将硬件中的数据存储在驱动程序中(因此当缓冲区满时有意丢失旧数据以防止
缓冲区溢出情况)。因此,生产者(DMA)将写入该RX环形缓冲区的
头部,而消费者(从用户空间执行
read()
的用户)将从该RX环形缓冲区的
尾部读取。
代码参考
这整个情况让我想起了串行控制台驱动程序
1 2。因此,在您的驱动程序实现中考虑使用
串行API(如果您的设备确实是串行控制台)。例如,请参阅
drivers/tty/serial/atmel_serial.c驱动程序。我对UART API并不是很熟悉,所以无法精确告诉您那里发生了什么,但乍一看似乎并不太难,因此您可能可以从那段代码中找到一些关于您的驱动程序设计的灵感。
如果您的驱动程序不应使用串行API,则可以使用以下驱动程序作为参考:
互补
回答您在评论中的问题:
您是否建议当没有可用数据时,read
调用 poll
并且 read
应该阻塞?
首先,您需要决定您想提供:
为了讨论,我们假设您想在驱动程序中提供这两个选项。在这种情况下,您应该在 open()
调用中检查 flags
参数是否包含 O_NONBLOCK
标志。根据 man 2 open
:
O_NONBLOCK
或 O_NDELAY
如果可能,以非阻塞模式打开文件。不会有任何对于返回的文件描述符进行的操作会导致调用进程等待。关于FIFO(命名管道)的处理,请参见fifo(7)
。有关O_NONBLOCK
与强制文件锁和文件租约一起使用的影响的讨论,请参见fcntl(2)
。
现在,当您了解用户选择的模式时,可以在驱动程序中执行以下操作:
- 如果
open()
中的flags
不包含这些标志,则可以进行阻塞read()
(即如果数据不可用,则等待DMA事务完成,然后返回新数据)。
- 但是,如果
open()
标志中存在O_NONBLOCK
,并且循环缓冲区中没有可用数据,则应从read()
调用中返回EWOULDBLOCK
错误代码。
来自man 2 read
:
EAGAIN
或EWOULDBLOCK
文件描述符fd
指向一个套接字,并已标记为非阻塞(O_NONBLOCK
),读取将会阻塞。POSIX.1-2001允许返回这两种错误中的任何一种,并且不要求这些常量具有相同的值,因此可移植应用程序应检查这两种可能性。
您还可以阅读下一篇文章以更好地了解相应的接口:
[1] POSIX操作系统串行编程指南
[2] 串行编程HOWTO
补充2
我需要一种后台任务,不断地从设备中读取数据并填充环形缓冲区。使用
poll
很简单 - 只需检查该缓冲区是否有内容,但是
read
更加困难,因为它可能需要等待环形缓冲区中的内容被发布。
例如,请查看drivers/char/virtio_console.c驱动程序实现。
- 在poll()函数中:执行
poll_wait()
函数(等待新数据到达)
- 在接收数据的中断处理程序中:执行
wake_up_interruptible()
函数(唤醒poll
和read
操作)
- 在read()函数中:
- 如果端口没有数据:
- 如果在
open()
操作中设置了O_NONBLOCK
标志:立即返回-EAGAIN
=-EWOULDBLOCK
- 否则,如果进行阻塞读取:执行
wait_event_freezable()
函数等待新数据到达
- 如果端口有数据:返回缓冲区中的数据
另请参阅相关问题: 如何将投票功能添加到内核模块代码中?。
read
调用poll
并且read
应该阻塞吗? - zmbpoll
现在很简单 - 只需检查该缓冲区是否有任何内容,但是read
更困难,因为它可能需要等待某些内容被发布到环形缓冲区中。 - zmbpoll
和read
的代码示例。 - Sam Protsenko