Linux进程管理:(九)实时调度

文章说明:

实时指的是一个特定任务的执行时间必须是确定的并且可预测的,在任何情况下都能保证任务的最大执行时间限制。通常实时分成软实时和硬实时两种。

  • 软实时:仅仅要求事件响应是实时的,并不要求任务必须在多长时间内完成,大多数情况下要求的是统计意义上实时,不需要100%达到实时,如视频播放器,偶尔不能在限定时间完成任务也是能接受的。
  • 硬实时:对任务的执行时间的要求非常严格,无论在什么情况下,任务的执行时间必须得到绝对保证,否则将产生灾难性后果,如飞行控制器等。

1. 实时延时分析

假设Linux操作系统已经打开了内核抢占功能,即配置了CONFIG_PREEMPT宏。当外设中断发生后,我们需要唤醒进程A来响应这个事件,从中断发生到进程A开始执行之间的这段延时,我们称为实时延时(T0-T4),如下图所示:

在这里插入图片描述

  • 在T0时刻,进程A处于睡眠等待状态(TASK_INTERRUPTIBLE),外设中断发生,从中断发生到Linux内核响应之间有一个延时,称为中断延时(T0-T1)。产生中断延时的原因:

    • 发生中断时,Linux内核正处于关中断的状态,如正在spinlock_irq()spin_lock_irqsave()的临界区里执行,因此本地处理器将无法立刻响应这个中断,如下图所示:

      在这里插入图片描述

    • 中断控制器的调度延时。现代的中断控制器支持中断优先级调度和抢占,因此,它可能会被高优先级中断抢占。

  • 在T1时刻,CPU响应了这个中断,Linux内核的中断处理分成了上半部和下半部。上半部处理会很快完成,而且没有其他中断打扰,但是下半部是在开中断情况下执行的,可能被其他中断打断。如下图所示,在处理完中断A的上半部之后,其他外设中有中断发生,CPU转向处理其他中断了,这样延时处理中断A的下半部。我们把开始响应中断到这个中断完全处理的这段时间称为中断处理延时(T1-T2)。

    在这里插入图片描述

  • 在T2时刻,中断处理完成,唤醒进程A。从唤醒进程到进程被调度器选中的这段延时称为调度延时(T2-T3)。产生调度延时的主要原因如下:

    • 中断发生时Linux内核正在自旋锁临界区里执行。这样,中断完成之后不能马上抢占调度,必须等到Linux内核执行完成自旋锁临界区才能抢占调度,如下图所示。自旋锁临界区是关闭内核抢占的,当退出自旋锁临界区时会打开内核抢占并且尝试抢占当前进程。

      在这里插入图片描述

    • 调度器选中进程A的时间也是不确定的,可能就绪队列中有比进程A优先级更高的进程。

  • 在T3时刻,调度器选中了进程A,还需要进程上下文切换后才能执行进程A,即上下文切换延时(T3-T4)。

  • 在T4时刻,进程A终于等到了执行的机会。

2. Linux内核实时性改进

早在2001年时,Robert Love 就给 Linux 内核打上了抢占补丁,所以Linux内核支持可抢占已经有十几年的时间了。如果Linux内核不支持抢占,那么进程只能在如下条件下进行调度

  • 进程主动要求调度,如调用schedule()或者cond_resched()等
  • 在系统调用、异常处理和中断处理完成返回用户空间前夕进行调度

上述条件都导致了早期Linux内核的调度延迟非常大。而在支持可抢占的内核中

  • 如果唤醒动作发生在系统调用或者异常处理上下文中,在下一次调用preempt_enable()时会检查是否需要抢占调度。preempt_enable()函数会主动调用__preempt_schedule()来判断是否需要抢占当前进程。
  • 中断处理返回前会检查是否要抢占当前进程。注意,这里是中断处理返回,而不是用户空间返回,二者之间是有很大区别的。

抢占的相关源码如下所示:

struct thread_info {
	...
	union {
		// 当为 0 时,表示内核可以被安全抢占
		// 当大于 0 时,则禁止抢占
		u64		preempt_count;
		...
	};
};

// 用于关抢占,给 thread_info 数据结构中的 preempt_count 成员加 1
#define preempt_disable() \
do { \
	preempt_count_inc(); \
	barrier(); \
} while (0)

// 用于打开抢占,给 thread_info 数据结构中的 preempt_count 成员减 1,然后程序会判断其是否为 0,
// 如果为 0,则调用 __preempt_schedule() 函数完成调度抢占
#define preempt_enable() \
do { \
	barrier(); \
	if (unlikely(preempt_count_dec_and_test())) \
		// 判断是否需要抢占当前进程
		__preempt_schedule(); \
} while (0)

内核仅仅支持可抢占调度,要达到硬实时系统(hard real-time systerm)的要求还远远不够,为此社区中有一群人致力于Linux内核的实时性优化和改进。最近几年有很多优化的补丁己经添加到了官方Linux内核中,如下表所示:

在这里插入图片描述

  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值