中断线程化
仅作为学习记录笔记。
想象一下,你正在运行一个对时间要求极其严格的程序(比如控制机器人手臂或处理实时音频),这时一个普通的USB设备突然插入了。在标准Linux中,处理这个USB中断可能会短暂地“冻结”整个系统,导致你的关键任务错过响应时限——这就是传统中断处理的“实时性痛点”。
PREEMPT-RT 这个强大的实时补丁,提供了一套巧妙的解决方案:中断线程化。它像一位精明的调度员,把原本在“中断小黑屋”(关中断状态)里干的重活累活,大部分都挪出来,交给专门的“工人线程”去处理。关键来了:这些“工人”(中断线程)可以被赋予不同的优先级,并且可以被更紧急的任务随时打断(抢占)!
一、什么是中断线程化
1. 普通Linux中断处理
中断是一种异步事件处理机制,用于响应硬件请求,它会打断进程的正常调度和执行,然后调用内核中断处理程序来响应设备的请求。
由于中断处理程序会打断其他进程的运行,所以,为了减少对正常进程运行调度的影响,中断处理程序就需要尽可能快地运行。如果中断本身要做的事情不多,那么处理起来也不会有太大问题;但如果中断要处理的事情很多,中断服务程序就有可能要运行很长时间。特别是,中断处理程序在响应中断时,还会临时关闭中断。若中断任务繁重,这就会导致上一次中断处理完成之前,其他中断都不能响应,甚至丢失中断。
所以Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部:
上半部:快速处理硬件中断,inux不支持中断嵌套,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作,即硬中断(Interrupt Request,IRQ)。
下半部:延迟处理上半部遗留任务,通常以内核线程方式运行,即软中断(SoftIRQ)。
硬中断处理程序(ISR)迅速响应硬件事件,执行最少工作,大部分任务推迟至软中断SoftIRQ或任务队列workqueue。
软中断是一种机制,用于在稍后执行较耗时的中断处理任务。一般以内核线程的方式执行,并且每个 CPU 都对应一个软中断内核线程,名字为 “ksoftirqd/CPU 编号”,如0 号 CPU 对应的软中断内核线程的名字就是 ksoftirqd/0。软中断在软中断上下文运行,执行时机有2个,一个是硬件中断返回后立即执行,这时和硬件中断一样,完全抢占进程上下文,不允许被调度和抢占,其执行时间为不超过2ms,如果执行的软中断超过了2ms会将软中断延迟到软中断线程 “ksoftirqd/CPU 编号”中执行。
系统中软硬件中断通过proc 文件系统来查看:
/proc/softirqs 提供了软中断的运行情况;
/proc/interrupts 提供了硬中断的运行情况。
2. 实时性的不足
传统的中断处理方式虽然提升了系统的吞吐量,但从实时任务响应实时性方面来看存在以下不足:
- 中断禁用:inux不支持中断嵌套,在硬中断处理过程中,其他中断会被屏蔽,增加了延迟。
- 中断串行处理:inux不支持中断嵌套,同一cpu上的外设中断处理串行执行,这在非紧急外设中断多的情况下导致紧急外设中断处理的不确定性。
- 不可预知的延迟:不同的驱动程序,硬中断处理程序执行需要的时间不同,不可预知,可能导致高优先级任务被中断抢占延迟执行,影响任务的实时性能。
- 软中断的不确定性:软中断在硬中断退出后前期处理是完全抢占进程上下文的,它的处理时间和调度顺序也可能影响到实时任务的执行。
普通 Linux 处理中断的方式,就像让所有紧急事件都挤在一条没有应急车道的单行线上,处理时间没谱,还经常封路。这导致整个系统的响应时间不稳定、不可预测、可能很长。对于那些要求必须在极短时间内、毫秒不差地做出反应的应用(比如控制机器人、实时音视频、工业自动化),这种不确定性是致命的,所以它不适合高实时性场景。
3. 中断线程化
为了解决上述问题,PREEMPT-RT补丁引入了中断线程化机制,于2009年合入linux主线版本2.6.30。顾名思义就是将原来硬件中断上下文的中断处理的部分也通过一个内核线程来处理,除了代码中明确指明非中断线程化的中断和系统硬件timer中断外的中断均通过中断线程来处理,中断线程默认是一个实时线程,调度类为SCHED_FIFO,实时优先级为50。同时将SoftIRQ全部放到ksoftirqd/CPU线程中执行,ksoftirqd/CPU为普通非实时任务不变。通过ps命令可以看到中断线程。
中断线程化通过以下方式提升了Linux系统的实时性:
- 减少中断禁用时间:由原本整个硬件中断处理过程都需要屏蔽全局中断,变成仅响应中断,唤醒中断线程部分会屏蔽全局中断,这部分只执行最少量的处理,减少了中断禁用的时间,降低了整体延迟。
- 优先级管理:中断处理程序作为内核线程,基本不会屏蔽全局中断,并且不同优先级的外设中断线程,可以被赋予不同的优先级,从而避免低优先级的外设中断阻塞高优先级外设、任务的执行。
- 可抢占性:中断线程作为可调度的内核线程,可以被高优先级任务抢占,使系统响应更加灵活和实时。
- 可预期性增强:中断处理时间变得更加可控和可预见,提高了系统的实时性能。
- 系统吞吐量降低:中断线程化后,CPU上下文切换增多,那 CPU 的处理效率就会打一定折扣。
通过这些改进,PREEMPT-RT的中断线程化机制有效地解决了传统中断处理方式在实时性上的不足,使Linux系统能够更好地满足高实时性应用的需求。
二、中断线程优先级配置
所有外设的中断线程默认一个实时线程,调度类为SCHED_FIFO,实时优先级为50,对于实时性要求高的外设应该设置更高的优先级,如何才能修改中断线程的优先级?方式有两种,第一种最直接的办法,驱动代码中添加代码修改,但这样不灵活。另外一种是通过chrt工具来修改。
使用chrt修改中断线程优先级(参考chrt文章)
例如,要将PID为1234的线程的实时优先级设置为50,可以执行以下命令:
chrt -f -p 50 1234
这里,-f
表示设置实时优先级,-p
表示操作对象是进程。同样,只有root用户才能修改线程的实时优先级。
注意:在使用这些命令时,请确保已正确指定PID,以免对其他进程造成不必要的影响。
对中断线程化的学习就到这里