linux-5.1/Documentation/cpu-load.txt
[...]
在大多数情况下,
/proc/stat
信息非常接近地反映了现实,但是由于内核如何/何时收集这些数据的性质,有时它根本不可信。[...]
如果我们想象系统有一个任务,它以下列方式周期性地燃烧周期:
time line between two timer interrupts |--------------------------------------| ^ ^ |_ something begins working | |_ something goes to sleep (only to be awaken quite soon)
在上述情况下,系统将根据 0% 加载
/proc/stat
(因为定时器中断总是在系统执行空闲处理程序时发生),但实际上负载更接近 99%。
该文件于 2007 年添加。
例如,如果有足够便宜和可靠的时间源(可靠的 TSC ) ,是否修改了 CPU 调度程序(例如 schedule() 函数)以测量每次进程从可运行转换为等待的时间?
该文档包括一个示例程序,smallhog.c
. 根据 LKML.org 上的链接线程,它能够占用 CPU,而内核只报告了几个 % 或更少的 CPU 使用率。
我尝试在我当前的系统上编译和运行它。内核报告程序的 CPU 使用率约为 80%。所以情况似乎发生了一些变化。我们是否确切地知道为什么smallhog.c
在这个系统上效率较低?
我使用 Fedora 30,Linux 内核 v5.2.0-rc5(大约),在“Intel(R) Core(TM) i5-5300U CPU”上以 64 位模式运行。
lscpu
显示constant_tsc
和nonstop_tsc
。journalctl -k | grep -iE "TSC|clocksource"
看起来内核发现 TSC 没有问题。cat /sys/devices/system/clocksource/clocksource0
显示“tsc”。
我看到链接的线程说
在所有架构上并非如此,有些架构通过记录用户/内核/中断转换的时间来进行更准确的记账......的确。这当然是常见的更无聊的 PC 架构的方式。
(也许hrtick的开发可能会对这个问题产生影响?即使只是为了使它更难利用。或者更容易?或者只是需要稍微不同的代码来利用?)。
您说该
smallhog
进程显示 80% 的 CPU 时间。该 CPU 上剩余的 20% 时间用于中断! 为什么 smallhog.c 在我的系统上显示不到 100% 的 CPU 使用率?smallhog
正在做一些非常中断密集的事情。它的具体战术显然被打败了IRQ_TIME_ACCOUNTING
。见下文。我怀疑仍然有办法躲避计时器滴答声:-)。您可能需要一种聪明的方法来预测滴答声何时触发。例如通过查看
/proc/interrupts
.这个特性在 Fedora 内核配置中启用(参见 参考资料
/boot/config-*
)。在 x86 CPU 上,它使用 TSC。可以使用启动时选项禁用该功能,tsc=noirqtime
.[*]更准确的会计方法
如问题中所述,PowerPC / S390 具有特定代码,可以在每次上下文切换时计算 CPU 时间。这被称为
VIRT_CPU_ACCOUNTING_NATIVE
。但是你的 x86 内核没有这个。有一个通用的等价物,称为
VIRT_CPU_ACCOUNTING_GEN
. (GEN 是“通用”的缩写)。这个特性是内置在你的 Fedora 内核中的。但默认情况下未激活此功能。你必须仔细阅读:-)。
VIRT_CPU_ACCOUNTING_GEN
仅在“完整的 dynticks 系统”上变得活跃。虽然 Fedora 内核配置包括NO_HZ_FULL
,但 Fedora 默认不启用“full dynticks”。启用“full dynticks”需要在启动时指定一个选项nohz_full=
,带有“adaptive-ticks CPUs”列表。(“至少一个非自适应时钟 CPU 必须保持在线......”)请参阅linux-5.2-rc5/init/Kconfig:
我在最后一段中标记了一行,因为它已经过时了。“完整的 dynticks 子系统”现已开发完成。
[*] TSC 考虑因素
如果 x86 CPU 没有 TSC,内核不会尝试将任何其他硬件时钟源用于
IRQ_TIME_ACCOUNTING
(或用于VIRT_CPU_ACCOUNTING_GEN
)。该代码建议接受任何可用的 TSC。我不知道这与没有
constant_tsc
:-) 的 CPU 配合得如何。虽然我有 99.9% 的把握,相关的维护者都知道这个问题,并且会问为什么它是可以接受的。请参阅native_sched_clock()和tsc_init():