CPU使用率が1%のプロセスで、負荷平均は1.5

CPU使用率が1%のプロセスで、負荷平均は1.5

最近、ほぼすべてのプロセスがスリープ状態であるにもかかわらず ( によると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 ミリ秒ごとに)。 ここに画像の説明を入力してください

そしてこれ(100ミリ秒ごと): ここに画像の説明を入力してください

ご覧のとおり、ソケットベースの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

前述のとおり、数字があまりにも偶然すぎるように思えたので、これは実験的なものでした。副作用が発生するかどうかは調査していません。

関連情報