我有一台运行 Debian 8 (jessie) 的机器,降级到内核 3.2-rt。这是运行一个设置为实时优先级 (SCHED_RR) 的应用程序,每 1ms 唤醒一次以进行一些处理,并且每 100ms 向另一台 PC 发送一条串行消息。这非常有效。 (除了默认服务之外,该机器实际上并不运行任何其他东西;特别是它不是台式机。)
当尝试将其升级到内核为 4.9-rt 的 Debian 9(stretch)时,我遇到了相当多的问题;最大的问题之一是时钟稳定性。虽然最初似乎运行正常,但启动后不久我得到了这个:
clocksource: timekeeping watchdog on CPU0: Marking clocksource 'tsc' as unstable because the skew is too large:
clocksource: 'acpi_pm' wd_now: 74c6dd wd_last: 8dd916 mask: ffffff
clocksource: 'tsc' cs_now: 119ac91c7b1 cs_last: 1158a25441d mask: ffffffffffffffff
clocksource: Switched to clocksource acpi_pm
第一次发生这种情况时,上述消息报告切换到“hpet”;发生此切换后,机器上的所有计时似乎都不稳定 - 代码用于clock_gettime(CLOCK_MONOTONIC, ...)
计时,并且报告它现在每 4 毫秒而不是每 1 毫秒醒来,串行消息实际上是每 200 毫秒而不是每100 毫秒,但串行消息内的时间戳表明它认为每 66 毫秒发送一次。 (其中一些可能正在计算唤醒滴答声而不是实际的毫秒数。)
我尝试在 BIOS 中禁用 HPET,这导致了上面显示的错误,并且它在启动时再次运行正常,直到记录上述内容,此时虽然它看起来大部分运行正常,但有时一些计时器报告负持续时间。
另一个主要问题是,每 1 毫秒运行一次的处理代码在 Jessie 中运行大约需要 200us,但在 Stretch 中运行需要 900us,没有明显的原因。我尝试运行perf record
它,它报告大部分时间(40% 的样本)都花在sys_clock_gettime
.
两种情况下的硬件相同:Supermicro X11SSQ 主板,配备 AMD64 架构的 i7-7700K CPU。 CPU 节流在 BIOS 中被禁用,但它会报告constant_tsc
和nonstop_tsc
。
可能感兴趣的是应用程序正在执行繁忙等待循环(if clock_gettime() < wakeup then sched_yield()
简化的伪代码)来调度处理代码。
作为一个实验,我将其更改为睡眠(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wakeup)
),至少到目前为止,时钟源错误没有再次发生,并且它仍然使用 tsc 时钟,它似乎工作正常(与其他时钟不同)。
然而,唤醒抖动现在恶化了 10 倍:睡眠时为 700-1200us (SD 36us),而忙等待时为 890-1120us (SD 2us)。
作为进一步的实验,我尝试将其更改为 SCHED_DEADLINE (使用单个sched_yield()
),但这与clock_nanosleep 具有相同的行为。
知道为什么这种行为在 Jessie 和 Stretch 之间(或者更有可能在 3.2 和 4.9 之间)发生变化吗?有没有更好的方法来实现低抖动等待而又不会明显破坏系统时钟?
答案1
在/etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT 行添加“notsc Clocksource=acpi_pm”
我现在的情况是:
GRUB_CMDLINE_LINUX_DEFAULT="quiet notsc clocksource=acpi_pm"
然后做:sudo update-grub
您可以设置其他可用的时钟源:
cat /sys/devices/system/clocksource/clocksource0/available_clocksource