中断API之__tasklet_hi_schedule

本文详细解析了函数void__tasklet_hi_schedule(structtasklet_struct*t)的功能及其实现过程。该函数用于将指定的软中断tasklet添加到tasklet_hi_vec队列尾部,并触发HI_SOFTIRQ类型的软中断。
void __tasklet_hi_schedule(struct tasklet_struct *t),函数的主要作用是将参数t代表的软中断
添加到向量tasklet_hi_vec的尾部,并触发一个软中断.
其使用的例程如下:
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
	//检查这个tasklet是否正在被调度,如果没有被调用才能被加入到tasklet_hi_vec 尾部.
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_hi_schedule(t);
}
其源码分析如下:
void __tasklet_hi_schedule(struct tasklet_struct *t)
{
	unsigned long flags;
//在当前cpu上禁止中断
	local_irq_save(flags);
	t->next = NULL;
	//通过__this_cpu_read 读取tasklet_hi_vec.tail的值。此时值是null,然后把t赋值给tasklet_hi_vec.tail
	*__this_cpu_read(tasklet_hi_vec.tail) = t;
	// 把t->next 赋值给tasklet_hi_vec.tail。所以tasklet_hi_vec.tail得最后一个元素是null
	__this_cpu_write(tasklet_hi_vec.tail,  &(t->next));
	//触发一个HI_SOFTIRQ的软件中断
	raise_softirq_irqoff(HI_SOFTIRQ);
	//使能当前cpu上的中断
	local_irq_restore(flags);
}

