
最近、ほぼすべてのプロセスがスリープ状態であるにもかかわらず ( によるとhtop
)、組み込みシステムで約 1.5 という高い負荷平均が観測されました。
問題のシステムは、buildrootを使用して構築されたリアルタイムLinuxカーネル(4.14.126)を実行するデュアルコアCortex-A9です。ルートファイルシステムにはinitramfsを使用しており、スワップはないので、ディスクI/Oなし通常操作中。
少し調べてみると、負荷の原因は、swupdateは、ソフトウェア更新のための便利な Web インターフェイスを提供します (今後も引き続き使用していきたいと考えています)。
time
そのアプリケーションの平均CPU使用率を推定するために(計算によって)(ユーザー+システム)/実数) の場合、値はわずか 1% 程度しか得られませんが、負荷平均が 1.5 であることを考えると、あまり意味がありません。
負荷平均にはTASK_UNINTERRUPTIBLE
、CPU 使用率に影響しない状態のプロセスも含まれることはわかっています。理解できないのは、そのアプリケーションのスレッド/プロセスのいずれかがなぜその状態になるのかということです。
状況をさらに分析するために、カーネルトレースをキャプチャしました。lttngこれは、swupdate が実行するのは次のことだけであることを示しています (50 ミリ秒ごとに)。
ご覧のとおり、ソケットベースのIPCと思われるものが少しあり、選択を待っています。何かIPC の場合、1 つのスレッドは主に でブロックしているように見えますnanosleep()
が、もう 1 つのスレッドは でブロックしていますaccept()
。私の知る限り、どちらもシステム リソースを消費しないはずです。
参考までに: 両方のスクリーンショットの時間ベースは同じで、IPC には合計で約 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
古いカーネルで実行していない限り、新しいカーネルは他のユーザーのプロセスの WCHAN 値をマスクするため、これを root として実行する必要があります。
それより深く調べることもできます (ただし ps は不可)。サンプリングして/proc/PID/syscall
、/proc/PID/stack
システム コールとカーネル スタック トレースの情報も取得できます。psn
このために Linux Process Snapper ( ) というツールを作成しました。これを使用すると、カーネル トレースに頼ることなく、このようなパフォーマンスの問題をかなり詳細に調べることができます。
[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 の場合ですが、その意味はわかります)。負荷は負荷を示します。つまり、実行中のプロセスと実行を待機しているプロセスの数です。
おそらくご存知のとおり (質問内の議論から)、I/O は通常そのような待機の 1 つであるため、負荷が増加します。ただし、待機を引き起こす可能性のあるシグナル/セマフォ/ロックもあり、これらは I/O を実行していない 1 つのプロセスによってのみ発生する可能性があります。たとえば、1 秒ごとに 1 つのプロセスが起動し、そのようなプロセスからのデータを待機しているプロセスが多数ある場合、負荷が高くなります (待機しているプロセスの数に等しい)。
パイプは I/O として表示されますが、mmap やロックは IO として分類されますか? これらは bio (ブロック I/O) には表示されないため、多くの負荷統計では表示されない可能性があります。
これを見つける最も簡単な方法は、プロセスをブロックして、それがどこにあるかを確認することです。これを何度も実行すると、1 つの関数がブロックしていることが分かるはずです (他の関数よりも頻繁に見つかるかもしれません)。
答え3
約 450 MHz で i.mx28 を実行している組み込みシステムでも同じ問題が発生していました。
htop
タスクの 1 つが原因で、CPU 使用率が常に 50% を示していましたswupdate
。
100 ミリ秒の観察を閲覧すると、mongoose_interface.c
最後にこれを読み取るときにトリガーされますstart_mongoose()
。
mg_mgr_poll(&mgr, 100);
実験的に 100 を 1000 に変更し、再コンパイルして再起動すると、swupdate
で確認されているように、CPU 使用率はスレッドの約 2% に低下しましたhtop
。
前述のとおり、数字があまりにも偶然すぎるように思えたので、これは実験的なものでした。副作用が発生するかどうかは調査していません。