miércoles, 6 de abril de 2011

Jugando con cpuset y los procesadores

Hace poco descubrí casi que por casualidad una pequeña utileria llamada cpuset. Leyendo el manual me entero que cpuset se usa para confinar procesos a procesadores y a bancos de memoria específicos.
Se pueden hacer cosas bastante interesantes con esto si uno tiene una maquina con multiprocesadores y le aplica algo de imaginacion al asunto. El procedimiento es algo truculento pero no es complicado en absoluto.


Lo primero que tenemos que hacer es (como root):
mkdir /dev/cpuset
mount -t cpuset cpuset /dev/cpuset
Es interesante como se mantiene la filosofía de UNIX de todo es un archivo. A pesar de que estamos "montando" un filesytem, realmente lo que estamos viendo una interfaz a parámetros del kernel de Linux.
Si damos un ls -l /dev/cpuset veremos:
ls -l
total 0
--w--w--w- 1 root root 0 2011-04-06 09:10 cgroup.event_control
-r--r--r-- 1 root root 0 2011-04-06 09:10 cgroup.procs
-rw-r--r-- 1 root root 0 2011-04-06 09:10 cpu_exclusive
-rw-r--r-- 1 root root 0 2011-04-06 09:10 cpus
-rw-r--r-- 1 root root 0 2011-04-06 09:10 mem_exclusive
-rw-r--r-- 1 root root 0 2011-04-06 09:10 mem_hardwall
-rw-r--r-- 1 root root 0 2011-04-06 09:10 memory_migrate
-r--r--r-- 1 root root 0 2011-04-06 09:10 memory_pressure
-rw-r--r-- 1 root root 0 2011-04-06 09:10 memory_pressure_enabled
-rw-r--r-- 1 root root 0 2011-04-06 09:10 memory_spread_page
-rw-r--r-- 1 root root 0 2011-04-06 09:10 memory_spread_slab
-rw-r--r-- 1 root root 0 2011-04-06 09:10 mems
-rw-r--r-- 1 root root 0 2011-04-06 09:10 notify_on_release
-rw-r--r-- 1 root root 0 2011-04-06 09:10 release_agent
-rw-r--r-- 1 root root 0 2011-04-06 09:10 sched_load_balance
-rw-r--r-- 1 root root 0 2011-04-06 09:10 sched_relax_domain_level
-rw-r--r-- 1 root root 0 2011-04-06 09:23 tasks
Este es el set por defecto, luego crearemos uno o varios sets para asignar los recursos de procesador y memoria. En el podemos ver varios archivos, pero por lo pronto nos interesan 3; cpus, mems y tasks.
Para crear un nuevo set solo debemos crear un directorio dentro de /dev/cpuset/:
mkdir /dev/cpuset/uno
Luego listando el contenido veremos un listado completamente igual al del set principal:
ls -l
total 0
--w--w--w- 1 root root 0 2011-04-06 09:12 cgroup.event_control
-r--r--r-- 1 root root 0 2011-04-06 09:12 cgroup.procs
-rw-r--r-- 1 root root 0 2011-04-06 09:12 cpu_exclusive
-rw-r--r-- 1 root root 0 2011-04-06 09:13 cpus
-rw-r--r-- 1 root root 0 2011-04-06 09:12 mem_exclusive
-rw-r--r-- 1 root root 0 2011-04-06 09:12 mem_hardwall
-rw-r--r-- 1 root root 0 2011-04-06 09:12 memory_migrate
-r--r--r-- 1 root root 0 2011-04-06 09:12 memory_pressure
-rw-r--r-- 1 root root 0 2011-04-06 09:12 memory_spread_page
-rw-r--r-- 1 root root 0 2011-04-06 09:12 memory_spread_slab
-rw-r--r-- 1 root root 0 2011-04-06 09:13 mems
-rw-r--r-- 1 root root 0 2011-04-06 09:12 notify_on_release
-rw-r--r-- 1 root root 0 2011-04-06 09:12 sched_load_balance
-rw-r--r-- 1 root root 0 2011-04-06 09:12 sched_relax_domain_level
-rw-r--r-- 1 root root 0 2011-04-06 09:23 tasks
Ahora podemos proceder a asignarle recursos a este set, recuerdan los archivos cpus, mems y tasks? Bueno, pasaremos a darles uso.
Entramos en directorio recien creado uno. Y lo primero que debemos hacer es asignar memorias y procesadores. Como no estamos en una maquina servidora con múltiples nodos de memoria, pues la memoria siempre estará en el banco 0 así que:
/bin/echo 0 > mems
Y ahora los procesadores:
/bin/echo 0-1,3 > cpus
Notese la manera de asignar los procesadores (que vale también para los bancos de memoria).
Una vez hecho esto lo único que queda es proceder a asignar los procesos a este set de cpu para lo que tenemos que agregar cada PID al archivo task uno en cada linea. Aquí es donde esta la parte triquiñosa, seleccionar todos los procesos asociados a una aplicación no siempre es trivial.
Para este ejemplo me serví de las utilerias del bash para hacer el truco, seleccione la aplicación que quería cambiar al set uno, el chrome, e hice lo siguiente:
for task  in `ps aux | grep chrome | awk '{print $2}'`; do /bin/echo $task > tasks; done
Diseccionando la linea tenemos un bucle que ejecuta el comando /bin/echo. El bucle es alimentado por una cadena de instrucciones que lanzan el listado de procesos asociados con chrome.
Para comprobar que los procesos están donde deben estar encojemos uno y escribimos:
taskset -cp nro_proc
El resultado sera algo como :
pid XXXX's current affinity list: 0,1,3
Y listo los procesos esta corriendo en los procesadores seleccionados.
Ahora bien, para que nos sirve todo esto? Pues por principio, podríamos ejecutar un vídeo con mplayer o vlc y asignarles un par de procesadores de forma exclusiva con lo que aseguraríamos de que ningún otro proceso consuma todos los recursos de procesador y el video quede saltado.
También podríamos dedicar un procesador únicamente para una compilación poco importante y no permitir que se consuma todos los recursos.
El limite es la imaginación.

No hay comentarios: