简体   繁体   English

在Linux内核模块中实现轮询

[英]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找到的大部分资源都说:

  1. call poll_wait for each wait queue that may indicate a change in status 为每个等待队列调用poll_wait ,这可能表示状态发生了变化
  2. return a bit mask indicating whether data is available 返回一个指示数据是否可用的位掩码

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功能。

Disclaimer 放弃

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. 因此,让我跳出结论进行更改,并尝试猜测哪种解决方案最适合您的情况。

Design 设计

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): 下一个案例在这里常用且充足(从我的观点来看):

  1. poll() your device file to wait for new data to arrive poll()您的设备文件,等待新数据到达
  2. 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环形缓冲区的尾部读取。

Code references 代码参考

This all situation reminds me of serial console [ 1 , 2 ] drivers. 这一切都提醒形势串行控制台 [我的12 ]的驱动程序。 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,则可以使用下一个驱动程序进行引用:

Complementary 补充

Answering your question in comment: 在评论中回答你的问题:

are you suggesting that read calls poll when there is no data available and read 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 or O_NDELAY O_NONBLOCKO_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 also fifo(7) . 有关FIFO(命名管道)的处理,另请参见fifo(7) For a discussion of the effect of O_NONBLOCK in conjunction with mandatory file locks and with file leases, see fcntl(2) . 有关O_NONBLOCK结合强制文件锁和文件租约的影响的讨论,请参阅fcntl(2)

Now when you're aware of mode chosen by user, you can do next (in your driver): 现在,当您了解用户选择的模式时,您可以执行下一步(在您的驱动程序中):

  1. If 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). 如果flagsopen()不包含这样的标志,你可以做阻塞read()即,如果数据不可用,等待DMA交易完成,然后返回新的数据)。
  2. But if there is 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 or EWOULDBLOCK EAGAINEWOULDBLOCK

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

Complementary 2 补充2

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, but read 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驱动程序实现。

  1. In poll() function: do poll_wait() (to wait for new data to arrive) poll()函数中:执行poll_wait() (等待新数据到达)
  2. In receive data interrupt handler : do wake_up_interruptible() (to wake up poll and read operations) 接收数据中断处理程序中 :执行wake_up_interruptible() (唤醒pollread操作)
  3. In read() function: read()函数中:
    • if port has no data : 如果端口没有数据
      • if O_NONBLOCK flag was set (in open() operation): return -EAGAIN = -EWOULDBLOCK immediately 如果设置了O_NONBLOCK标志(在open()操作中):立即返回-EAGAIN = -EWOULDBLOCK
      • otherwise we have blocking read: do wait_event_freezable() to wait for new data to arrive 否则我们有阻塞读取:do wait_event_freezable()等待新数据到达
    • if port do have data: return data from buffer 如果端口有数据: 从缓冲区返回数据

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM