La respuesta es usar cpusets. La utilidad python cpuset facilita su configuración.
Conceptos básicos
3 juegos de CPU
root
:presente en todas las configuraciones y contiene todas las cpus (sin blindaje )system
:contiene CPU utilizadas para tareas del sistema, las que deben ejecutarse pero no son "importantes" (sin blindaje )user
:contiene CPU usadas para tareas "importantes" - las que queremos ejecutar en modo "tiempo real" (blindadas )
El shield
El comando administra estos 3 cpusets.
Durante la configuración, mueve todas las tareas móviles al conjunto de CPU sin blindaje (system
) y durante el desmontaje mueve todas las tareas móviles al root
cpuset.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 root
a system
(y por lo tanto fuera del user
cpuset).
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 deja 1 núcleo para tareas sin importancia; dado 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 root
cpuset (es decir, en todas las CPU)
$ cset shield --cpu 1-3
Algunos subprocesos del kernel (aquellos que no están vinculados a un CPU específico) se pueden mover al system
cpuset. (En general, no es una buena idea mover subprocesos del núcleo que se han vinculado a una CPU específica)
$ cset shield --kthread on
Ahora enumeremos lo que se está ejecutando en el escudo (user
) o sin blindaje (system
) cpusets:(-v
para verbose, que enumerará los nombres de los procesos) (agregue un segundo -v
para mostrar más de 80 caracteres)
$ cset shield --shield -v
$ cset shield --unshield -v -v
Si queremos detener el escudo (desmontaje)
$ cset shield --reset
Ahora vamos a ejecutar un proceso en el escudo (comandos después de '--'
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 varios procesos pasando una lista separada por comas o rangos (cualquier proceso en el rango se moverá, incluso si hay espacios))
$ 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 dan un control más preciso de cpusets
Conjunto
Crear, ajustar, renombrar, mover y destruir 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
Cambie "my_cpuset1" para usar solo cpus 1 y 3
$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1
Destruye un conjunto de CPU
$ cset set --destroy --set=my_cpuset1
Cambiar el nombre de 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
Enumere cpusets existentes (profundidad de nivel 1)
$ cset set --list
Lista cpuset existente y sus hijos
$ cset set --list --set=my_cpuset1
Listar todos los cpusets existentes
$ cset set --list --recurse
Proceso
Administrar 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
Mover 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
Mover una tarea y todas sus hermanas
$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads
Mover todas las tareas de un cpuset a otro
$ cset proc --move --fromset=my_cpuset1 --toset=system
Mover subprocesos del kernel no anclados a un cpuset
$ cset proc --kthread --fromset=root --toset=system
Mueva a la fuerza los subprocesos del núcleo (incluidos los que están anclados a una CPU específica) a un conjunto de CPU (nota:esto puede tener consecuencias nefastas 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
- Cree un
system
cpuset con 1 CPU (0) - Crea un
prio_low
cpuset con 1 cpu (1) - Crea un
prio_met
cpuset con 2 cpus (1-2) - Crea un
prio_high
cpuset con 3 cpus (1-3) - Crea un
prio_all
cpuset con las 4 cpus (0-3) (tenga en cuenta que esto es lo mismo que root; se considera una buena práctica mantener una separación de root)
Para lograr lo anterior, cree prio_all y luego cree un 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
Hay otras dos formas en las que puedo pensar en hacer esto (aunque no tan elegantes como cset, que no parece tener un fantástico nivel de soporte de Redhat):
1) Taskset todo, incluido el PID 1:agradable y fácil (pero, al parecer, nunca he visto ningún problema, puede causar ineficiencias en el programador). El siguiente script (que debe ejecutarse como root) ejecuta el conjunto de tareas en todos los procesos en ejecución, incluido init (pid 1); esto anclará todos los procesos en ejecución a uno o más 'núcleos basura' y, al anclar también init, garantizará que cualquier proceso futuro también se inicie en la lista de 'núcleos basura':
#!/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 más los mecanismos cset para varios proyectos (por cierto, disculpe la autopromoción descarada :-)), acabo de presentar una patente para una herramienta llamada Pontus Vision ThreadManager que presenta estrategias de fijación óptimas para cualquier plataforma x86 dada a cualquier carga de trabajo de software dada; después de probarlo en el sitio de un cliente, obtuve muy buenos resultados (reducción del 270 % en las latencias máximas), por lo que vale la pena fijarlo y aislar la CPU.