0%

给Linux内核增加实时补丁并安装

使用UltraISO制作启动盘

  • 链接
  • 用管理员权限启动UltraISO

为何要安装实时补丁

  • Linux系统从原理上说并不是一个实时系统,因为Linux系统有很多状态是不可被抢占的,比如持有自旋锁的状态等等,在其他博客中有所涉及,这会导致Linux系统定时不准等一系列问题,对实时控制十分不利

  • 增加实时补丁不能完全使得Linux系统变为实时系统,但是可以使得Linux系统不可抢占的部分大为减少,增强实时性

    对Linux内核实时性影响比较大的情况

  • 实际上是Linux内核的进程切换频率影响比较大,决定了定时的最佳分辨率

  • Linux内核编译的时候最多只支持1000Hz,也就是时间分辨率为1ms,但是假如要求更高的话就必须自己在kernel/Kconfig.hz中添加设置如下图

    • picture 5
    • picture 7
  • 如图编译的时候就可以增加一个自定义的2000Hz选项

  • picture 10

  • picture 8

  • 然后再选择实时内核

  • picture 9

  • 注意,对定时准确性影响比较大的是内核的timer频率,甚至实时内核与否都只起到次要作用

(更新)安装参考

  • 安装依赖sudo apt-get install build-essential libncurses-dev bison flex libssl-dev libelf-dev

  • 如果下载的是形如patches-6.1.83-rt28.tar.xz之类的

  • 先将其解压缩,然后在内核源码目录下执行

    for patch in <解压缩目录>/*.patch; do
    patch -p1 < "$patch"
    done
  • 简单流程

    • 将内核源码解压到/usr/src/<你的内核名称>,目录下
      • sudo tar -xvf <>.tar.xz -C <path to kernel dir>
    • sudo su切换到root用户
    • (此处跳过打实时patch的步骤)
    • 将旧的config复制到当前源码目录下的.config文件
      • cp /boot/config-$(uname -r) .config
    • 按照上面说的修改.config
    • 使用make menuconfig修改设置
      • picture 16
      • picture 17
      • picture 18
  • 此外还要在make menuconfig中配置抢占为General setup -> Preemption Model:选择 Fully Preemptible Kernel (RT)

  • Processor type and features -> Timer frequency:设置为 1000 Hz 以获得更高的时钟分辨率

  • General setup -> Timers subsystem:启用 High Resolution Timer Support

  • 然后删除内核模块的一些校验如图picture 2

  • 注意最后要注释设置CONFIG_MODULE_SIG_ALL, CONFIG_MODULE_SIG_KEY, CONFIG_SYSTEM_TRUSTED_KEYS,CONFIG_SYSTEM_REVOCATION_LIST, CONFIG_SYSTEM_REVOCATION_KEYS, CONFIG_DEBUG_INFO=y

    • 注意,上述每个都必须注释掉,如果注释了一部分但是漏掉其余部分会导致编译内核的时候出错
  • 修改.configCONFIG_LOCALVERSION选项为自己需要的内核后缀名(记得前面加-

  • make -j8 && make modules_install -j8

  • make install

修改默认启动内核

  • 先使用sudo update-grub观察

    • picture 14
  • 一般情况下编译安装内核之后,系统默认的启动内核还是原来的

  • 此时需要修改/etc/default/grub如图

    • picture 13
    • 也就是修改为GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux <你的Linux内核版本名称>"
  • 然后执行sudo update-grub

  • 然后重启即可自动进入指定的内核

    • picture 15
  • 交大内核镜像(下载快)

    内核模块测试

  • 重点是要在一开始解压内核源码的时候,就把内核源码解压到/usr/src目录下,这样的话就可以编译自定义内核模块了

  • picture 4

  • 按照上述逻辑完美解决

  • picture 3


问题

启动Ubuntu的时候bad shim signature

  • 因为BIOS里开启了安全启动,进入BIOS关闭secure boot即可

    遇到类似于memtest86+ needs a 16bit等等问题

  • /etc/default/grub文件中添加一行GRUB_DISABLE_OS_PROBER=false,但是未能解决问题
  • 但是只要在开机的时候按下Esc到Ubuntu高级设置,找到需要的内核启动即可

    更换内核之后遇到载入Ubuntu内存盘,error: out of memory

  • 更换新内核之后遇到载入Ubuntu内存盘,error: out of memory无法开机,但是用旧的内核可以启动
  • sudo update-initramfs -u
  • 之后又报错can't find command hwmatch out of memory
  • 重新编译内核,暂时未解决
  • 怀疑是系统分区太小导致的,重新格式化并且分配系统分区解决问题

更换内核之后因为Linux下头文件与内核版本不符导致不能安装本机编译的模块

以下方法未解决问题

  • insmod报错为insmod: ERROR: could not insert module ***.ko: Invalid module format

  • dmesg查看到报错为disagrees about version of symbol module_layout

  • 可能的解决方法

    • (不要执行这一步)编译内核的时候删去模块版本检查module versioning support,在Enable loadable module support

    • 然后重新安装内核,重新编译模块安装,安装失败使用sudo dmesg查看内核log,得到version magic '6.2.0-rt3 SMP preempt mod_unload modversions ' should be '6.2.0-rt3 SMP preempt_rt mod_unload '

    • 重新安装内核之后修改内核/usr/src下的内核源码

    • /usr/src/linux-headers-系统内核名称/include/generated下的utsrelease.h中的选项

      #define UTS_RELEASE "6.2.0-rt3"
      #define UTS_UBUNTU_RELEASE_ABI 35
    • 第一个修改为需要的内核名称即可

    • 然后修改/usr/src/linux-headers-<内核名称>/include/config/kernel.release文件,内容修改为

      <需要的内核名称> // 比如 6.2.0-rt3
    • 然后修改真正产生version magic字符串的文件/usr/src/linux-headers-<内核名称>/include/linux/vermagic.h

      /* SPDX-License-Identifier: GPL-2.0 */
      #ifndef _LINUX_VERMAGIC_H
      #define _LINUX_VERMAGIC_H

      #ifndef INCLUDE_VERMAGIC
      #error "This header can be included from kernel/module.c or *.mod.c only"
      #endif

      #include <generated/utsrelease.h>
      #include <asm/vermagic.h>

      /* Simply sanity version stamp for modules. */
      #ifdef CONFIG_SMP
      #define MODULE_VERMAGIC_SMP "SMP "
      #else
      #define MODULE_VERMAGIC_SMP ""
      #endif
      /*
      #ifdef CONFIG_PREEMPT_BUILD
      #define MODULE_VERMAGIC_PREEMPT "preempt "
      #elif defined(CONFIG_PREEMPT_RT)
      #define MODULE_VERMAGIC_PREEMPT "preempt_rt "
      #else
      #define MODULE_VERMAGIC_PREEMPT ""
      #endif
      */
      // 修改此处
      #define MODULE_VERMAGIC_PREEMPT "preempt_rt "
      #ifdef CONFIG_MODULE_UNLOAD
      #define MODULE_VERMAGIC_MODULE_UNLOAD "mod_unload "
      #else
      #define MODULE_VERMAGIC_MODULE_UNLOAD ""
      #endif
      #ifdef CONFIG_MODVERSIONS
      #define MODULE_VERMAGIC_MODVERSIONS "modversions "
      #else
      #define MODULE_VERMAGIC_MODVERSIONS ""
      #endif
      #ifdef RANDSTRUCT
      #include <generated/randstruct_hash.h>
      #define MODULE_RANDSTRUCT "RANDSTRUCT_" RANDSTRUCT_HASHED_SEED
      #else
      #define MODULE_RANDSTRUCT
      #endif
      // 修改下面的部分拼出自己想要的version magic
      #define VERMAGIC_STRING \
      UTS_RELEASE " " \
      MODULE_VERMAGIC_SMP \
      MODULE_VERMAGIC_PREEMPT \
      MODULE_VERMAGIC_MODULE_UNLOAD \
      // MODULE_VERMAGIC_MODVERSIONS \
      MODULE_ARCH_VERMAGIC \
      MODULE_RANDSTRUCT

      #endif /* _LINUX_VERMAGIC_H */
    • 找到VERMAGIC_STRING中的对应项,自己修改为需要的值或者顺序,注释掉不需要的即可

    • 然后得到与前面要求的相同的version magic字符串,即可insmod

      找不到符号Unknown symbol __mutex_init (err -2)

  • insmod报错insmod: ERROR: could not insert module ***.ko: Unknown symbol in module

  • 未能解决

    最终解决方案

  • 在编译内核模块的时候,将内核模块目录下的Makefile文件中的KDIR修改为自己编译当前内核源码的目录,不要用/usr/src下的源码,然后照常make即可insmod

  • 不要make menuconfig中关闭module versioning support选项,否则会因为version magic字符串不一致导致很多预编译好的其他模组不能安装