实时Linux主要有两类方案:
- 单内核方案:对主线传统的Linux内核打入PREEMPT_RT补丁,使内核成为硬实时操作系统
- 双内核方案:主线传统Linux内核+实时内核的双内核方案,常见的主流方式有:RT-Linux,RTAI、Xenomai。
本文重点讲解单内核PREEMPT_RT补丁方案。
概念基础
实时性
实时分为硬实时和软实时,两者主要的区别主要就是就绪运行时间确定性。然后,主线Linux是软实时系统,加入实时补丁后将其改造为硬实时系统。
可抢占性
实现实时内核很重要的特点是可抢占性,就绪的高优先级的任务能够抢占低优先级任务。Linux内核在2.6版本之后引入了抢占模型,支持任务抢占。
主线Linux支持如下三种抢占模型:
(1) No Forced Preemption (Server):
传统的 Linux 抢占模型,面向吞吐量。系统调用返回和中断是唯一的抢占点。
(2) Voluntary Kernel Preemption (Desktop):
此选项通过向内核代码 [. . . ] 以稍微降低吞吐量为代价。除了显式抢占点外,系统调用返回和中断返回都是隐式抢占点
(3) Preemptible Kernel (Low-Latency Desktop):
此选项通过使所有内核代码(不在临界区中执行的)可抢占来减少内核的延迟。隐式抢占点位于每个抢占禁用部分之后。
对于软实时的嵌入式系统来说,内核抢占模式配置为Preemptible Kernel (Low-Latency Desktop)最佳。
进程调度策略
Linux内核支持实时进程和非实时进程调度(无PREEMPT_RT补丁支持也是支持实时进程调度,只是软实时,有补丁后就是硬实时)。
对于Linux进程任务来说,Linux 内核实现了多种调度策略。它们分为非实时和实时策略。调度策略已经在主线 Linux 中实现。
非实时策略:
- SCHED_OTHER: 每个任务都有一个所谓的“nice值”。它是一个介于 -20(最高 nice 值)和 19(最低 nice 值)之间的值。任务执行时间的平均值取决于相关的 nice 值。
- SCHED_BATCH: 此策略源自 SCHED_OTHER 并针对吞吐量进行了优化。
- SCHED_IDLE: 它也是从 SCHED_OTHER 派生的,但它的值比 19 弱。
实时策略:
- SCHED_FIFO: 任务的优先级介于 1(低)和 99(高)之间。在此策略下运行的任务将被调度,直到它完成或更高优先级的任务抢占它。
- SCHED_RR: 此策略源自 SCHED_FIFO。与 SCHED_FIFO 的区别在于任务在定义的时间片的持续时间内运行(如果它没有被更高优先级的任务抢占)。一旦时间片用完,它可以被具有相同优先级的任务中断。时间片定义在 procfs (/proc/sys/kernel/sched_rr_timeslice_ms) 中导出。
- SCHED_DEADLINE: 此策略实施全局最早deadline优先 (GEDF) 算法。在此策略下调度的任务可以抢占使用 SCHED_FIFO 或 SCHED_RR 调度的任何任务。
设置api:
sched_setscheduler()
PREEMPT_RT 实时补丁
打入PREEMPT_RT补丁,可实现硬实时内核。
PREEMPT_RT下载地址:http://cdn.kernel.org/pub/linux/kernel/projects/rt/
PREEMPT_RT补丁主要做了如下修改,修改背后的原理后续文章再深入描述:
- 高分辨率定时器
- 中断线程化
- 自旋锁spinlock_t改为互斥锁rt_mutex,要使用自旋锁则使用raw_spinlock_t
打入PREEMPT_RT后内核抢占模型配置则会多了如下两项:
- Preemptible Kernel (Basic RT): 这种抢占模型类似于“抢占内核(低延迟桌面)”模型。除了上面提到的属性外,线程中断处理程序是强制的(就像使用内核命令行参数时一样
threadirqs
)。该模型主要用于 PREEMPT_RT 补丁实现的替代机制的测试和调试。 - Fully Preemptible Kernel (Real-Time): 除了少数选定的关键部分之外,所有内核代码都是可抢占的。线程中断处理程序是强制的。此外,还实现了几种替代机制,如睡眠自旋锁和 rt_mutex,以减少抢占禁用部分。此外,大的抢占禁用部分被单独的锁定结构取代。必须选择这种抢占模型以获得硬实时行为。
PREEMPT_RT 实时补丁的限制
某些环境当前不能很好地与 Preempt-RT 配合使用。由于不兼容,CONFIG_PREEMPT_RT_FULL=y 禁用了几个功能。
禁用的配置选项,主要是虚拟化配置:
- CONFIG_TRANSPARENT_HUGEPAGE
- CONFIG_OPROFILE
- CONFIG_XEN (arm64)
- CONFIG_X86_POWERNOW_K8
- CONFIG_BCACHE
- CONFIG_HIGHMEM(mips,powerpc)
- CONFIG_KVM_MPIC (powerpc)
- CONFIG_RT_GROUP_SCHED
- CONFIG_CPUMASK_OFFSTACK
PREEMPT_RT 实时补丁修改步骤
1、下载PREEMPT_RT补丁
对应内核版本选择PREEMPT_RT补丁的版本,不对应版本选择则可能出现不可预估性的问题或者打补丁出现大量冲突。
下载地址:http://cdn.kernel.org/pub/linux/kernel/projects/rt/
2、打补丁以及配置内核
传统的打补丁方法:
git apply xxx.patch
# 或
git am xxx.patch
传统的配置内核方法:
# 1、输入配置内核命令
make menuconfig
# 2、选中全功能实时抢占配置,保存退出
(*)Fully Preemptible Kernel (Real-Time)
因为我用的是yocto来构建系统,如下是我的菜谱追加文件
FILESEXTRAPATHS_prepend := "${THISDIR}/preempt-rt-patch:" ## 存放补丁和配置的目录
PATCHTOOL