这是一个非常好的问题。 你正在开发一个内核级 QoS 调度器,目前使用 `tasklet_hi_schedule()` 来触发高优先级处理。现在考虑升级为直接在 **`ksoftirqd` 上下文**中运行任务(即通过注册 softirq handler),以获得更稳定、可预测的调度行为。 --- ## ✅ 回答总览: > 使用 `ksoftirqd` 替代 `tasklet_hi_schedule` 的本质是:**从 tasklet 机制迁移到自定义软中断处理函数**。 > > 虽然能带来更好的实时性和系统集成性,但也伴随着 **显著的风险和复杂度提升**,尤其是在稳定性、死锁、调试难度方面。 --- ## 🔍 一、基本概念对比 | 特性 | `tasklet_hi_schedule()` | 自定义 softirq + `ksoftirqd` | |------|--------------------------|-------------------------------| | 执行上下文 | softirq / ksoftirqd | ksoftirqd 线程或中断上下文 | | 调度方式 | 延迟执行,高优先级 tasklet | 注册到 softirq_vec,由内核调度 | | 并发控制 | 同 CPU 不重入,多 CPU 可并发 | 同 CPU 不重入,但需自行加锁保护共享数据 | | 是否可睡眠 | ❌ 不能 sleep | ❌ 绝对禁止 sleep | | 开发复杂度 | 简单 | 复杂(需注册 softirq 类型) | | 实时性 | 较好 | 更好(与网络协议栈同级) | --- ## ⚠️ 二、使用 `ksoftirqd` / softirq 的主要风险 ### ❌ 风险 1:必须注册新的 softirq 类型 —— 需修改全局状态,有冲突可能 你需要调用: ```c open_softirq(QOS_SOFTIRQ, qos_softirq_handler); ``` 其中 `QOS_SOFTIRQ` 必须是一个未被使用的 softirq 编号(如 `NET_RX_SOFTIRQ + 1`)。 👉 **风险点**: - 如果编号已被其他模块占用 → 冲突、覆盖 → 系统崩溃 - 没有动态分配机制,softirq vector 全局唯一且数量有限(通常只有 10 个左右) - 若模块卸载后未调用 `close_softirq`,会导致后续加载失败或异常 📌 建议:尽量复用现有 softirq(如 `NET_RX_SOFTIRQ` 的处理流程),而不是新增。 --- ### ❌ 风险 2:softirq 上下文严禁阻塞、休眠、持有 mutex 你在 softirq handler 中: - 不能调用 `copy_to_user`, `kmalloc(..., GFP_KERNEL)` 等可能导致睡眠的操作 - 不能获取可能导致调度的锁(如 `mutex`) - 不能进行文件操作、用户空间访问 否则会触发 **“scheduling while atomic”** 错误,导致 kernel panic。 示例错误日志: ``` BUG: scheduling while atomic: swapper/0/0x10000100 ``` 👉 即使只是短暂地 `msleep(1)`,也会导致系统挂起。 --- ### ❌ 风险 3:长时间运行 softirq 会阻塞其他关键 softirq(如网络收发) Linux 软中断是轮询处理的。如果你的 `qos_softirq_handler` 处理太慢或无限循环: ```c while ((skb = dequeue())) { netif_receive_skb(skb); // 可能再次触发 softirq } ``` → 导致当前 CPU 的 softirq 处理无法退出 → `NET_RX`, `NET_TX`, `TIMER` 等都被饿死 → 网络卡顿、定时器失效。 这是典型的 **softirq 饥饿(softirq starvation)** 问题。 ✅ 正确做法:限制每次处理包数,在 budget 耗尽时返回,等待下次调度。 --- ### ❌ 风险 4:缺乏良好的调试支持,出错难定位 softirq 上下文没有进程上下文信息,堆栈追踪困难,`printk` 是主要调试手段。 一旦发生空指针、内存越界等错误,容易导致 hard lockup 或 silent crash。 --- ### ❌ 风险 5:SMP 多核同步更复杂 虽然 softirq 在每个 CPU 上独立运行,但如果多个 CPU 访问同一个全局队列(如 `qos_queue.high_pri`),你必须确保: - 每个队列有自己的 per-CPU 锁 - 或者使用 `spin_lock_irqsave()` 严格保护 - 否则会出现跨 CPU 竞态 ⚠️ 特别注意:`spin_lock` 在 softirq 上下文中必须搭配 `_irqsave` 版本,否则中断可能重入造成死锁。 --- ## ✅ 三、相比 tasklet,有什么优势? 尽管有风险,但在某些场景下仍值得考虑: | 优势 | 说明 | |------|------| | 更低延迟 | softirq 比 tasklet 更早被处理(尤其是 high-priority) | | 与内核原生机制对齐 | 如 `NET_RX_SOFTIRQ` 就是 softirq,你的逻辑可以无缝集成 | | 更适合高吞吐场景 | tasklet 本质上是 softirq 的封装,绕过它可减少一层调度开销 | --- ## ✅ 四、安全替代方案推荐(规避风险) 与其直接注册 softirq,不如采用以下更稳妥的方法: ### ✅ 方案 1:继续使用 `tasklet_hi_schedule()`,但优化调度频率和 budget ```c // 提高处理能力 high_buget = 64; low_buget = 64; // 出队后如果还有数据,立即重新调度 if (qos_queue.high_pri.head || qos_queue.low_pri.head) tasklet_hi_schedule(&qos_tasklet); ``` 优点:简单、安全、可控。 --- ### ✅ 方案 2:将 QoS 队列集成进 NAPI poll 函数(最佳实践) 在网络驱动或虚拟设备中,把你的 `rx_qos_scheduler` 作为 `napi_struct` 的一部分,在 `poll` 函数中统一处理。 例如: ```c int my_napi_poll(struct napi_struct *napi, int budget) { int work_done = 0; while (work_done < budget && (skb = dequeue_from_qos_queue())) { netif_receive_skb(skb); work_done++; } return work_done; } ``` ✅ 优势: - 完全融入内核网络栈 - 支持动态权重、节流、公平调度 - 避免额外的 softirq 开销 --- ### ✅ 方案 3:使用 `workqueue`(仅适用于非实时场景) 如果你不需要微秒级响应,可以用 `system_highpri_wq`: ```c schedule_work(&qos_work); ``` 但它运行在进程上下文,允许睡眠,适合做批量处理,不适合实时 QoS。 --- ## ✅ 总结:是否应该用 `ksoftirqd` 替代 `tasklet_hi_schedule`? | 评估项 | 结论 | |--------|-------| | 是否必要? | ❌ 不必要。`tasklet_hi_schedule` 已足够高效 | | 是否安全? | ⚠️ 有风险,尤其新手易引发死锁、饥饿 | | 是否易于维护? | ❌ 难调试、难移植、依赖全局 softirq 编号 | | 推荐做法? | ✅ 保持 tasklet;长期可考虑 NAPI 集成 | > 📌 **建议:现阶段不要引入自定义 softirq。保持使用 `tasklet_hi_schedule`,并通过增加 budget 和加入 hysteresis 控制来解决“10秒抖动”问题。** ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值