我们最近观察到我们的嵌入式系统的平均负载高达 1.5 左右,尽管几乎所有进程都应该处于睡眠状态(根据htop
)。
该系统是一个双核 Cortex-A9,运行使用 buildroot 构建的实时 Linux 内核 (4.14.126)。我们使用 initramfs 作为根文件系统,并且没有交换,所以肯定有无磁盘 I/O在正常操作期间。
经过一番挖掘,我们发现负载是由一个名为的程序引起的更新,它为我们提供了一个方便的软件更新网络界面(我们非常希望继续使用它)。
当我用来time
估计该应用程序的平均 cpu 使用率时(通过计算(用户+系统)/真实),我得到的值只有 1% 左右,考虑到 1.5 的平均负载,这没有多大意义。
我知道平均负载还包括处于该TASK_UNINTERRUPTIBLE
状态的进程,这些进程不会影响 CPU 使用率。我不明白的是为什么该应用程序的任何线程/进程都会处于该状态。
为了进一步分析情况,我使用捕获了内核跟踪LTNG,这表明 swupdate 所做的唯一事情就是(每 50 毫秒):
正如你所看到的,有一些看起来是基于套接字的 IPC,并且有一个 select 正在等待某物。在 IPC 情况下,一个线程似乎主要阻塞 in nanosleep()
,而另一个线程阻塞 in accept()
,据我所知,这两个线程都不应该消耗任何系统资源。
仅供参考:两个屏幕截图的时基相同,IPC 大约需要 1 分钟。总共 500-600μs(考虑到 50ms 的间隔,与观察到的 1% CPU 使用率非常吻合)
那么,是什么造成了这里的负载呢?
答案1
由于状态 R 和 D 中的任务都会增加 Linux 负载,因此您可以对处于这两种状态之一的系统中的所有线程进行采样。例如:
for x in {1..100} ; do ps -aeos,user,comm,wchan | grep "^[RD]" ; sleep 0.1 ; done | sort | uniq -c | sort -nbr | head -20
下面的示例输出,您需要忽略显示您自己的ps
进程始终处于活动状态的第一行 - 因为它是执行所有采样的进程:
# for x in {1..100} ; do
> ps -aeos,user,comm,wchan | grep "^[RD]"
> sleep 0.1
> done | sort | uniq -c | sort -nbr | head -20
100 R root ps -
3 R oracle oracle_14047_li -
2 R root rcu_sched rcu_gp_kthread
2 R root rcu_sched -
2 R root kworker/1:2-eve -
2 R oracle perl -
2 R oracle ora_vktm_lin19c hrtimer_nanosleep
2 D root md10_raid10 md_super_wait
2 D oracle ora_ckpt_linprd md_write_start
1 R redis redis-server -
1 R oracle ora_vktm_linprd hrtimer_nanosleep
1 R oracle ora_m001_linprd -
1 D root xfsaild/dm-18 rq_qos_wait
1 D oracle ora_mz00_lin19c x64_sys_io_destroy
1 D oracle ora_lg00_lin19c inode_dio_wait
1 D oracle ora_dbrm_lin19c msleep
除非您在旧内核上运行,否则您应该以 root 身份运行它,因为新内核会屏蔽其他用户进程的 WCHAN 值。
您可以比这更深入(但不能使用 ps),您也可以采样/proc/PID/syscall
并/proc/PID/stack
获取系统调用和内核堆栈跟踪信息。我为此编写了一个名为 Linux Process Snapper ( psn
) 的工具,因此您可以对此类性能问题进行相当高级的深入研究,而无需诉诸内核跟踪:
[tanel@linux01 ~]$ sudo psn -G syscall,wchan
Linux Process Snapper v0.18 by Tanel Poder [https://0x.tools]
Sampling /proc/syscall, stat, wchan for 5 seconds... finished.
=== Active Threads ==========================================================================================
samples | avg_threads | comm | state | syscall | wchan
-------------------------------------------------------------------------------------------------------------
511 | 255.50 | (kworker/*:*) | Disk (Uninterruptible) | [kernel_thread] | blkdev_issue_flush
506 | 253.00 | (oracle_*_l) | Disk (Uninterruptible) | pread64 | do_blockdev_direct_IO
28 | 14.00 | (oracle_*_l) | Running (ON CPU) | [running] | 0
1 | 0.50 | (collectl) | Running (ON CPU) | [running] | 0
1 | 0.50 | (mysqld) | Running (ON CPU) | [running] | 0
1 | 0.50 | (ora_lgwr_lin*c) | Disk (Uninterruptible) | io_submit | inode_dio_wait
1 | 0.50 | (oracle_*_l) | Disk (Uninterruptible) | pread64 | 0
1 | 0.50 | (oracle_*_l) | Running (ON CPU) | [running] | SYSC_semtimedop
1 | 0.50 | (oracle_*_l) | Running (ON CPU) | [running] | read_events
1 | 0.50 | (oracle_*_l) | Running (ON CPU) | read | 0
1 | 0.50 | (oracle_*_l) | Running (ON CPU) | semtimedop | SYSC_semtimedop
您可以比这更深入,相关的博客条目在这里:
答案2
CPU 使用率和负载是不同的指标。事实上,负载可能高于 1。CPU 是 CPU 使用的实时时间,因此它应该始终小于 100%(但在多个核心/CPU 上,但您明白了)。 Load表示负载:有多少进程正在运行和等待运行。
正如您可能知道的那样(从问题的讨论中),I/O 通常是此类等待之一,因此它会增加负载。但是您也可以拥有可能导致等待的信号/信号量/锁,而这些可能只是由一个不执行 I/O 的进程引起的。例如,如果一个进程每秒唤醒一次,并且有许多进程正在等待来自该进程的数据,则会获得更高的负载(等于等待的进程数)。
你可能将管道视为 I/O,但 mmap 和锁...你是否将其归类为 IO?它们不会出现在bio(块I/O)中,因此您可能在许多负载统计信息中看不到它们。
通常找出这一点的更简单方法是:阻止进程并检查它在哪里。多次执行此操作,您应该会看到一个函数被阻塞(并且您可能会发现它比其他函数更频繁)。
答案3
我在大约 450 MHz 的 i.mx28 上运行的嵌入式系统上遇到了同样的问题。
htop
持续显示 50% CPU 使用率,这完全是由其中一项swupdate
任务引起的。
当浏览mongoose_interface.c
您的 100 毫秒观察时,在阅读以下内容时触发start_mongoose()
:
mg_mgr_poll(&mgr, 100);
我实验性地将 100 更改为 1000,重新编译并重新启动后,CPU 使用率下降到swupdate
线程的 2% 左右,如htop
.
如前所述,这是实验性的,因为这些数字让我觉得太巧合了。我没有调查过是否会出现任何副作用。