linux - picard - mp3tag
¿Cómo proteger una cpu del programador de linux(evitar que programe subprocesos en esa cpu)? (3)
Aquí se explica cómo hacerlo a la antigua usanza, utilizando cgroups. Tengo una máquina Fedora 28 y RedHat / Fedora quiere que uses systemd-run
, pero no pude encontrar esta funcionalidad allí. Me encantaría saber cómo hacerlo usando systemd-run
, si alguien quisiera iluminarme.
Digamos que quiero excluir mi cuarta CPU (de CPU 0-3) de la programación y mover todos los procesos existentes a CPU 0-2. Entonces quiero poner un proceso en la CPU 3 por sí mismo.
sudo su -
cgcreate -g cpuset:not_cpu_3
echo 0-2 > /sys/fs/cgroup/cpuset/not_cpu_3/cpuset.cpus
# This "0" is the memory node. See https://utcc.utoronto.ca/~cks/space/blog/linux/NUMAMemoryInfo
# for more information *
echo 0 > /sys/fs/cgroup/cpuset/not_cpu_3/cpuset.mems
- Específicamente, en su máquina querrá revisar
/proc/zoneinfo
y la/sys/devices/system/node
. Obtener la información de nodo adecuada se deja como un ejercicio para el lector.
Ahora que tenemos nuestro cgroup, necesitamos crear nuestro cgroup aislado de CPU 3:
cgcreate -g cpuset:cpu_3
echo 3 > /sys/fs/cgroup/cpuset/cpu_3/cpuset.cpus
# Again, the memory node(s) you want to specify.
echo 0 > /sys/fs/cgroup/cpuset/cpu_3/cpuset.mems
Coloque todos los procesos / subprocesos en el not_cpu_3
not_cpu_3:
for pid in $(ps -eLo pid) ; do cgclassify -g cpuset:not_cpu_3 $pid; done
Revisión:
ps -eL k psr o psr,pid,tid,args | sort | cut -c -80
¡NOTA! Los procesos actualmente en reposo no se moverán . Deben despertarse para que el programador los ponga en una CPU diferente. Para ver esto, elija su proceso de espera favorito en la lista anterior; un proceso, por ejemplo, un navegador web, que pensó que debería estar en la CPU 0-2 pero todavía está en 3. Usando su ID de hilo de la lista anterior, realice:
kill -CONT <thread_id>
ejemplo
kill -CONT 9812
Ejecute el comando ps y observe que se ha movido a otra CPU.
¡DOBLE NOTA! ¡Algunos hilos del núcleo no pueden y no se moverán! Por ejemplo, puede observar que cada CPU tiene un subproceso de núcleo [kthreadd]
. La asignación de procesos a cgroups funciona para procesos de espacio de usuario, no para hilos de núcleo. Esta es la vida en el mundo multitarea.
Ahora para mover un proceso y todos sus hijos al grupo de control cpu_3:
pid=12566 # for example
cgclassify -g cpuset:cpu_3 $pid
taskset -c -p 3 $pid
Nuevamente, si $pid
está inactivo, deberá activarlo para que el movimiento de la CPU se realice.
Para deshacer todo esto, simplemente elimine los cgroups que ha creado. Todos quedarán atrapados en el grupo raíz:
cgdelete -r cpuset:cpu_3
cgdelete -r cpuset:not_cpu_3
No es necesario reiniciar.
(Lo siento, no entiendo la tercera pregunta del póster original. No puedo comentar sobre eso).
Es posible utilizar sched_setaffinity
para anclar un hilo a una CPU, lo que aumenta el rendimiento (en algunas situaciones)
De la página man de linux:
Al restringir un proceso para que se ejecute en una sola CPU, también se evita el costo de rendimiento causado por la invalidación de la memoria caché que se produce cuando un proceso deja de ejecutarse en una CPU y, a continuación, vuelve a ejecutarse en una CPU diferente.
Además, si deseo una respuesta más en tiempo real, puedo cambiar la política del programador para ese hilo a SCHED_FIFO
, y aumentar la prioridad a algún valor alto (hasta sched_get_priority_max
), lo que significa que el hilo en cuestión siempre debe adelantarse a cualquier otro hilo que se ejecuta en su CPU cuando esté listo.
Sin embargo, en este punto, el subproceso que se ejecuta en la CPU, que el subproceso en tiempo real acaba de anular, posiblemente habrá desalojado gran parte de las entradas de caché de nivel 1 del subproceso en tiempo real.
Mis preguntas son las siguientes:
- ¿Es posible evitar que el programador programe cualquier subproceso en una CPU determinada? (por ejemplo: oculte la CPU completamente del programador, o de alguna otra manera)
- ¿Hay algunos subprocesos que absolutamente deben ser capaces de ejecutarse en esa CPU? (por ejemplo: hilos del núcleo / hilos de interrupción)
- Si necesito tener subprocesos del kernel ejecutándose en esa CPU, ¿cuál es un valor de prioridad máxima razonable para usar de tal manera que no se pierda de hambre los subprocesos del kernel?
Hay otras dos maneras en las que puedo pensar en hacer esto (aunque no es tan elegante como cset, que no parece tener un nivel fantástico de soporte de Redhat):
1) Tareas: todo, incluido el PID 1: agradable y fácil (pero, de todas formas, nunca he visto ningún problema por mi cuenta, puede causar ineficiencias en el programador). La siguiente secuencia de comandos (que debe ejecutarse como root) ejecuta el conjunto de tareas en todos los procesos en ejecución, incluido init (pid 1); esto fijará todos los procesos en ejecución a uno o más ''núcleos de correo no deseado'', y al también fijar el inicio, asegurará que cualquier proceso futuro también se inicie en la lista de ''núcleos de correo no deseado'':
#!/bin/bash
if [[ -z $1 ]]; then
printf "Usage: %s ''<csv list of cores to set as junk in double quotes>''", $0
exit -1;
fi
for i in `ps -eLfad |awk ''{ print $4 } ''|grep -v PID | xargs echo `; do
taskset -pc $1 $i;
done
2) use el parámetro del kernel isolcpus (aquí está la documentación de https://www.kernel.org/doc/Documentation/kernel-parameters.txt ):
isolcpus= [KNL,SMP] Isolate CPUs from the general scheduler.
Format:
<cpu number>,...,<cpu number>
or
<cpu number>-<cpu number>
(must be a positive range in ascending order)
or a mixture
<cpu number>,...,<cpu number>-<cpu number>
This option can be used to specify one or more CPUs
to isolate from the general SMP balancing and scheduling
algorithms. You can move a process onto or off an
"isolated" CPU via the CPU affinity syscalls or cpuset.
<cpu number> begins at 0 and the maximum value is
"number of CPUs in system - 1".
This option is the preferred way to isolate CPUs. The
alternative -- manually setting the CPU mask of all
tasks in the system -- can cause problems and
suboptimal load balancer performance.
He usado estos dos y los mecanismos cset para varios proyectos (incidentalmente, perdone la auto promoción descarada :-)), acabo de presentar una patente para una herramienta llamada Pontus Vision ThreadManager que presenta estrategias de fijación óptimas para cualquier dada la plataforma x86 a cualquier carga de trabajo de software dada; después de probarlo en el sitio de un cliente, obtuve muy buenos resultados (270% de reducción en las latencias máximas), por lo que vale la pena realizar el pinning y el aislamiento de la CPU.
La respuesta es usar cpusets . La utilidad Python cpuset facilita la configuración de los mismos.
Conceptos básicos
3 cpusets
-
root
: presente en todas las configuraciones y contiene todos los CPU ( sin blindaje ) -
system
: contiene los CPU que se utilizan para las tareas del sistema, las que deben ejecutarse pero no son "importantes" ( sin blindaje ) -
user
: contiene las CPU utilizadas para las tareas "importantes": las que queremos ejecutar en modo "en tiempo real" ( blindado )
El comando de shield
gestiona estos 3 cpusets.
Durante la configuración, mueve todas las tareas móviles al conjunto no protegido ( system
) y durante el desmontaje mueve todas las tareas móviles al conjunto de la root
. Después de la configuración, el subcomando le permite mover tareas al escudo ( user
) cpuset, y además, para mover tareas especiales (subprocesos del kernel) desde la root
al system
(y, por lo tanto, fuera del cpuset del user
).
Comandos:
Primero creamos un escudo. Naturalmente, el diseño del escudo dependerá de la máquina / tarea. Por ejemplo, supongamos que tenemos una máquina no NUMA de 4 núcleos: queremos dedicar 3 núcleos al escudo y dejar 1 núcleo para tareas sin importancia ; ya que no es NUMA, no necesitamos especificar ningún parámetro de nodo de memoria, y dejamos los subprocesos del kernel ejecutándose en el cpuset root
(es decir, en todos los cpus)
$ cset shield --cpu 1-3
Algunos subprocesos del núcleo (aquellos que no están vinculados a un CPU específico) se pueden mover al conjunto del system
. (En general, no es una buena idea mover los hilos del núcleo que se han enlazado a una CPU específica)
$ cset shield --kthread on
Ahora vamos a enumerar lo que se está ejecutando en el escudo ( user
) o en las redes no blindadas (del system
): ( -v
para verbose, que listará los nombres de los procesos) (agregue una 2 -v
para mostrar más de 80 caracteres)
$ cset shield --shield -v
$ cset shield --unshield -v -v
Si queremos detener el escudo (derribar)
$ cset shield --reset
Ahora ejecutemos un proceso en el escudo (los comandos que siguen a ''--''
se pasan al comando a ejecutar, no a cset
)
$ cset shield --exec mycommand -- -arg1 -arg2
Si ya tenemos un proceso en ejecución que queremos mover al escudo (tenga en cuenta que podemos mover múltiples procesos al pasar una lista separada por comas, o rangos (cualquier proceso en el rango se moverá, incluso si hay espacios vacíos))
$ cset shield --shield --pid 1234
$ cset shield --shield --pid 1234,1236
$ cset shield --shield --pid 1234,1237,1238-1240
Conceptos avanzados
cset set/proc
- estos le brindan un mejor control de los cpusets
Conjunto
Crea, ajusta, renombra, mueve y destruye cpusets
Comandos
Cree un cpuset, usando cpus 1-3, use el nodo NUMA 1 y llámelo "my_cpuset1"
$ cset set --cpu=1-3 --mem=1 --set=my_cpuset1
Cambia "my_cpuset1" para usar solo el cpus 1 y 3
$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1
Destruir un cpuset
$ cset set --destroy --set=my_cpuset1
Renombrar un cpuset existente
$ cset set --set=my_cpuset1 --newname=your_cpuset1
Crear un cpuset jerárquico
$ cset set --cpu=3 --mem=1 --set=my_cpuset1/my_subset1
Listar los conjuntos de cpusets existentes (profundidad de nivel 1)
$ cset set --list
Lista existente de la CPU y sus hijos.
$ cset set --list --set=my_cpuset1
Listar todos los cpusets existentes
$ cset set --list --recurse
Proc
Gestionar hilos y procesos.
Comandos
Lista de tareas que se ejecutan en un cpuset
$ cset proc --list --set=my_cpuset1 --verbose
Ejecutar una tarea en un cpuset
$ cset proc --set=my_cpuset1 --exec myApp -- --arg1 --arg2
Moviendo una tarea
$ cset proc --toset=my_cpuset1 --move --pid 1234
$ cset proc --toset=my_cpuset1 --move --pid 1234,1236
$ cset proc --toset=my_cpuset1 --move --pid 1238-1340
Moviendo una tarea y todos sus hermanos.
$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads
Mueve todas las tareas de un cpuset a otro
$ cset proc --move --fromset=my_cpuset1 --toset=system
Mueva los hilos del núcleo sin clavijas a un cpuset
$ cset proc --kthread --fromset=root --toset=system
Mueva a la fuerza los hilos del kernel (incluidos los que están sujetos a una CPU específica) en un cpuset (nota: esto puede tener graves consecuencias para el sistema, asegúrese de saber lo que está haciendo)
$ cset proc --kthread --fromset=root --toset=system --force
Ejemplo de jerarquía
Podemos usar cpusets jerárquicos para crear agrupaciones priorizadas
- Crear un
system
cpuset con 1 cpu (0) - Crear un
prio_low
prio_low con 1 cpu (1) - Crear un
prio_met
cpuset con 2 cpus (1-2) - Cree un
prio_high
cpuset con 3 cpus (1-3) - Cree un
prio_all
cpuset con los 4 cpus (0-3) (tenga en cuenta que esto es lo mismo que la raíz; se considera una buena práctica mantener una separación de la raíz)
Para lograr lo anterior, cree prio_all y luego cree el subconjunto prio_high en prio_all, etc.
$ cset set --cpu=0 --set=system
$ cset set --cpu=0-3 --set=prio_all
$ cset set --cpu=1-3 --set=/prio_all/prio_high
$ cset set --cpu=1-2 --set=/prio_all/prio_high/prio_med
$ cset set --cpu=1 --set=/prio_all/prio_high/prio_med/prio_low