中断是一种异步事件,由设备在需要服务时生成。 例如,当串行端口上有可用的数据时,会生成中断, 或以太网数据包已送达。 打扰可让驾驶员在事件后立即了解事件 但不会让驱动程序花时间轮询(主动等待)它。
使用中断的驱动程序的一般架构是 中断处理线程 (IHT) 在驱动程序启动 / 绑定期间创建 操作。 此线程会等待中断发生,当中断发生时,它会执行一些 一种服务操作
以串行端口驱动程序为例。 它可以由于发生以下任一事件而收到中断:
- 已到达一个或多个角色
- 现在可以在聊天室中传输一个或多个字符
- 控制线(例如
DTR
)的状态发生变化。
中断唤醒 IHT。 IHT 通常通过读取某些状态寄存器来确定事件的原因。 然后,它会运行相应的服务函数来处理事件。 完成后,IHT 返回到休眠状态,等待下一次中断。
例如,如果一个字符到达,则 IHT 被唤醒,读取一个状态寄存器, “数据可用”表示“有可用数据”然后调用一个函数 字符从串行端口 FIFO 转移到驱动程序缓冲区。
无需内核级代码
你可能比较熟悉使用中断功能的其他操作系统 服务例程 (ISR)。 这些是内核级处理程序,在特权模式下运行,与 中断控制器硬件。
在 Fuchsia 中,内核处理中断的特权部分 处理,并提供线程级函数供驱动程序使用。
不同之处在于,IHT 在线程级别运行,而 ISR 在 。 它的主要优点是,如果 IHT 崩溃,它只会删除 驱动程序,而失败的 ISR 会切断整个操作系统。
附加到中断
目前,唯一提供中断功能的总线是 PCI 总线。 它支持两种类型:旧版和消息信号中断 (MSI)。
因此,要在 PCI 上使用中断,请执行以下操作:
- 确定您的设备支持哪种类型(旧版或 MSI)、
- 将中断模式设为匹配模式
- 获取设备中断矢量的句柄(通常有一个,但也可能是多个),
- 启动 IHT 后台线程
- 安排 IHT 线程等待中断(在步骤 3 中的句柄上)。
步骤 1
和 2
由“Pci::ConfigureInterruptMode”辅助函数处理:
// Configure interrupt mode.
uint32_t irq_cnt = 1;
fuchsia_hardware_pci::InterruptMode mode;
zx_status_t status = pci.ConfigureInterruptMode(irq_cnt, &mode);
if (status != ZX_OK) {
// handle error
}
Pci::ConfigureInterruptMode() 采用两个参数:
#include <lib/device-protocol/pci.h>
zx_status_t Pci::ConfigureInterruptMode(uint32_t requested_irq_count,
fpci::InterruptMode* out_mode);
第一个参数 requested_irq_count
是所需的中断数量。
第二个参数是 out 参数,用于表示设备支持的中断模式。
配置中断支持后,调用 Pci::MapInterrupt()。 为所选中断创建句柄请注意, Pci::MapInterrupt() 具有以下原型:
#include <lib/device-protocol/pci.h>
zx_status_t Pci::MapInterrupt(uint32_t which_irq, zx::interrupt* out_interrupt);
第一个参数 which_irq
表示与所需的设备相关中断编号,第二个参数
是指向所创建的中断对象的指针。
现在,您已经有了中断句柄。
请注意,绝大多数设备只有一个中断,因此只需传递 对于
which_irq
,0
是正常的。 如果您的设备有多个中断,常见做法是运行for
循环中的 pci::MapInterrupt() 函数 并将句柄绑定到每个中断
等待中断
在 IHT 中,您调用 zx::interrupt::wait() 等待中断。 以下原型适用:
zx_status_t zx::interrupt::wait(zx::time* out_timestamp);
第一个参数可以是 nullptr
(典型),也可以是指向时间的指针
指示中断触发时间的戳记(以纳秒为单位,
相对于使用以下代码提取的单调时钟源:
zx_clock_get_monotonic()
)。
因此,典型的 IHT 可能具有以下形状:
int MyDevice::IrqThread() {
for (;;) {
zx_status_t status = dev->irq_.wait(nullptr);
// do stuff
}
}
设备类有一个成员(此处为 irq_
),即您从中获取的对象
Pci::MapInterrupt():
边缘中断模式与关卡中断模式
中断硬件可以在以下两种模式下运行:"边缘"或“level”。
在边缘模式下,中断在活跃边缘(当硬件 信号从无效变为有效),并且一次性。 也就是说,系统必须将此信号恢复为无效状态,然后才能再次识别它。
在电平模式下,当硬件信号处于 活动状态。
通常,边缘模式在中断专用时使用,而级别模式则 当中断由多台设备共享时使用(因为您希望 中断以保持有效状态,直到所有设备均取消断言其请求行)。
Zircon 内核会视情况自动屏蔽和解除中断。 对于音频级触发的硬件中断, zx::interrupt::wait() 在返回之前屏蔽中断,并在下次调用时取消屏蔽。 对于边缘触发的中断,中断保持未屏蔽状态。
IHT 不应执行任何长时间运行的任务。 对于执行冗长任务的驱动程序,请使用工作线程。
关闭使用中断的驱动程序
如需彻底关闭使用中断的驱动程序,您可以使用 zx::interrupt::destroy() 来取消 zx::interrupt::wait() 调用。
具体想法是,当前台线程确定驱动程序应该 它就会销毁中断句柄,从而导致 IHT 关闭:
void MyDevice::DdkRelease() {
// destroy the handle, this will cause zx_interrupt_wait() to pop
irq_.destroy();
irq_thread_.join();
...
}
int MyDevice::IrqThread() {
...
for(;;) {
zx_status_t status = irq_.wait(nullptr);
if (status == ZX_ERR_CANCELED) {
// we are being shut down, do any cleanups required
...
break;
}
...
}
}
主线程在被请求关闭时,会销毁中断句柄。
这会导致 IHT 的
zx::interrupt::wait()
调用来唤醒并显示错误代码。
IHT 查看错误代码(在本例中为 ZX_ERR_CANCELED
),并
决策。
同时,主线程正在等待加入 IHT 以参与通话
thread::join()。
IHT 退出后,thread::join() 返回,并且主线程
线程可以完成其处理。
我们邀请高级阅读器查看一些与中断相关的 可用的函数: