文章目录
- 0. 引言
- 1. 设备与处理器中断交互机制
- 1.1 交互时序图
- 1.2 时序图说明
- 1.3 用户空间中断处理方法
- 2. 中断模块设计要点
- 3. 代码说明
- 3.1 `Interrupts` 类
- 3.2 中断处理
- 3.3 `start` 方法
0. 引言
本文介绍在 Linux-ARM 系统中利用中断与外设(如 DSP、DAC、扫描仪等)交互的模块,实现低延迟的中断响应服务。外设通过 UIO 驱动暴露 /dev/uio
设备节点,用户空间程序可以通过这些节点来处理中断。
本方案将使用到select,select的高效使用请:Linux编程:使用 select高效的 UART 通信
1. 设备与处理器中断交互机制
-
设备侧(裸机程序/外设):
- 当设备完成任务或发生事件时,触发硬件中断信号。
- 中断信号通过硬件线路发送至中断控制器。
-
处理器侧(运行操作系统):
- 中断控制器接收信号,判断中断类型和优先级。
- 操作系统内核处理并调度相应的中断服务程序(ISR)。
- ISR 可能位于内核空间,也可能通过
/dev/uio
让用户空间程序处理。
1.1 交互时序图
1.2 时序图说明
- 设备触发中断:设备触发硬件中断信号,中断信号通过中断线传递给控制器
- 中断控制器处理:控制器生成中断请求并发送给处理器。
- 处理器接收中断:操作系统接收到中断请求并暂停当前任务。
- 调用 ISR:操作系统调用对应的中断服务程序(ISR)。
- 处理中断:ISR 执行预定义的处理逻辑。
- 中断处理完成:ISR 处理完毕后,返回内核。
- 通知控制器:处理器通知中断控制器处理完成。
- 设备继续执行:设备得到响应后继续运行。
1.3 用户空间中断处理方法
在用户空间处理中断时,通常使用 UIO(Userspace I/O)机制。设备驱动将中断映射到 /dev/uioX
设备文件,用户空间程序通过 select
等系统调用来等待并处理中断。
// 用户空间中断处理示例
int fd = open("/dev/uio0", O_RDWR);
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN;while (true) {int ret = poll(&fds, 1, -1);if (ret > 0) {uint32_t info;read(fd, &info, sizeof(info)); // 读取中断信息// 处理中断事件// ...// 重新使能中断write(fd, &info, sizeof(info));}
}
2. 中断模块设计要点
中断管理模块的设计包括以下要点:
- 线程优先级设置:使用
sched_setscheduler
设置线程的优先级和调度策略。 - 实时线程:通过
SCHED_FIFO
或SCHED_RR
确保线程能够及时获得 CPU 时间片。 - 中断信号处理:确保实时线程能够正确处理中断信号,避免任务被不必要地打断。
以下展示了如何通过 /dev/uio
设备文件接收中断,并利用 select
等待中断。
#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
#include <functional>
#include <unordered_map>
#include <string>
#include <iostream>
#include <thread>
#include <sched.h>
#include <sys/types.h>
#include <pthread.h>class Interrupts {public:// 构造函数,初始化成员变量Interrupts();// 中断初始化int init();// 启动中断处理线程void start();// 注册中断并连接到回调函数int registerInterrupt(const std::string& interrupt_name, std::function<void(void)> interrupt_handler);// 等待中断事件int waitForInterrupt();// 处理中断事件int processInterrupts();private:// 保存文件描述符与回调函数的映射std::unordered_map<int, std::function<void(void)>> interrupt_handlers_;// 最大文件描述符int max_interrupt_fd_;// 文件描述符集合fd_set master_set_;fd_set backup_set_;// 实时线程优先级static constexpr int kInterruptThreadPriority = 50; // 设置线程优先级,假设为50(越高越优先)
};// 构造函数,初始化成员变量
Interrupts::Interrupts() : max_interrupt_fd_(-1) {FD_ZERO(&master_set_);FD_ZERO(&backup_set_);
}// 初始化中断源
int Interrupts::init() {std::function<void(void)> dma_handler = []() {fprintf(stdout, "DMA interrupt triggered!\n");};if (registerInterrupt("dma_irq", dma_handler) < 0) {fprintf(stderr, "Failed to register DMA interrupt!\n");return -1;}return 0;
}// 注册中断
int Interrupts::registerInterrupt(const std::string& interrupt_name, std::function<void(void)> interrupt_handler) {std::string interrupt_path = "/dev/" + interrupt_name;int fd = open(interrupt_path.c_str(), O_RDWR);if (fd < 0) {fprintf(stderr, "Failed to open interrupt device: %s\n", interrupt_path.c_str());return -1; // 打开文件失败}uint32_t info = 1; // 解锁中断if (write(fd, &info, sizeof(info)) != sizeof(info)) {fprintf(stderr, "Failed to unlock interrupt: %s\n", interrupt_path.c_str());close(fd);return -1; // 写入失败}FD_SET(fd, &master_set_);if (fd > max_interrupt_fd_) {max_interrupt_fd_ = fd;}interrupt_handlers_[fd] = interrupt_handler;return 0;
}// 设置线程的调度策略和优先级
void Interrupts::setThreadPriority() {struct sched_param param;param.sched_priority = kInterruptThreadPriority;// 设置调度策略为 FIFOif (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {fprintf(stderr, "Failed to set thread priority!\n");exit(1); // 设置失败,退出程序}
}// 等待中断事件
int Interrupts::waitForInterrupt() {backup_set_ = master_set_;if (select(max_interrupt_fd_ + 1, &backup_set_, nullptr, nullptr, nullptr) < 0) {if (errno != EINTR) {fprintf(stderr, "Error in select() while waiting for interrupt\n");return -1; // select 出错}return 0; // 被信号中断,继续等待}return 0;
}// 处理中断事件
int Interrupts::processInterrupts() {// 遍历所有已注册的中断处理器for (auto& entry : interrupt_handlers_) {int fd = entry.first;if (FD_ISSET(fd, &backup_set_)) {uint32_t info;if (read(fd, &info, sizeof(info)) != sizeof(info)) {fprintf(stderr, "Failed to read interrupt data from fd: %d\n", fd);continue; // 读取失败,跳过此中断}entry.second(); // 调用中断处理器info = 1; // 解除中断屏蔽if (write(fd, &info, sizeof(info)) != sizeof(info)) {fprintf(stderr, "Failed to write interrupt data back to fd: %d\n", fd);}}}return 0;
}// 启动中断处理线程
void Interrupts::start() {// 设置当前线程的优先级setThreadPriority();// 启动一个循环处理while (true) {if (waitForInterrupt() == 0) {if (processInterrupts() != 0) {fprintf(stderr, "Error processing interrupts\n");}}}
}
3. 代码说明
3.1 Interrupts
类
- 构造函数 (
Interrupts
):初始化文件描述符集合和最大文件描述符。 init
:初始化中断源,例如为 DMA 注册中断。registerInterrupt
:通过/dev/uioX
设备文件注册外设中断,解除中断屏蔽并存储回调函数。waitForInterrupt
:通过select
等待中断事件发生。processInterrupts
:处理触发的中断事件,调用对应的回调函数。
3.2 中断处理
当 DMA 中断触发时,会调用 dma_handler
来处理。例如,打印 "DMA interrupt triggered!"
。
3.3 start
方法
start
方法通过 sched_setscheduler
和 SCHED_FIFO
设置线程优先级。它启动一个循环来等待中断并处理。