中断申请后在close(fd)之后,进行中断释放 free_irq,但是有一下报错

本文探讨了一种特定的Linux内核错误:尝试释放已释放的中断(IRQ)。此问题通常出现在应用程序退出时,由于未能正确管理中断资源而导致。文章提供了解决方案,建议将请求中断(request_irq)的操作从驱动初始化阶段移至如设备打开(open)等其他时机。
------------[ cut here ]------------
WARNING: at kernel/irq/manage.c:1147 __free_irq+0x108/0x17c()
Trying to free already-free IRQ 19

Modules linked in: sar_adc hi3518_adec(P) hi3518_aenc(P) hi3518_ao(P) hi3518_ai(P) hi3518_sio(P) hidmac acodec(P) hi3518_isp(P) pwm hi_rtc hirtc hi_i2c gpioi2c ssp_sony hi3518_ive(P) hi3518_vda(P) hi3518_region(P) hi3518_rc(P) hi3518_jpege(P) hi3518_h264e(P) hi3518_chnl(P) hi3518_group(P) hi3518_venc(P) hi3518_vou(P) hi3518_vpss(P) hi3518_viu(P) hi3518_dsu(P) hi3518_tde(P) hi3518_sys(P) hi3518_base(P) mmz



上面错误是app在退出的时候,找不到相应的中断号进行释放,导致崩溃,只要把request_irq放在open里面或者其他地方,就是不要放在驱动的初始化地方。

