
Recientemente observamos un promedio de carga alto de aproximadamente 1,5 en nuestro sistema integrado, a pesar de que supuestamente todos los procesos están inactivos (según htop
).
El sistema en cuestión es un Cortex-A9 de doble núcleo que ejecuta un kernel de Linux en tiempo real (4.14.126) creado con buildroot. Estamos usando initramfs para nuestro sistema de archivos raíz y no hay intercambio, por lo que definitivamente haysin E/S de discodurante el funcionamiento normal.
Después de investigar un poco, descubrimos que la carga es causada por un programa llamadoactualizar, que nos proporciona una cómoda interfaz web para actualizaciones de software (y nos gustaría mucho seguir usándola).
Cuando suelo time
estimar el uso promedio de CPU de esa aplicación (calculando(usuario+sistema)/real), obtengo un valor de sólo alrededor del 1%, lo que no tiene mucho sentido considerando la carga promedio de 1,5.
Sé que el promedio de carga también incluye procesos en el TASK_UNINTERRUPTIBLE
estado que no contribuyen al uso de la CPU. Lo que no entiendo es por qué alguno de los subprocesos/procesos de esa aplicación estaría alguna vez en ese estado.
Para analizar más a fondo la situación, he capturado un seguimiento del kernel usandoltng, lo que muestra que lo único que hace swupdate es esto (cada 50 ms):
Como puede ver, hay un poco de lo que parece ser IPC basado en sockets y una selección en espera dealgo. En el caso de IPC, un hilo parece estar bloqueando principalmente nanosleep()
, mientras que el otro está bloqueando accept()
, ninguno de los cuales debería consumir recursos del sistema, hasta donde yo sé.
Para su información: la base de tiempo para ambas capturas de pantalla es la misma y el IPC tarda aprox. 500-600 µs en total (lo que, considerando el intervalo de 50 ms, encaja bastante bien con el 1 % de uso de CPU observado)
Entonces, ¿qué está causando la carga aquí?
Respuesta1
Dado que las tareas tanto en el estado R como en D contribuyen a la carga de Linux, puede probar todos los subprocesos de su sistema en cualquiera de estos estados. Por ejemplo:
for x in {1..100} ; do ps -aeos,user,comm,wchan | grep "^[RD]" ; sleep 0.1 ; done | sort | uniq -c | sort -nbr | head -20
Como resultado del ejemplo a continuación, deberá ignorar la línea superior que muestra su propio ps
proceso siempre activo, ya que es el que realiza todo el muestreo:
# 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 esté ejecutando un kernel antiguo, debe ejecutarlo como root ya que los kernels más nuevos enmascaran los valores WCHAN de los procesos de otros usuarios.
Puede ir más allá de eso (pero no con ps), puede probar /proc/PID/syscall
y /proc/PID/stack
obtener información de seguimiento de la pila del kernel y de llamadas al sistema también. He escrito una herramienta llamada Linux Process Snapper ( psn
) para esto, de modo que pueda realizar un análisis bastante avanzado de dichos problemas de rendimiento sin tener que recurrir al seguimiento del 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
Puedes ir mucho más allá, una entrada de blog relevante está aquí:
Respuesta2
El uso y la carga de la CPU son métricas diferentes. De hecho, la carga podría estar por encima de 1. La CPU es el tiempo real utilizado por la CPU, por lo que siempre debe ser inferior al 100% (pero en múltiples núcleos/CPU, pero se entiende la idea). Carga indica la carga: cuántos procesos se están ejecutando y esperando ser ejecutados.
Como probablemente sepa (por la discusión en su pregunta), la E/S generalmente es una de esas esperas, por lo que aumenta las cargas. Pero también puede tener señales/semáforos/bloqueos que podrían causar espera, y estos pueden ser causados simplemente por un proceso que no está realizando E/S). Por ejemplo, si un proceso se activa cada segundo y hay muchos procesos que están esperando datos de dicho proceso, obtendrá una carga mayor (igual a la cantidad de procesos en espera).
Es posible que vea las tuberías como E/S, pero mmap y locks... ¿se clasifican como IO? No aparecerán en la biografía (bloqueo de E/S), por lo que es posible que no los veas en muchas estadísticas de carga.
A menudo, la forma más sencilla de averiguarlo es bloquear un proceso y comprobar dónde se encuentra. Hágalo muchas veces y, con suerte, debería ver que una función se está bloqueando (y es posible que la encuentre con mucha más frecuencia que otras funciones).
Respuesta3
Estaba teniendo el mismo problema en un sistema integrado que se ejecuta en un i.mx28 a aproximadamente 450 MHz.
htop
constantemente mostraba un 50% de uso de CPU, causado únicamente por una de las swupdate
tareas.
Al navegar a través de mongoose_interface.c
su observación de 100 ms activada al leer esto al final de start_mongoose()
:
mg_mgr_poll(&mgr, 100);
Experimentalmente cambié 100 a 1000 y después de volver a compilar y reiniciar, el uso de la CPU se redujo a aproximadamente el 2 % de los swupdate
subprocesos, como se observa en htop
.
Como dije, esto ha sido experimental ya que los números me parecieron una gran coincidencia. No he investigado si se produce algún efecto secundario.