跟着思兼学习Klipper(25)提高 Klipper 进程优先级减少报错

前言

原创文章,转载引用请务必注明链接,水平有限,如有疏漏,欢迎指正交流。

文章如有更新请访问 DFRobot 社区 或者 cnblogs 博客园

欢迎对 Klipper 固件,以及对改版 CNC 加工的 Voron 三叉戟、v0、v2.4 感兴趣的朋友加群交流(QQ Group:490111638)


由于 Klipper 主要采用滚动更新机制(小而频繁的更新),最近的某次更新中,主观感受其出现 "timer too close" 错误的几率大大增加。

本文尝试通过提升 Klipper 上位机程序 klippy 的进程优先级来提高性能/运行稳定性,减少报错。

本文环境:Ubuntu 22.04 on RK3399,Klipper 全家桶最新版。

同时开辟 [先锋测试] 栏目,主要是我有一些想法,却没有时间精力去测试验证,不过我会提供解题思路,有兴趣的可以试试看。

另外,着急的直接看 2.1 即可。同时本文所附参考文献和链接都值得好好看。

本文涉及到的内容:

  1. klipper 报错 timer too close 的常见原因和可能的解决方法
  2. 使用 renice 工具调节 klippy 上位机软件的系统优先级
  3. 修改 klippy 的 CPU 核心亲和力,增加进程的 CPU 缓存命中率,从而提升其运行性能

1、[他山之石] 可能的原因

Sineos帖子中 总结了一些常见原因及可能的解决方法。

This error typically occurs when the host sends a message to the MCU, scheduling an event at a time that is in the past.

该错误一般发生在上位机向 MCU 发送消息时,计划触发事件的时间落后于当前。

1.1 Reasons | 原因

  • High system load of the host | 上位机系统负载过高
  • High disk activity of the host | 上位机磁盘读写过高
  • Swapping due to low free memory | 低可用内存导致的高磁盘交换空间使用
  • Disk errors / dying SD card | 磁盘错误或者 SD 卡不良
  • Unstable voltage | 电压不稳定
  • Other hardware hogging the USB bus or other system resources | 其他硬件占用 USB 总线或者其他系统资源
  • Running in a Virtual Machine | 在虚拟机中运行
  • USB, UART or CANBUS wiring faults leading to extremely delayed messages | 通讯接线错误导致的高传输延迟
  • ElectroMagnetic Interference (EMI) affecting proper signal transmission or leading to high resend rates | 电磁干扰信号传输或者导致高重传率

1.2 Potential solutions | 可能的解决方案

  • Check for other resource-intensive processes running in parallel | 检查是否同时有高系统资源占用的进程
  • Remove additional hardware, especially web cams | 断开其他硬件,特别是网络摄像头
  • Check SD card for errors / replace SD card | 检查 SD 卡是否故障/更换其他 SD 卡
  • Check for Under-voltage detected! errors / make sure to use a good and adequate power supply | 检查是否有低电压告警(适用树莓派),确保电源供电功率满足上位机需求。
  • Do not run data lines (USB, UART, CANBUS) close to and in parallel to high current lines like heaters or steppers. Use high quality cables (shielded / ferrite core). Keep such cables as short as possible | 避免通讯数据线(USB、UART、CANBUS)与高电流电源线(加热器、步进电机)太近或者并排走行。或使用高质量线缆(屏蔽或带磁环)。或尽量保证线缆越短越好。
  • Also see Advanced Trouble-Shooting / Graphing Klipper | 其他

2、我的想法与解决思路

一是使用 klipper 官方工具生成系统负载图进行分析多无明确阳性发现;二是由于 KlipperBox 硬件不变,既往都是很稳定的,此次仅仅升级 Klipper 便导致此错误,故考虑 Klipper 软件问题可能性大。例如运行在上位机的 Klippy 软件对系统资源、实时性要求变高;新版 MCU 固件存在问题等。其实有一点,也是人们常说的,如果当前的固件用着好好的,请不要轻易升级。除非有重大功能更新或者BUG修复。这里探讨两个方法,旨在提升 Klippy 进程优先级,降低由于系统资源被抢占导致出错的机率。

2.1 方法一:调整进程优先级

CPU 给每个进程分配时间片,时间到了就换下一个进程,当切换间隔较短时,用户感觉到就是多个程序同时运行的。

在操作系统的角度看,可用资源太少,而进程又是那么多,每个进程都需要使用可用资源,自然而然的需要给进程划分优先级,谁的优先级高,谁先享受资源的使用

Linux系统优先级有两个来控制:Priority 和 Nice,两者的关系简述如下:

  • 系统进程默认有一个优先级 Priority,PRI 值越小越快被执行
  • 优先级可以通过 Nice 来调节,最终的优先级为 PRI(new) = PRI(old) + nice,这样,当 nice 值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。
  • 可以通过 renice 工具来调节 nice 值。
  • 更多内容可参考:Linux进程中的优先级的理解(PRI 和 NI)_pri ni-CSDN博客

我们可以在 htop 工具中看到进程 Priority (优先级PRI) 和 Nice (NI)值,如:

由于 renice 工具需要知道进程的 PID 编号进程标识符(PID),所以我们可以先获取 klippy 进程的 PID(pidof 命令查询不到,父进程为 python),然后调用 renice 提升优先级,例如项目:klipper-priority-fix

