软中断上下文能够睡眠吗?

这个问题实际上是一个老生常谈的问题,答案也很简单, Linux 在软中断上下文中是不能睡眠的,原因在于 Linux 的软中断实现上下文有可能是中断上下文,如果在中断上下文中睡眠,那么会导致 Linux 无法调度,直接的反应是系统 Kernel Panic ,并且提示 dequeue_task 出错。所以,在软中断上下文中,我们不能使用信号量等可能导致睡眠的函数,这一点在编写 IO 回调函数时需要特别注意。在最近的一个项目中,我们在 dm-io callback 函数中去持有 semaphore 访问竞争资源,导致了系统的 kernel panic 。其原因就在于 dm-io 的回调函数在 scsi soft irq 中执行, scsi soft irq 是一个软中断,其会在硬中断发生之后被执行,执行上下文为中断上下文。

 

       中断上下文中无法睡眠的原因大家一定很清楚,原因在于中断上下文不是一个进程上下文,其没有一个专门用来描述 CPU 寄 存器等信息的数据结构,所以无法被调度器调度。如果将中断上下文也设计成进程上下文,那么调度器就可以对其进行调度,如果在开中断的情况下,其自然就可以 睡眠了。但是,如果这样设计,那么中断处理的效率将会降低。中断(硬中断、软中断)处理都是些耗时不是很长,对实时性要求很高,执行频度较高的应用,所 以,如果采用一个专门的后台 daemon 对其处理,显然并不合适。

 

       Linux 对中断进行了有效的管理,一个中断发生之后,都会通过相应的中断向量表获取该中断的处理函数。在 Linux 操作系统中都会调用 do_IRQ 这个函数,在这个函数中都会执行 __do_IRQ (), __do_IRQ 函数调用该中断的具体执行函数。在执行过程中,该函数通过中断号找到具体的中断描述结构 irq_desc ,该结构对某一具体硬件中断进行了描述。在 irq_desc 结构中存在一条链表 irqaction ,这条链表中的某一项成员都是一个中断处理方法。这条链表很有意思,其实现了中断共享,例如传统的 PCI 总线就是采用共享中断的方法,该链表中的一个节点就对应了一个 PCI 设备的中断处理方法。在 PCI 设备驱动加载时,都需要注册本设备的中断处理函数,通常会调用 request_irq 这个函数,通过这个函数会构造一个具体的 irq action ,然后挂接到某个具体 irq_desc action 链表下,实现中断处理方法的注册。在 __do_IRQ 函数中会通过 handle_IRQ_event ()函数遍历所有的 action 节点,完成中断处理过程。到目前为止,中断处理函数 do_IRQ 完 成的都是上半部的工作,也就是设备注册的中断服务程序。在中断上半部中,通常都是关中断的,基本都是完成很简单的操作,否则将会导致中断的丢失。耗时时间 相对较长,对实时性要求不是最高的应用都会被延迟处理,都会在中断下半部中执行。所以,在中断上半部中都会触发软中断事件,然后执行完毕,退出服务。

 

       __do_IRQ 完成之后,返回到 do_IRQ 函数,在该函数中调用了一个非常重要的函数 irq_exit (),在该函数中调用 invoke_softirq (), invoke_softirq 调用 do_softirq ()函数,执行软中断的操作。此时,程序的执行环境还是中断上下文,但是与中断上半部不同的是,软中断执行过程中是开中断的,能够被硬中断而中断。所以,如果用户的程序在软中断中睡眠,操作系统该如何调度呢?只有 kernel panic 了。另外,软中断除了上述执行点之外,还有其他的执行点,在内核中还有一个软中断的 daemon 处理软中断事务,驱动程序也可以自己触发一个软中断事件,并且在软中断的 daemon 上下文中执行。但是硬中断触发的事件都不会在这个 daemon 的上下文中执行,除非修改 Linux 中的 do__IRQ 代码。

 

       上述对软中断的执行做了简要分析,我对 Linux 中的硬中断管理机制做了一些代码分析,这一块代码量不是很大,可移植性非常的好 ~~ 建议大家阅读,对我上述的分析和理解存在什么不同意见,欢迎大家讨论。

 

本文转载自:http://blog.ednchina.com/tiloog/182873/message.aspx

 

