
我們最近觀察到我們的嵌入式系統上的平均負載約為 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。 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
.
如前所述,這是實驗性的,因為這些數字讓我覺得太巧了。我沒有調查過是否會出現任何副作用。