【Linux / Ubuntu22.04 | 安装实时内核补丁PREEMPT_RT】

前言:

通过uname -r命令查看当前版本内核,注意:选择自己目前内核最接近的 kernal补丁和RT补丁 并下载,根据其他文章描述在5.15.0-64-generic的基础上,分别安装了5.15.167-rt79和5.10.225-rt117,都是可以正常运行rt内核的,实时性都是OK的。

(Ps: 不过,Ubuntu 提供了 Ubuntu Pro(原 Ubuntu Advantage)实时内核(Real-time Kernel) 支持,从 Ubuntu 22.04 LTS 开始,Canonical 提供了 官方维护的 PREEMPT_RT 内核 作为 Ubuntu Pro 的一项服务(适用于 x86_64 和 ARM64 架构)。当然如果你是Ubuntu Pro 的实时内核需订阅(个人免费使用最多5台设备),可以通过下面命令进行安装,如果你不是那么接下来需要手动编译或从第三方源获取 PREEMPT_RT 补丁)

sudo apt install linux-realtime

1. 准备前工作

查看当前内核版本

uname -a

本文使用环境为Ubuntu 24.04.3 LTS, Linux ttw-MIC-3395 6.8.0-40-generic PREEMPT_DYNAMIC

image-20250805141241468

2.下载内核源码和RT补丁

正常顺序应该是,先查 RT 补丁,再下对应主线程内核源码

例如,你发现 patch-6.8.2-rt11.patch.xz 存在,那就说明 linux-6.8.2.tar.xz 这个主线内核版本有对应的 RT 补丁,可以组合使用

Ps:为什么不建议先下内核再找补丁?:

  • RT 补丁并非对所有主线版本都可用,很多主线版本根本没有对应补丁
  • 即使版本接近(如 6.8.2 和 6.8.3),也可能存在不兼容,导致补丁失败或内核不稳定
  1. RT补丁:https://www.kernel.org/pub/linux/kernel/projects/rt/
  2. linux内核源码:https://www.kernel.org/pub/linux/kernel

本部使用:

  • RT补丁:patch-6.8.2-rt11.patch.xz
  • 内核补丁:linux-6.8.2.tar.xz

3.安装依赖

sudo apt update #更新源
sudo apt-get install libncurses-dev libssl-dev build-essential openssl zlibc libelf-dev minizip libidn11-dev libidn11 bison flex zstd

问题一:无法定位软件包zlibc、libidn11

问题二:Mack和依赖问题

4.解压内核和把RT补丁打进去

sudo mkdir /usr/src/rt_kernal
# 可根据下载位置进行拷贝,当然不复制也没啥~
sudo cp /home/ttw/下载/linux-6.8.2.tar.xz /usr/src/rt_kernal/
sudo cp /home/ttw/下载/patch-6.8.2-rt11.patch.xz /usr/src/rt_kernal/
cd /usr/src/rt_kernal/
sudo su
xz -cd linux-6.8.2.tar.xz | tar xvf -
cd linux-6.8.2
xzcat ../patch-6.8.2-rt11.patch.xz | patch -p1

5.配置内核

  1. 复制系统当前内核的.config文件(无论当前是哪个内核版本都可以)
cp -v /boot/config-6.8.0-40-generic .config
  1. 进入图形化界面配置.config文件
make menuconfig # 使用 menuconfig 进行内核配置:
  1. 需要改动的地方如下:

General Setup -> Timers subsystem -> Timer tick handling 设置为 Full dynticks system
General Setup -> Timers subsystem 开启 High Resolution Timer Support
General Setup -> Preemption Model 设置为 Fully Preemptible Kernel (Real Time)RT
Processor type and features -> Timer frequency 设置为 1000 HZ
Device Drivers -> staging drivers 设置为 不开启 ——[ ] 默认开启,按N取消

Ps:理论上只有高亮的两条需要配置其他都是默认的,最后save 保存一下。

image-20250805190213948

image-20250805190256241

6.编辑Config文件

CONFIG_SYSTEM_TRUSTED_KEYS=“”
CONFIG_SYSTEM_REVOCATION_KEYS=“”
CONFIG_DEBUG_INFO=n
不然新内核带debug信息超大

image-20250807093225092

image-20250807093050910

image-20250807084549943

7.编译带RT补丁的内核

其中-j$(nproc)表示使用本机的全部线程数进行编译

Ps: 下面这一条指令是最慢的,CPU内核数越多速度越快,因为是全量编译,drivers/ 是最大最耗时的,其次是 fs/net/kernel/arch/sound/

make -j$(nproc)  #编译内核
sudo make INSTSALL_MOD_STRIP=1 modules_install #安装模块
sudo make install #安装内核

8.查看是否成功

cd /boot
ls | grep rt

查看/boot 目录下是否有生成的rt核心,应该包括以下4个文件:

config-6.8.2-rt11
initrd.img-6.8.2-rt11
System.map-6.8.2-rt11
vmlinuz-6.8.2-rt11