<think>嗯,用户想了解在软中断上下文使用读写锁的方法。这需要结合Linux内核中软中断的特殊性和锁机制的特性来分析。 用户可能正在开发内核模块或驱动,需要在软中断处理中保护共享数据。根据引用[2]和[4]的关键信息:软中断属于中断上下文睡眠,因此只能使用自旋锁系列的同步机制。 仔细看引用材料:信号量会导致睡眠所以能在中断上下文使用(引用[2]),而软中断属于中断上下文(引用[4])。那么读写锁的选择范围就缩小到自旋锁的变体——读写自旋锁(rwlock_t)或排队读写锁(qrwlock)。 引用[3]提到读写自旋锁的读者饿死写者问题,以及排队读写锁的改进方案。这对用户应该很有价值,因为软中断对时效性要求高。 用户可能需要知道具体API用法。虽然引用没直接给出代码,但根据内核编程惯例: - rwlock_t需要rwlock_init初始化 - 读锁用read_lock_irqsave/read_unlock_irqrestore - 写锁用write_lock_irqsave/write_unlock_irqrestore - 排队读写锁接口类似但前缀是qread/qwrite 特别要注意的是,在软中断中必须用_irqsave变体来保存中断状态,因为软中断可能抢占硬中断(引用[4])。用户如果忽略这点可能导致死锁。 考虑到性能,引用[1]提到rwlock_t通常值得使用,但在软中断这种非睡眠场景下可能是最优解。如果用户的数据访问模式读多写少且持有时间短,用读写自旋锁;如果写操作频繁或有饿死风险,建议转向排队读写锁。 最后想到需要提醒用户:软中断会并发运行在多核上,所以锁必须全局共享。另外测试时最好打开CONFIG_DEBUG_SPINLOCK检测死锁。 </think> 在Linux内核的软中断上下文使用读写锁时,必须遵守中断上下文的特殊限制。以下是关键要点和实现方法: ### 核心原则 1. **禁用睡眠操作** 软中断属于中断上下文(引用[4]),**使用任何可能引发睡眠的锁**,如信号量(`struct rw_semaphore`)或互斥锁(引用[2])。 2. **必须使用自旋锁变体** 仅能选择以下两种读写锁: - **读写自旋锁**(`rwlock_t`) - **排队读写锁**(`qrwlock`,需配置`CONFIG_QUEUED_RWLOCKS`) ### 具体实现步骤 #### 1. 定义并初始化锁 ```c #include <linux/rwlock.h> // 或 #include <linux/qrwlock.h> 若用排队锁 rwlock_t my_rwlock; // 普通读写自旋锁 // qrwlock my_qrwlock; // 排队读写锁 void init_lock(void) { rwlock_init(&my_rwlock); // qrwlock_init(&my_qrwlock); } ``` #### 2. 在软中断使用锁(以读锁为例) ```c void softirq_handler(void) { unsigned long flags; // 获取读锁(禁用本地中断) read_lock_irqsave(&my_rwlock, flags); // 或 qread_lock_irqsave(&my_qrwlock, flags); /*-- 安全读取共享数据 --*/ // 释放读锁(恢复中断状态) read_unlock_irqrestore(&my_rwlock, flags); // 或 qread_unlock_irqrestore(&my_qrwlock, flags); } ``` #### 3. 写锁使用(同理) ```c write_lock_irqsave(&my_rwlock, flags); // 或 qwrite_lock_irqsave(&my_qrwlock, flags); /*-- 修改共享数据 --*/ write_unlock_irqrestore(&my_rwlock, flags); // 或 qwrite_unlock_irqrestore(&my_qrwlock, flags); ``` ### 关键注意事项 1. **中断屏蔽必要性** 必须使用`_irqsave/_irqrestore`变体(如示例),防止锁操作期间被硬中断打断导致死锁(引用[4])。 2. **锁选择建议** - 优先用**排队读写锁**(`qrwlock`),避免普通`rwlock_t`的**写者饿死问题**(引用[3])。 - 若临界区极短(如<10条指令),可用普通`rwlock_t`。 3. **性能考量** 读写锁在**读多写少**时性能优势显著,但软中断中应确保锁持有时间**极短**(引用[1])。若写操作频繁,建议改用普通自旋锁。 > **示例场景**:在软中断中更新网络收包统计计数(多核并发读,偶发写)。 > 使用`qrwlock`保护计数变量,读锁用于读取统计,写锁在重置计数器时使用。 --- ### 锁类型对比表 | **特性** | `rwlock_t` | `qrwlock` (排队锁) | |-------------------|---------------------|------------------------| | 写者饥饿问题 | 可能发生(引用[3]) | 已解决 | | 性能开销 | 较低 | 略高(队列维护) | | 适用场景 | 临界区极短 | 通用场景 | | 中断上下文兼容性 | ✅ | ✅ | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值