<think>我们面临的任务是:在Linux内核模块中,通过poll机制和/proc文件系统,实现用户态程序能够读取由内核驱动触发的中断信息,并进一步读取CPLD寄存器。 整体思路: 1. 创建一个/proc文件,用户态程序可以通过读取这个文件来获取中断信息。 2. 在驱动中,当中断发生时,记录中断信息(例如时间、中断号等),并唤醒等待队列,以便用户态程序知道有中断发生。 3. 使用poll机制,使得用户态程序可以阻塞地等待中断事件,当有中断发生时,poll会返回,用户态程序可以读取/proc文件获取中断信息。 4. 用户态程序在读取中断信息后,可以进一步执行读取CPLD寄存器的操作(这部分在用户态完成,通常通过/dev/mem或sysfs等方式,但这里我们只关注如何通知用户态中断发生)。 步骤详解(内核模块部分): a. 创建/proc文件入口。 b. 实现/proc文件的read、write(如果需要)、poll等操作。 c. 初始化一个等待队列,用于在中断处理函数和用户态读取进程之间同步。 d. 在中断处理函数中,记录中断信息,并唤醒等待队列。 e. 在proc文件的poll操作中,将当前进程加入等待队列,并返回事件掩码(POLLIN表示有数据可读)。 用户态程序: 用户态程序通过poll()系统调用监视/proc文件,当有中断发生时,poll返回,然后程序读取/proc文件内容(即中断信息),然后进行读取CPLD寄存器的操作。 注意:这里我们只实现内核模块中通过/proc文件通知中断事件,读取CPLD寄存器的操作在用户态完成(具体方法不在本模块中)。 下面我们编写一个示例内核模块: 假设: - 我们有一个硬件中断(例如IRQ号为5),在模块中注册这个中断。 - 创建/proc/interrupt_event文件。 内核模块代码:</think>### 解决方案:使用 poll + /proc 文件实现中断通知和 CPLD 寄存器读取 #### 内核模块实现 以下是完整的内核模块代码,实现中断处理、proc 文件操作和 poll 支持: ```c #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/poll.h> #include <linux/uaccess.h> #include <linux/fs.h> #include <linux/sched.h> #include <linux/io.h> #define IRQ_NUM 5 // 硬件中断号(根据实际修改) #define PROC_NAME "cpld" // /proc 文件名 // 全局变量 static DECLARE_WAIT_QUEUE_HEAD(irq_waitq); static volatile int irq_occurred = 0; static char interrupt_info[128] = {0}; static struct proc_dir_entry *proc_entry; // 模拟读取 CPLD 寄存器(实际需替换为硬件操作) static u32 read_cpld_register(void) { // 这里应替换为实际的硬件寄存器读取操作 // 示例:return ioread32(cpld_base + offset); return 0xDEADBEEF; // 返回模拟值 } // 中断处理函数 static irqreturn_t irq_handler(int irq, void *dev_id) { u32 reg_value = read_cpld_register(); // 读取CPLD寄存器 // 记录中断信息(时间戳 + 寄存器值) snprintf(interrupt_info, sizeof(interrupt_info), "[%lld] IRQ %d: CPLD=0x%08X\n", ktime_get_ns(), irq, reg_value); irq_occurred = 1; // 设置中断标志 wake_up_interruptible(&irq_waitq); // 唤醒等待队列 return IRQ_HANDLED; } // proc文件读取函数 static ssize_t proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { if (*ppos > 0 || !irq_occurred) return 0; // 将中断信息复制到用户空间 int len = strlen(interrupt_info); if (copy_to_user(buf, interrupt_info, len)) return -EFAULT; *ppos = len; irq_occurred = 0; // 重置中断标志 return len; } // proc文件poll函数 static unsigned int proc_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; poll_wait(file, &irq_waitq, wait); // 加入等待队列 if (irq_occurred) mask |= POLLIN | POLLRDNORM; // 有数据可读 return mask; } // proc文件操作结构体 static struct proc_ops proc_fops = { .proc_read = proc_read, .proc_poll = proc_poll, }; // 模块初始化 static int __init cpld_driver_init(void) { // 注册中断处理程序 if (request_irq(IRQ_NUM, irq_handler, IRQF_SHARED, "cpld_irq", NULL)) { printk(KERN_ERR "Failed to request IRQ %d\n", IRQ_NUM); return -EIO; } // 创建/proc文件 proc_entry = proc_create(PROC_NAME, 0666, NULL, &proc_fops); if (!proc_entry) { free_irq(IRQ_NUM, NULL); return -ENOMEM; } printk(KERN_INFO "CPLD driver loaded. IRQ:%d\n", IRQ_NUM); return 0; } // 模块退出 static void __exit cpld_driver_exit(void) { remove_proc_entry(PROC_NAME, NULL); free_irq(IRQ_NUM, NULL); printk(KERN_INFO "CPLD driver unloaded\n"); } module_init(cpld_driver_init); module_exit(cpld_driver_exit); MODULE_LICENSE("GPL"); ``` #### 用户态程序 用户态程序通过 poll 监控 `/proc/cpld` 文件,读取中断信息: ```c #include <stdio.h> #include <fcntl.h> #include <poll.h> #include <unistd.h> int main() { const char *proc_file = "/proc/cpld"; struct pollfd pfd = { .fd = open(proc_file, O_RDONLY), .events = POLLIN }; if (pfd.fd < 0) { perror("Open /proc/cpld failed"); return 1; } while (1) { int ret = poll(&pfd, 1, -1); // 阻塞等待中断 if (ret > 0 && (pfd.revents & POLLIN)) { char buf[256]; ssize_t len = read(pfd.fd, buf, sizeof(buf)-1); if (len > 0) { buf[len] = '\0'; printf("Interrupt received: %s", buf); // 这里添加读取CPLD寄存器的实际代码 // 示例:read_cpld_register(); } lseek(pfd.fd, 0, SEEK_SET); // 重置文件指针 } else { perror("Poll error"); break; } } close(pfd.fd); return 0; } ``` ### 原理解释 1. **中断处理流程**: - 硬件中断触发内核的 `irq_handler` - 中断处理中读取 CPLD 寄存器值并记录时间戳 - 设置 `irq_occurred` 标志并唤醒等待队列 2. **/proc 文件系统**: - 通过 `proc_create` 创建 `/proc/cpld` 虚拟文件 - `proc_read` 将中断信息复制到用户空间 - `proc_poll` 实现 poll 的等待机制 3. **poll 同步机制**: - 用户态调用 poll 阻塞等待 - 内核通过 `poll_wait` 将进程加入等待队列 - 中断发生后 `wake_up_interruptible` 唤醒进程 4. **数据流**: ```mermaid graph LR A[硬件中断] --> B[中断处理函数] B --> C[记录寄存器值] C --> D[设置标志位] D --> E[唤醒用户进程] E --> F[用户态读取/proc文件] F --> G[执行CPLD操作] ``` ### 实际应用注意事项 1. **硬件适配**: - 修改 `IRQ_NUM` 为实际中断号 - 在 `read_cpld_register` 中实现真实的寄存器访问(如使用 `ioremap`/`iounmap`) 2. **安全增强**: - 添加 spinlock 保护共享数据 - 限制 proc 文件最大读取次数 - 实现写操作进行调试控制 3. **性能优化**: - 使用非阻塞 I/O 减少延迟 - 添加环形缓冲区存储多次中断 - 支持多用户同时监控 4. **错误处理**: - 检查中断注册返回值 - 处理内存分配失败 - 防范用户空间缓冲区溢出
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值