
Недавно мы наблюдали высокую среднюю нагрузку около 1,5 на нашей встроенной системе, хотя практически все процессы предположительно находятся в спящем режиме (согласно htop
).
Система, о которой идет речь, представляет собой двухъядерный Cortex-A9, работающий под управлением ядра Linux в реальном времени (4.14.126), собранного с помощью buildroot. Мы используем initramfs для нашей корневой файловой системы, и у нас нет подкачки, поэтому определенно естьнет дискового ввода-выводапри нормальной работе.
После небольшого исследования мы выяснили, что нагрузка вызвана программой под названиемswupdate, который предоставляет нам удобный веб-интерфейс для обновления программного обеспечения (и мы бы очень хотели продолжать его использовать).
Когда я использую time
для оценки среднего использования процессора этим приложением (путем вычисления(пользователь+система)/реальный), я получаю значение всего около 1%, что не имеет особого смысла, учитывая среднюю нагрузку 1,5.
Я знаю, что средняя загрузка также включает процессы в TASK_UNINTERRUPTIBLE
состоянии, которые не способствуют использованию процессора. Я не понимаю, почему какой-либо из потоков/процессов этого приложения когда-либо будет в этом состоянии.
Для дальнейшего анализа ситуации я записал трассировку ядра с помощьюlttng, что показывает, что единственное, что делает swupdate, это (каждые 50 мс):
Как вы можете видеть, есть немного того, что похоже на IPC на основе сокетов, и выбор ожиданиячто-нибудь. В случае IPC один поток, по-видимому, в основном блокирует nanosleep()
, а другой блокирует accept()
, и ни один из них не должен потреблять системные ресурсы, насколько мне известно.
К вашему сведению: временная база для обоих снимков экрана одинакова, а IPC занимает в общей сложности около 500-600 мкс (что, учитывая интервал в 50 мс, вполне соответствует наблюдаемому 1% использованию ЦП).
Так что же здесь является причиной нагрузки?
решение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
Использование ЦП и загрузка — разные показатели. Фактически загрузка может быть выше 1. ЦП — это реальное время, используемое ЦП, поэтому оно всегда должно быть меньше 100% (но на многоядерных/ЦП, но вы поняли идею). Загрузка показывает загрузку: сколько процессов запущено и ожидает запуска.
Как вы, вероятно, знаете (из обсуждения в вашем вопросе), ввод-вывод обычно является одним из таких ожиданий, поэтому он увеличивает нагрузку. Но у вас также могут быть сигналы/семафоры/блокировки, которые могут вызвать ожидание, и они могут быть вызваны только одним процессом, который не выполняет ввод-вывод). Например, если один процесс просыпается каждую секунду, и есть много процессов, которые ждут данных от такого процесса, вы получаете более высокую нагрузку (равную количеству ожидающих процессов).
Вы можете видеть каналы как ввод-вывод, но mmap и блокировки... вы классифицируете как ввод-вывод? Они не будут отображаться в bio (блок ввода-вывода), поэтому вы можете не увидеть их во многих статистиках нагрузки.
Часто более простой способ узнать это: заблокировать процесс и проверить, где он находится. Сделайте это много раз, и вы, как можно надеяться, увидите, что одна функция блокируется (и вы можете обнаружить это гораздо чаще, чем другие функции).
решение3
У меня возникла та же проблема на встраиваемой системе, работающей на i.mx28 на частоте примерно 450 МГц.
htop
постоянно отображалась загрузка ЦП на 50%, вызванная исключительно одной из swupdate
задач.
При просмотре mongoose_interface.c
вашего 100-мс наблюдения, сработавшего при прочтении этого в конце start_mongoose()
:
mg_mgr_poll(&mgr, 100);
Я экспериментально изменил 100 на 1000, и после перекомпиляции и перезапуска загрузка ЦП снизилась примерно до 2% потоков, swupdate
как и наблюдалось в htop
.
Как я уже сказал, это было экспериментально, так как цифры показались мне слишком уж похожими на совпадения. Я не исследовал, возникают ли какие-либо побочные эффекты.