[英]Implementing poll in a Linux kernel module
I have a simple character device driver that allows you to read from a custom hardware device. 我有一个简单的字符设备驱动程序,允许您从自定义硬件设备读取。 It uses a DMA to copy data from the device's memory into kernel space (and then up to the user).
它使用DMA将数据从设备的内存复制到内核空间(然后再复制到用户)。
The read
call is very simple. read
调用非常简单。 It starts a DMA write, and then waits on a wait queue. 它启动DMA写入,然后等待等待队列。 When the DMA completes, the interrupt handler sets a flag and wakes up the wait queue.
当DMA完成时,中断处理程序设置一个标志并唤醒等待队列。 The important thing to note is that I can start the DMA at any time, even before the device has data to provide .
需要注意的重要一点是,我可以随时启动DMA, 甚至在设备提供数据之前 。 The DMA engine will sit and wait until there is data to copy.
DMA引擎将等待,直到有数据要复制。 This works well.
这很好用。 I can implement a simple blocking read call in user space and it behaves as I would expect.
我可以在用户空间中实现一个简单的阻塞读取调用,它的行为与我期望的一样。
I would like to implement poll
so that I can use the select
system call in userspace, allowing me to monitor both this device and a socket simultaneously. 我想实现
poll
以便我可以在用户空间中使用select
系统调用,允许我同时监视此设备和套接字。
Most of the resources I can find on poll
say to: 我在
poll
找到的大部分资源都说:
poll_wait
for each wait queue that may indicate a change in status poll_wait
,这可能表示状态发生了变化 The second part is what confuses me. 第二部分让我感到困惑。 Most of the examples I've seen have an easy way (a pointer comparison or status bit) to check whether data is available.
我见过的大多数示例都有一种简单的方法(指针比较或状态位)来检查数据是否可用。 In my case, data will never be available unless I initiate the DMA , and even once I do that, the data is not immediately available (it may take some time before the device actually has data and for the DMA to complete).
在我的情况下, 除非我启动DMA , 否则数据将永远不可用 ,即使我这样做,数据也不会立即可用(在设备实际拥有数据和DMA完成之前可能需要一些时间)。
How would this be implemented then? 那怎么实现呢? Should the
poll
function actually start the DMA so that the data eventually becomes available? poll
功能是否应该实际启动DMA以使数据最终可用? I imagine this would break my read
function. 我想这会打破我的
read
功能。
Well, this is a good architectural question and it implies some assumptions about your hardware and desired user-space interface. 嗯,这是一个很好的架构问题,它暗示了一些关于硬件和所需用户空间接口的假设。 So let me jump into conclusions for a change and try to guess which solution would be best in your case.
因此,让我跳出结论进行更改,并尝试猜测哪种解决方案最适合您的情况。
Taking into the account that you haven't mentioned write()
operation, I will assume further that your hardware is producing new data all the time. 考虑到你没有提到
write()
操作的帐户,我将进一步假设你的硬件一直在生成新数据。 If it's so, the design you mentioned can be exactly what is confusing you: 如果是这样,你提到的设计可能正是令你困惑的:
The
read
call is very simple.read
调用非常简单。 It starts a DMA write, and then waits on a wait queue.它启动DMA写入,然后等待等待队列。
This is exactly what prevents you from working with your driver in regular, commonly used (and probably desired for you) way. 这正是阻止您以常规,常用(以及可能对您而言)方式使用驱动程序的原因。 Let's think out of the box and come up with the desired user interface first (how you would want to use your driver from user-space).
让我们开箱即用,首先想出用户界面(如何从用户空间使用你的驱动程序)。 The next case is commonly used and sufficient here (from my point of view):
下一个案例在这里常用且充足(从我的观点来看):
poll()
your device file to wait for new data to arrive poll()
您的设备文件,等待新数据到达 read()
your device file to obtain arrived data read()
您的设备文件以获取到达的数据 Now you can see that data requesting (to DMA) should be started not by read()
operation. 现在您可以看到请求(到DMA)的数据不应该由
read()
操作启动。 The correct solution would be to read data continuously in the driver (without any triggering from user-space) and store it internally, and when user asks your driver for the data to consume (by read()
operation) -- provide the user with data stored internally. 正确的解决方案将是连续驾驶者读取数据(未经用户空间任何触发),并将其存储在内部,当用户请求你的驱动程序的数据消费 (通过
read()
操作) -提供与用户内部存储的数据。 If there is no data stored internally in driver -- user can wait for new data to arrive using poll()
operation. 如果驱动程序内部没有存储数据 - 用户可以使用
poll()
操作等待新数据到达。
As you can see, this is well-known producer-consumer problem .You can use circular buffer to store data from your hardware in your driver (so you intentionally lost old data when buffer is full to prevent buffer overflow situation). 正如您所看到的,这是众所周知的生产者 - 消费者问题 。您可以使用循环缓冲区将硬件中的数据存储在驱动程序中(因此当缓冲区已满时故意丢失旧数据以防止缓冲区溢出情况)。 So the producer (DMA) writes to the head of that RX ring buffer, and the consumer (user performing
read()
from user-space) reads from tail of that RX ring buffer. 因此,生产者(DMA)写入该RX环形缓冲区的头部 ,而消费者(用户空间的用户执行
read()
从该RX环形缓冲区的尾部读取。
This all situation reminds me of serial console [ 1 , 2 ] drivers. 这一切都提醒形势串行控制台 [我的1 , 2 ]的驱动程序。 So consider using Serial API in your driver implementation (if your device in fact is a serial console).
因此,请考虑在驱动程序实现中使用串行API (如果您的设备实际上是串行控制台)。 For example see drivers/tty/serial/atmel_serial.c driver.
例如,请参阅drivers / tty / serial / atmel_serial.c驱动程序。 I'm not really familiar with UART API, so I can't tell you precisely what's going on there, but it doesn't look too hard at the first glance, so probably you can figure out a thing or two from that code for your driver design.
我对UART API并不是很熟悉,所以我无法准确地告诉你那里发生了什么,但乍一看它看起来并不太难,所以可能你可以从那个代码中找到一两件事你的司机设计。
If your driver shouldn't use Serial API, you can use next drivers for references: 如果您的驱动程序不应使用Serial API,则可以使用下一个驱动程序进行引用:
Answering your question in comment: 在评论中回答你的问题:
are you suggesting that
read
callspoll
when there is no data available andread
should block?你建议在没有可用数据时
read
调用进行poll
并且read
应该阻止吗?
First of all, you want to decide, whether you want to provide: 首先,您想决定是否要提供:
Let's assume (for the sake of argument) that you want to provide both options in your driver. 让我们假设(为了争论)你想在你的驱动程序中提供这两个选项。 In that case, you should check in
open()
call if flags
parameter contains O_NONBLOCK
flag. 在这种情况下,如果
flags
参数包含O_NONBLOCK
标志,则应检入open()
调用。 From man 2 open
: 从
man 2 open
:
O_NONBLOCK
orO_NDELAY
O_NONBLOCK
或O_NDELAY
When possible, the file is opened in nonblocking mode.
如果可能,文件以非阻塞模式打开。 Neither the
open()
nor any subsequent operations on the file descriptor which is returned will cause the calling process to wait.对返回的文件描述符的
open()
或任何后续操作都不会导致调用进程等待。 For the handling of FIFOs (named pipes), see alsofifo(7)
.有关FIFO(命名管道)的处理,另请参见
fifo(7)
。 For a discussion of the effect ofO_NONBLOCK
in conjunction with mandatory file locks and with file leases, seefcntl(2)
.有关
O_NONBLOCK
结合强制文件锁和文件租约的影响的讨论,请参阅fcntl(2)
。
Now when you're aware of mode chosen by user, you can do next (in your driver): 现在,当您了解用户选择的模式时,您可以执行下一步(在您的驱动程序中):
flags
in open()
don't contain such flags, you can do blocking read()
(ie if data is not available, wait for DMA transaction to finish and then return new data). flags
的open()
不包含这样的标志,你可以做阻塞read()
即,如果数据不可用,等待DMA交易完成,然后返回新的数据)。 O_NONBLOCK
in open()
flags and there is no data available in circular buffer -- you should return from read()
call with EWOULDBLOCK
error code. open()
标志中有O_NONBLOCK
并且循环缓冲区中没有可用数据 - 则应该使用EWOULDBLOCK
错误代码从read()
调用返回。 From man 2 read
: 从
man 2 read
:
EAGAIN
orEWOULDBLOCK
EAGAIN
或EWOULDBLOCK
The file descriptor
fd
refers to a socket and has been marked nonblocking (O_NONBLOCK
), and the read would block.文件描述符
fd
引用套接字并且已被标记为非阻塞(O_NONBLOCK
),并且读取将阻塞。 POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.POSIX.1-2001允许在这种情况下返回错误,并且不要求这些常量具有相同的值,因此便携式应用程序应检查这两种可能性。
You also may want to read next articles to get a better grasp on corresponding interfaces: 您还可以阅读下一篇文章,以便更好地掌握相应的界面:
[1] Serial Programming Guide for POSIX Operating Systems [1] POSIX操作系统的串行编程指南
[2] Serial Programming HOWTO [2] 串行编程HOWTO
I need some sort of background task that is continuously reading from the device and populating the ring buffer.
我需要某种后台任务,不断从设备读取并填充环形缓冲区。
poll
is now trivial - just check if there's anything in that buffer, butread
is more difficult because it may need to wait for something to be posted to the ring buffer.poll
现在是微不足道的 - 只需检查缓冲区中是否有任何内容,但read
更困难,因为它可能需要等待将某些内容发布到环形缓冲区。
For example look at drivers/char/virtio_console.c driver implementation. 例如,查看drivers / char / virtio_console.c驱动程序实现。
poll_wait()
(to wait for new data to arrive) poll_wait()
(等待新数据到达) wake_up_interruptible()
(to wake up poll
and read
operations) wake_up_interruptible()
(唤醒poll
和read
操作) O_NONBLOCK
flag was set (in open()
operation): return -EAGAIN
= -EWOULDBLOCK
immediately O_NONBLOCK
标志(在open()
操作中):立即返回-EAGAIN
= -EWOULDBLOCK
wait_event_freezable()
to wait for new data to arrive wait_event_freezable()
等待新数据到达 See also related question: How to add poll function to the kernel module code? 另请参阅相关问题: 如何将poll函数添加到内核模块代码中? .
。
Like with read
and other functions on special device (character or block one), poll
function can be implemented in a way, which exhibit behaviour you desire . 与特殊设备 (字符或块1)上的
read
和其他功能一样, poll
功能可以以某种方式实现,这种方式表现出您想要的行为。 The only restriction on implementation is that poll()
shall be "awoken" via waitqueue, but this doesn't restrict possible behaviour . 关于执行问题的唯一限制是
poll()
通过等待队列应被“惊醒”,但是这并不限制可能的行为 。
Moreover, behaviour of poll
isn't required to be binded with read
/ write
! 此外,
poll
行为不需要与read
/ write
绑定 ! [Again, this is true only in case of special devices .] [同样,只有特殊设备才会出现这种情况。]
So, just impose that behaviour on poll
which you think would be more useful for your task . 因此,只需将这种行为强加于您认为对您的任务更有用的
poll
中 。
One example from my practice (probably, it is applicable to your case): 我的实践中的一个例子 (可能适用于您的情况):
Colleague of me implements circular buffer of messages via character device. 我的同事通过字符设备实现消息的循环缓冲。 Messages can be read via
mmap()
. 可以通过
mmap()
读取消息。 poll
"awakes" when at least one page has been filled by messages. 当消息填充至少一个页面时,
poll
“唤醒”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.