# 下载安装
git clone https://github.com/Dids/klipper-priority-fix.git ~/klipper-priority-fix
~/klipper-priority-fix/scripts/install.sh

# 默认用户没有 renice 权限,使用 root 权限临时解决.同时执行两个替换规则
sudo sed -i 's/^User=/#&/; s/^Group=/#&/' /etc/systemd/system/klipper-priority-fix.service

sudo systemctl daemon-reload
sudo systemctl restart klipper-priority-fix

# 安装脚本会自动添加 update_manager 更新条目
# 安装脚本会自动添加到 ~/printer_data/moonraker.asvc 中用于管理服务
systemctl restart moonraker

此工具默认将 klippy 设置为 0 + (-20),也就是系统最高优先级。如果出现莫名其妙的问题,可以参考下文修改源码。具体可以使用 htop 命令来确认。

2.2 [先锋测试] 方法二:设置进程与 CPU 核心绑定以提高性能

2.2.1 什么是绑核

所谓绑核,其实就是设定某个进程/线程与某个CPU核的亲和力(affinity)。
设定以后,Linux调度器就会让这个进程/线程只在所绑定的核上面去运行。但并不是说该进程/线程就独占这个CPU的核,其他的进程/线程还是可以在这个核上面运行的。

如果想要实现某个进程/线程独占某个核,就要使用cpuset命令去实现。

其实,很多情况下,为了提高性能,Linux调度器会自动实现尽量让某个进程/线程在同样的CPU上去运行。所以,除非必须,我们没有必要显式的去进行进程绑核操作(虚拟机中有时候需要这样做)。

——source: linux中进程亲和性绑定 - 苦咖啡~~ - 博客园 (cnblogs.com)

最后一句话没空去找来源,但是是否和虚拟机运行 Klipper 容易出现各式问题有关?有待验证。同时我们也了解到一个新工具 cpuset,只不过目前用不到。下面是来自的 redhat.com 一个直观演示。

另外还有一个问题,实际上多数程序对多核心支持一般,主要在对单核性能要求高一些。

2.2.2 绑核提升进程运行性能?

在Linux系统中,进程的调度切换是由内核自动完成的,在多核CPU上,进程有可能在不同的CPU核上来回切换执行,这对CPU的缓存不是很有利。

——source: Linux如何将进程绑定CPU核心以提高性能 - 简书 (jianshu.com)

原文是以 x86 CPU 的 L1/L2 和 L3 缓存为例,对于 ARM 架构的 CPU 是否同样适用呢?我们以常见的 Amlogic S905 为例,其 CPU 框图如下:

并参考 S905 的数据手册(来自 Hardkernel) 可知:

  1. S905 有 4 个核心,每个核心有独立的 32KB 缓存
  2. 所有核心共用 512KB L2 缓存

所以猜测上述机制有效,即绑核有可能提升 klippy 进程运行性能。具体可以参考上述文章里的程序进行测试验证。

2.2.3 如何绑核

两种方法,一是合并到上述脚本,二是使用配置文件。

Method 1:合并到 klipper-priority-fix 脚本

由于 taskset 命令也需要知道进程 PID,所以可以考虑修改源码和上述 klipper-priority-fix 项目合并,毕竟获取进程 PID 这步原脚本已经实现了。不过需要测试后才建议合并,毕竟绑核可能比提高优先级更可能引发问题。这个理论上不难,我们剖析下此项目:

  • install.sh 调用 util.sh 调用 klipper-priority-fix.py,主要过程包括去掉 py后缀并复制到 /usr/local/bin/klipper-priority-fix,以及安装系统服务。

  • klipper-priority-fix.py 脚本中设置默认 nice 为 -20,Priority 为 0,此时进程优先级最高。

  • klipper-priority-fix.py 脚本中使用 sudo 运行 renice 需要输入密码时服务会报错无权运行。具体为 cmd = f"renice -n {KLIPPY_NICE_VALUE} -p {pid} || sudo renice -n {KLIPPY_NICE_VALUE} -p {pid} "

  • 合并后的脚本如下,修改 /usr/local/bin/klipper-priority-fix 第 19 行

    cmd = f"renice -n {KLIPPY_NICE_VALUE} -p {pid} || sudo renice -n {KLIPPY_NICE_VALUE} -p {pid} && taskset -cp 0 {pid}"
    
Method 2:使用配置文件

修改 systemd service 文件来修改进程的 CPU 亲和力,在 [Service] 下添加如下内容

sudo nano klipper.service

# [Service] 添加如下内容,0-3分别代表不同核心,如【0-1,3】,这里我们绑定到第一个核心。
CPUAffinity=0

# 重载服务并重启服务生效
sudo systemctl daemon-reload
sudo systemctl restart klipper

# 查看进程 pid 现在的绑核情况
pi@kbox  ~  taskset -p {pid}
pid 690's current affinity mask: 1

# 设置进程绑核
pi@kbox  ~  taskset -cp 0 {pid}
pid 690's current affinity list: 0-5
pid 690's new affinity list: 0

此外,我们还可以使用 limits.conf 来设置用户名下进程启动时默认的优先级。

参考文献

番外

我们还可以使用 ionice 工具来获取或设置程序的IO调度与优先级,理论上不是导致这次报错的主要原因。

先锋测试:

本期我们提出以下假设:

posted @ 2023-10-04 11:42  思兼  阅读(1179)  评论(0编辑  收藏  举报