PREEMPT_RT 技术实现:Threaded interrupt handler

中断线程化,使得高优先级的中断变成了普通的内核线程 ,和其他程序共用CPU,通过优先级 来决定谁能够获取CPU的使用权 。将一些不重要的中断改在线程中执行,只要RT任务优先级比中断线程优先级高,就可以优先执行,以此来提高系统的实时性能

传统主线内核默认中断行为

普通硬中断上下文:关闭硬件中断 ,同时隐式禁止抢占、禁止软中断,中断处理全程在硬中断上下文执行。

为了清晰描述,我用excel画了下面这张图。从图中可以看到,硬中断、软中断、特定的tasklet 都不在调度器的管辖范围 内,运行在interrupt context 中。无论是内核线程还是用户程序,在中断发生时,都需要立刻让出CPU的使用权 。在实时系统中,其实很多中断所做的并不是特别重要的工作,或者说,即使有些许延迟,也是无关紧要 的。主线内核中的默认中断行为,会严重影响RT任务的执行周期及抖动。

PREEMPT_RT中的中断行为

PREEMPT_RT 默认强制开启线程化中断(threaded interrupts) ;除带 IRQF_NO_THREAD 标记的中断外,所有中断处理函数都运行在内核线程上下文,不再跑在硬中断上下文。

如下图所示,不重要的硬中断、所有软中断和tasklet 都不在运行在interrupt context 中,而是作为普通的内核线程,交给调度器,根据调度算法来决定是否可被抢占 。换个说法就是,通过提高RT Task 的优先级,在PREEMPT_RT kernel中,RT Task 可以避免一些中断的干扰(传统内核中的中断,在PREEMPT_RT内核中已经变成了线程)。

不会被线程化的中断

IRQF_NO_THREAD 标志的中断仍以原始硬中断方式运行、关硬件中断;

  • IPI 跨处理器中断 主动使用该标志
  • IRQF_TIMERIRQF_PER_CPU 类型中断隐式自带 IRQF_NO_THREAD,不会线程化。

线程化中断的线程属性

开启后,中断交由内核线程执行,调度策略为 SCHED_FIFO,默认优先级 50。

c 复制代码
/*
 * Priority of a process goes from 0..MAX_PRIO-1, valid RT
 * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH
 * tasks are in the range MAX_RT_PRIO..MAX_PRIO-1. Priority
 * values are inverted: lower p->prio value means higher priority.
 */

#define MAX_RT_PRIO		100

void sched_set_fifo(struct task_struct *p)
{
	struct sched_param sp = { .sched_priority = MAX_RT_PRIO / 2 };
	WARN_ON_ONCE(sched_setscheduler_nocheck(p, SCHED_FIFO, &sp) != 0);
}

/*
 * Interrupt handler thread
 */
static int irq_thread(void *data)
{
...
	sched_set_fifo(current);
...
}

本作品采用 CC BY-NC-SA 4.0 协议