在Linux内核模块中实现民意测验 [英] Implementing poll in a Linux kernel module

查看:71
本文介绍了在Linux内核模块中实现民意测验的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

大多数资源我可以在poll上找到说:

    为可能指示状态变化的每个等待队列
  1. 呼叫poll_wait
  2. 返回表示数据是否可用的位掩码

第二部分使我感到困惑.我见过的大多数示例都有一种简单的方法(指针比较或状态位)来检查数据是否可用.就我而言,除非启动DMA ,否则数据将永远不可用,即使执行了此操作,数据也不会立即可用(在设备实际拥有数据之前可能需要一些时间,并且对于DMA完成).

那么这将如何实施? poll函数是否应该实际上启动DMA,以便最终使数据可用?我想这会破坏我的read函数.

解决方案

免责声明

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

设计

考虑到您没有提到write()操作的原因,我将进一步假设您的硬件一直在产生新数据.如果是这样,您提到的设计可能正是使您感到困惑的地方:

read调用非常简单.它开始DMA写操作,然后在等待队列中等待.

这正是阻止您以常规,常用(可能是您所希望的)方式使用驱动程序的原因.让我们思考开箱即用的情况,并首先提出所需的用户界面(如何从用户空间使用驱动程序).在我看来,下一种情况是常用且足够的:

  1. poll()您的设备文件等待新数据到达
  2. read()您的设备文件以获取到达的数据

现在您可以看到,通过read()操作可以开始向DMA请求数据.正确的解决方案是在驱动程序中连续读取数据(不从用户空间触发任何数据)并将其存储在内部,以及当用户要求驱动程序要使用的数据时(通过read()操作) )-向用户提供内部存储的数据.如果驱动程序内部没有存储数据,则用户可以使用poll()操作等待新数据到达.

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

代码参考

这一切使我想起串行控制台 [ 1 2 ]驱动程序.因此,请考虑在驱动程序实现中使用串行API (如果您的设备位于实际上是串行控制台).例如,请参见 drivers/tty/serial/atmel_serial.c 驱动程序.我对UART API不太熟悉,因此我无法确切告诉您发生了什么,但是乍一看它看起来并不难,因此您可能可以从该代码中找出一两个问题您的驱动程序设计.

如果您的驱动程序不应该使用串行API,则可以使用下一个驱动程序作为参考:

互补

在评论中回答您的问题:

您是否建议在没有可用数据且read应该阻止的情况下read调用poll?

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

让我们假设(出于争论的目的)您想在驱动程序中同时提供两个选项.在这种情况下,如果flags参数包含O_NONBLOCK标志,则应签入open()调用.来自 man 2 open :

O_NONBLOCKO_NDELAY

在可能的情况下,以非阻止模式打开文件. open()或对返回的文件描述符进行的任何后续操作都不会导致调用过程等待.有关FIFO(命名管道)的处理,另请参见fifo(7).有关O_NONBLOCK与强制性文件锁定和文件租约一起使用的影响的讨论,请参见fcntl(2).

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

  1. 如果open()中的flags不包含此类标志,则可以阻止read()(即,如果数据不可用,请等待DMA事务完成,然后返回新数据).
  2. 但是如果open()标志中有O_NONBLOCK并且循环缓冲区中没有可用数据-您应该使用EWOULDBLOCK错误代码从read()调用返回.

来自 man 2 read :

EAGAINEWOULDBLOCK

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

您可能还想阅读下一篇文章,以更好地了解相应的界面:

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

[2] 串行编程指南

互补2

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

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

  1. poll()函数中:做poll_wait()(等待新数据到达)
  2. 接收数据中断处理程序中: wake_up_interruptible()(唤醒pollread操作)
  3. read()函数中:
    • 如果端口没有数据:
      • 如果设置了O_NONBLOCK标志(在open()操作中):立即返回-EAGAIN = -EWOULDBLOCK
      • 否则,我们将阻止读取:执行wait_event_freezable()等待新数据到达
    • 如果端口确实有数据:返回来自缓冲区的数据


另请参阅相关问题:如何添加民意调查内核模块代码有功能吗?.

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).

The read call is very simple. It starts a DMA write, and then waits on a wait queue. When the DMA completes, the interrupt handler sets a flag and wakes up the wait queue. The important thing to note is that I can start the DMA at any time, even before the device has data to provide. The DMA engine will sit and wait until there is data to copy. 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.

Most of the resources I can find on poll say to:

  1. call poll_wait for each wait queue that may indicate a change in status
  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).

How would this be implemented then? Should the poll function actually start the DMA so that the data eventually becomes available? I imagine this would break my read function.

解决方案

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. If it's so, the design you mentioned can be exactly what is confusing you:

The read call is very simple. It starts a DMA write, and then waits on a wait queue.

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
  2. read() your device file to obtain arrived data

Now you can see that data requesting (to DMA) should be started not by read() operation. 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. If there is no data stored internally in driver -- user can wait for new data to arrive using poll() operation.

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.

Code references

This all situation reminds me of serial console [1, 2] drivers. So consider using Serial API in your driver implementation (if your device in fact is a serial console). For example see drivers/tty/serial/atmel_serial.c driver. 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.

If your driver shouldn't use Serial API, you can use next drivers for references:

Complementary

Answering your question in comment:

are you suggesting that read calls poll when there is no data available and read should block?

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. From man 2 open:

O_NONBLOCK or 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. For the handling of FIFOs (named pipes), see also fifo(7). For a discussion of the effect of O_NONBLOCK in conjunction with mandatory file locks and with file leases, see 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() (i.e. if data is not available, wait for DMA transaction to finish and then return new data).
  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.

From man 2 read:

EAGAIN or EWOULDBLOCK

The file descriptor fd refers to a socket and has been marked nonblocking (O_NONBLOCK), and the read would block. 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.

You also may want to read next articles to get a better grasp on corresponding interfaces:

[1] Serial Programming Guide for POSIX Operating Systems

[2] Serial Programming HOWTO

Complementary 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.

For example look at drivers/char/virtio_console.c driver implementation.

  1. In poll() function: do poll_wait() (to wait for new data to arrive)
  2. In receive data interrupt handler: do wake_up_interruptible() (to wake up poll and read operations)
  3. In read() function:
    • if port has no data:
      • if O_NONBLOCK flag was set (in open() operation): return -EAGAIN = -EWOULDBLOCK immediately
      • otherwise we have blocking read: do wait_event_freezable() to wait for new data to arrive
    • if port do have data: return data from buffer


See also related question: How to add poll function to the kernel module code?.

这篇关于在Linux内核模块中实现民意测验的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