如果都有,则重启电脑,(在make install #安装内核完成后)系统将把 “Linux 6.8.2-rt11” 列进 GRUB 菜单第一条(默认启动项)。

reboot

重启后并确认当前内核版本,后缀为rt,就表明你已经完成!

uname -r # uname -a # 也行

image-20250808144500165

9.设置用户权限以使用RT实时调度(关键一步)

为了能够以用户权限调度线程(驱动程序将执行此操作),您需要通过更改/etc/security/limits.conf文件来修改用户的限制

建议为实时用户设置一个组,而不是将固定的用户名写入配置文件:

sudo groupadd realtime
sudo usermod -aG realtime $(whoami)

然后,确认当前文件夹下是否包含以下内容,没有则进行天添加:

sudo vi /etc/security/limits.conf
@realtime soft rtprio 99
@realtime soft priority 99
@realtime soft memlock 204800
@realtime hard rtprio 99
@realtime hard priority 99
@realtime hard memlock 204800
  • rtprio : 实时调度的优先级
  • priority : 最高调度优先级
  • memlock : 是内存锁定的大小,单位为KB,防止应用程序的内存被交换到磁盘(即锁定内存),从而保证高性能

重新登录或重启(必须!)输入以下指令,结果一致说明配置成功~

ulimit -r        # 应输出 99 返回用户的最大实时优先级
ulimit -l        # 应输出 204800(单位 KiB)

10.测试实时性

安装rt_test测试工具

sudo apt-get install rt-tests 

运行测试 5个线程,线程优先级99,以ns单位显示时间

sudo cyclictest -t 5 -p 99 -N -m

测试结果中各项含义如下

T: 线程
P: 线程优先级
C: 计数器。线程的时间间隔每达到一次,计数器加1
I: 时间间隔为1000微秒(us)
Min: 最小延时(us)
Act: 最近一次的延时(us)
Avg: 平均延时(us)
Max: 最大延时(us)

选项 中文含义
-a CPUSET --affinity=CPUSET 把测量线程“钉”在指定的一组 CPU 上,按轮询方式分配;如 -a 2 表示所有线程都跑在 CPU 2,-a 3-5,0 表示依次使用 3、4、5、0 号 CPU。
-A USEC --aligned=USEC 把线程的唤醒时间对齐到某个微秒偏移(USEC),减少抖动。
-b USEC --breaktrace=USEC 当延迟超过 USEC 微秒时,向 ftrace 发送“break trace”命令,方便抓异常。
-c CLOCK --clock=CLOCK 选择时钟源:0=CLOCK_MONOTONIC(缺省),1=CLOCK_REALTIME。
-d DIST --distance=DIST 线程之间的时间间隔偏移量,单位微秒,默认 500 μs。
-D TIME --duration=TIME 设定测试持续时长;可加后缀 m(分钟)、h(小时)、d(天)。
--latency=PM_QOS 打开 /dev/cpu_dma_latency 并把给定值写进去,以降低 CPU 睡眠延迟。
-F PATH --fifo=PATH 创建一个命名管道,把运行时统计信息实时写入该管道。
-h US --histogram=US 测试结束后在终端打印延迟直方图;US 为要跟踪的最大延迟(μs)。
-H US --histofall=US -h 类似,但额外增加一列“整体统计”。
--histfile=PATH 把直方图写到指定文件,而不是 stdout。
-i INTV --interval=INTV 每个线程的基准周期,单位微秒,默认 1000 μs。
--json=FILENAME 把最终结果以 JSON 格式写入指定文件。
-l LOOPS --loops=LOOPS 指定循环次数;默认为 0,表示无限循环。
--laptop 省电模式:降低 CPU 频率、减少功耗,实时性能会变差。
--mainaffinity=CPUSET 仅把主线程绑到指定 CPU,不影响测量线程。
-m --mlockall mlockall 锁住所有内存,防止页换入换出。
-M --refresh_on_max 只在出现新的最大延迟时才刷新屏幕,适合低带宽终端。
-N --nsecs 输出结果以纳秒为单位(默认是微秒)。
-o RED --oscope=RED “示波器模式”,减少冗余输出,每 RED 次采样输出一次。
-p PRIO --priority=PRIO 设置最高优先级线程的优先级值(与调度策略配合使用)。
--policy=NAME 设置测量线程的调度策略:other、normal、batch、idle、fifo、rr。
--priospread -p 指定的优先级开始,依次递减,给每个线程分配不同优先级。
-q --quiet 静默模式,只在退出时打印汇总。
-r --relative 使用相对定时器(相对时间),而非绝对定时器。
-R --resolution 检测并打印时钟分辨率(大量调用 clock_gettime)。
--secaligned [USEC] 把线程唤醒对齐到下一秒,并可加微秒偏移。
-s --system 用传统的 nanosleep/setitimer 系统调用,而非 POSIX 定时器。
-S --smp “标准 SMP 测试”快捷方式:自动启用 -a-t 且所有线程同优先级。
--spike=TRIGGER 记录所有超过 TRIGGER 微秒的延迟尖峰。
--spike-nodes=N 最多记录 N 个尖峰,默认 1024。
--smi 启用 SMI(系统管理中断)计数器(需内核支持)。
-t [NUM] --threads=NUM 启动线程数;不带 NUM 时使用所有在线 CPU 数量;缺省为 1。
--tracemark -b 触发时,向 ftrace 写一个标记事件。
-u --unbuffered 强制无缓冲输出,方便实时处理数据。
-v --verbose 实时打印详细统计:格式 n:c:v 分别表示线程号、计数、延迟值(μs)。
--dbg_cyclictest 打开调试信息,输出内部状态,方便排查问题。
-x --posix_timers 使用 POSIX 定时器(timer_create)而非 clock_nanosleep

11.启动设置

/etc/default/grub中配置开机自动选择

sudo vi /etc/default/grub
GRUB_TIMEOUT=10       %超时时间,单位s
GRUB_DEFAULT="1>2"    %1代表默认启动第2个内核,2代表所启动内核1中的第3个(序号从0开始)

然后更新grub

sudo update-grub

温馨 小提示~

/etc/security/limits.conf 只是把 “允许” 的门槛打开:让你指定的用户或组 有权 把线程/进程调度策略设成 SCHED_FIFOSCHED_RR,并把优先级调到 99。
它并不会自动把任何线程的调度策略改成实时。

因此:

  • 不调用 pthread_attr_setschedpolicy(..., SCHED_FIFO)(或 sched_setscheduler),线程仍然是默认的 SCHED_OTHER,优先级固定为 0。

  • 只有在你显式设置 SCHED_FIFO/SCHED_RR 并给出优先级 99 后,系统才会真正按实时线程来调度它。

给出C/C++ 设置代码

void set_thread_realtime(int priority) {
    struct sched_param sp = { .sched_priority = priority };
    pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
}

int main(int argc, char *argv[]) {
    set_thread_realtime(99); // 99是优先级
}

问题一:无法定位软件包zlibc、libidn11

  1. zlibc (下载第三个.deb的软件包):http://zlibc.linux.lu/download.html
  2. libidn11 (选择对应地区的随便一个就可以了,可能算是危险文件,允许下载即可。):https://packages.debian.org/bullseye/amd64/libidn11/download

image-20250805151212031

# 手动安装旧包
sudo dpkg -i zlibc_*.deb libidn11_*.deb

问题二:Mack和依赖问题

情况一:libncurses-dev,版本不对,导致依赖链断裂。

报错如图所示:

下列软件包有未满足的依赖关系: libncurses-dev : 依赖: libtinfo6 (= 6.3-2ubuntu0.1) 但是 6.4+20240113-1ubuntu2 正要被安装 依赖: libncurses6 (= 6.3-2ubuntu0.1) 但是 6.4+20240113-1ubuntu2 正要被安装 依赖: libncursesw6 (= 6.3-2ubuntu0.1) 但是 6.4+20240113-1ubuntu2 正要被安装 E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。

image-20250805163457775

Mack 时候报错

image-20250805164213788

解决:

先安装(切换root)

sudo su
apt install aptitude
 aptitude install libncurses-dev

点击n采取降级方案,之后输入 Y

image-20250805183609184

正常的话便可以安装了

apt-get install libncurses5-dev

情况二:缺少 flex(或 bison)

报错如图所示:

make[2]: *** [scripts/Makefile.host:9:scripts/kconfig/lexer.lex.c] 错误 127 make[1]: *** [/usr/src/rt_kernal/linux-6.8.2/Makefile:689:menuconfig] 错误 2 make: *** [Makefile:240:__sub-make] 错误 2

image-20250805164112025

说明在生成 lexer.lex.c 时,缺少 flex(或 bison 这类 词法/语法生成器。直接根据下面命令安装就可以

sudo apt install flex bison

情况三: 补装常见缺失依赖

报错命令如下所示:

compilation terminated. make[5]: *** [/usr/src/rt_kernal/linux-6.8.2/tools/build/Makefile.build:106:/usr/src/rt_kernal/linux-6.8.2/tools/objtool/arch/x86/decode.o] 错误 1 make[4]: *** [/usr/src/rt_kernal/linux-6.8.2/tools/build/Makefile.build:158:arch/x86] 错误 2 make[3]: *** [Makefile:66:/usr/src/rt_kernal/linux-6.8.2/tools/objtool/objtool-in.o] 错误 2 make[2]: *** [Makefile:73:objtool] 错误 2 make[1]: *** [/usr/src/rt_kernal/linux-6.8.2/Makefile:1360:tools/objtool] 错误 2 make: *** [Makefile:240:__sub-make] 错误 2

建议先补充依赖

# 1. 补装常见缺失依赖
sudo apt update
sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev bc

理论上处理完这一步应该就可以编译了

# 2. 如果还是不行就确认一下 gcc 版本
gcc --version   # 若 >13,建议切回 11/12
sudo apt install gcc-12 g++-12
export CC=gcc-12 CXX=g++-12
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