当前位置: 首页 > 面试题库 >

在Linux内核模块中实现民意测验

堵飞鸿
2023-03-14
问题内容

我有一个简单的字符设备驱动程序,可让您从自定义硬件设备中读取。它使用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() 您的设备文件以获得到达的数据

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

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

代码参考

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

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

  • drivers / char / virtio_console.c
  • 驱动程序/char/xillybus/xillybus_core.c

补充

在评论中回答您的问题:

您是否建议在没有可用数据且应阻止时read拨打电话?poll``read

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

  • 阻止I / O
  • 非阻塞I / O
  • 或两者都

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

O_NONBLOCK 要么 O_NDELAY

如果可能,以非阻止模式打开文件。open()返回的文件描述符的或后续操作都不会导致调用过程等待。有关FIFO(命名管道)的处理,另请参见fifo(7)。有关O_NONBLOCK与强制性文件锁定和文件租用结合使用的影响的讨论,请参见fcntl(2)

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

  1. 如果flagsin中open()不包含此类标志,则可以进行阻止read()(即,如果数据不可用,请等待DMA事务完成,然后返回新数据)。
  2. 但如果O_NONBLOCKopen()标志并没有在循环缓冲区可用数据-你应该返回read()调用与EWOULDBLOCK错误代码。

来自man 2 read

EAGAIN 要么 EWOULDBLOCK

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

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

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

[2] 串口编程指南

补充2

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

例如,查看driver / char / virtio_console.c驱动程序的实现。

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


 类似资料:
  • 主要内容:initramfe虚拟文件系统GRUB 加载了内核之后,内核首先会再进行二次系统的自检,而不一定使用 BIOS 检测的硬件信息。这时内核终于开始替代 BIOS 接管 Linux 的启动过程了。 内核完成再次系统自检之后,开始采用动态的方式加载每个硬件的模块,这个动态模块大家可以想象成硬件的驱动(默认 Linux 硬件的驱动是不需要手工安装的,如果是重要的功能,则会直接编译到内核当中;如果是非重要的功能,比如硬件驱动会编译为模块

  • 主要内容:内核模块保存位置与模块保存文件,内核模块的查看,内核模块的添加与删除Linux 的内核会在启动过程中自动检验和加载硬件与文件系统的驱动。一般这些驱动都是用模块的形式加载的,使用模块的形式保存驱动,可以不直接把驱动放入内核,有利于控制内核大小。 模块的全称是 动态可加载内核模块,它是具有独立功能的程序,可以被单独编译,但不能独立运行。模块是为内核或其他模块提供功能的代码集合。这些模块可以是 Linux 源码中自带的,也可以是由硬件厂商开发的(可以想象成驱动)。不过内

  • MANAGING THE LINUX KERNEL AND LOADABLE KERNEL MODULES 所有操作系统至少由两个主要组件组成。其中第一个也是最重要的是内核。 内核位于操作系统的中心,控制着操作系统所做的一切,包括管理内存,控制 CPU,甚至控制用户在屏幕上看到的内容。操作系统的第二个元素通常被称为用户区域,几乎包括其他所有元素。 内核被设计成一个受保护或特权的区域,只能由 roo

  • 在本章中,我们将研究如何在Drupal中创建Poll module 。 此模块可帮助您为您的网站创建民意调查。 您可以提出问题,提供任意数量的答案,您的访问者也可以投票。 以下是创建Poll Module的步骤。 Step 1 - 单击菜单栏中的“ Module ”。 Step 2 - 启用“ Poll module ,然后单击“ Save Configuration 。 Step 3 - 单击“

  • 问题内容: 在不深入了解 为什么 的细节的情况下,我正在寻找一种干净的(尽可能)的方法来替换可加载模块中的内核函数和系统调用。我最初的想法是编写一些代码来覆盖某些功能,这些功能将采用原始功能(可能的话, 调用 该功能),然后添加一些自己的代码。关键是我编写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时将改为访问我的函数。 通过将代码放入适当的函数中,我可以轻松地(相对地)直接在内核中执行

  • 问题内容: 所有, 下面的代码来自“ Unix环境中的高级编程”,它创建一个新线程,并打印主线程和新线程的进程ID和线程ID。 在书中,它表示在linux中,此代码的输出将显示两个线程具有不同的进程ID,因为pthread使用轻量级进程来模拟线程。但是,当我在Ubuntu 12.04中运行此代码时,它具有内核3.2,并打印了相同的pid。 那么,新的Linux内核是否会更改pthread的内部实现