
Recentemente, observamos uma alta média de carga de cerca de 1,5 em nosso sistema embarcado, embora praticamente todos os processos estejam supostamente inativos (de acordo com htop
).
O sistema em questão é um Cortex-A9 dual-core rodando um kernel Linux em tempo real (4.14.126) construído usando buildroot. Estamos usando initramfs para nosso sistema de arquivos raiz e não há troca, então definitivamente hásem E/S de discodurante a operação normal.
Depois de pesquisar um pouco, descobrimos que a carga é causada por um programa chamadoatualizar, que nos fornece uma interface web conveniente para atualizações de software (e gostaríamos muito de continuar usando isso).
Quando eu uso time
para estimar o uso médio da CPU desse aplicativo (calculando(usuário+sistema)/real), obtenho um valor de apenas cerca de 1%, o que não faz muito sentido considerando a média de carga de 1,5.
Eu sei que a média de carga também inclui processos no TASK_UNINTERRUPTIBLE
estado, que não contribuem para o uso da CPU. O que não entendo é por que algum dos threads/processos desse aplicativo estaria nesse estado.
Para analisar melhor a situação, capturei um rastreamento do kernel usandolttng, o que mostra que a única coisa que o swupdate faz é isto (a cada 50ms):
Como você pode ver, há um pouco do que parece ser um IPC baseado em soquete e um select aguardandoalgo. No caso do IPC, um thread parece estar bloqueando principalmente em nanosleep()
, enquanto o outro está bloqueando em accept()
, nenhum dos quais deve consumir recursos do sistema, até onde eu sei.
Para sua informação: a base de tempo para ambas as capturas de tela é a mesma e o IPC leva aprox. 500-600µs no total (o que, considerando o intervalo de 50ms, se ajusta perfeitamente ao uso observado de 1% da CPU)
Então, o que está causando a carga aqui?
Responder1
Como as tarefas no estado R e D contribuem para a carga do Linux, você pode obter amostras de todos os threads do seu sistema em qualquer um desses estados. Por exemplo:
for x in {1..100} ; do ps -aeos,user,comm,wchan | grep "^[RD]" ; sleep 0.1 ; done | sort | uniq -c | sort -nbr | head -20
Exemplo de saída abaixo, você precisaria ignorar a linha superior mostrando seu próprio ps
processo sempre ativo - já que é aquele que faz toda a amostragem:
# 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
A menos que você esteja executando em um kernel antigo, você deve executá-lo como root, pois os kernels mais recentes mascaram os valores WCHAN dos processos de outros usuários.
Você pode ir mais fundo do que isso (mas não com ps), você pode experimentar /proc/PID/syscall
e /proc/PID/stack
obter informações sobre chamadas do sistema e rastreamento de pilha do kernel também. Eu escrevi uma ferramenta chamada Linux Process Snapper ( psn
) para isso, para que você possa fazer um detalhamento bastante avançado desses problemas de desempenho sem precisar recorrer ao rastreamento do kernel:
[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
Você pode ir muito mais fundo do que isso, uma entrada de blog relevante está aqui:
Responder2
O uso e a carga da CPU são métricas diferentes. Na verdade, a carga pode estar acima de 1. CPU é o tempo real usado pela CPU, portanto deve ser sempre menor que 100% (mas em vários núcleos/CPUs, mas você entendeu). Carga indica a carga: quantos processos estão em execução e aguardando para serem executados.
Como você provavelmente sabe (pela discussão em sua pergunta), a E/S geralmente é uma dessas esperas, por isso aumenta as cargas. Mas você também pode ter sinais/semáforos/bloqueios que podem causar espera, e estes podem ser causados apenas por um processo que não está executando E/S). Por exemplo, se um processo for ativado a cada segundo e houver muitos processos aguardando dados desse processo, você obterá uma carga maior (igual ao número de processos em espera).
Você pode ver pipes como I/O, mas mmap e locks... você classifica como IO? Eles não aparecerão na bio (bloco de E/S), portanto você poderá não vê-los em muitas estatísticas de carga.
Muitas vezes a maneira mais fácil de descobrir isso: bloquear um processo e verificar onde ele está. Faça isso muitas vezes e você verá que uma função está bloqueando (e você poderá descobrir isso com mais frequência do que outras funções).
Responder3
Eu estava tendo o mesmo problema em um sistema embarcado rodando em um i.mx28 a aproximadamente 450 MHz.
htop
mostrava constantemente 50% de uso da CPU, causado exclusivamente por uma das swupdate
tarefas.
Ao navegar pela mongoose_interface.c
sua observação de 100 ms acionada ao ler isto no final de start_mongoose()
:
mg_mgr_poll(&mgr, 100);
Alterei experimentalmente de 100 para 1000 e, após recompilar e reiniciar, o uso da CPU caiu para cerca de 2% dos swupdate
threads, conforme observado em htop
.
Como dito, isso foi experimental, pois os números me pareceram uma coincidência demais. Não investiguei se ocorrem quaisquer efeitos colaterais.