Ajustando recursos para tareas e IRQs de tiempo real

Después de haber parcheado el kernel usando los parches de tiempo real de Ingo Molnar, ejecuto jack y observo que no funciona. El error es que no es capaz de ejecutar en tiempo real. Joder, pero si acabo de parchear el kernel… El tema es que las aplicaciones de tiempo real, además de algunos cambios en el kernel, necesitan de ciertos ajustes para poder funcionar «bien».

Aquí toca hablar de las capabilities de linux. Cuando un proceso (o hebra) está corriendo, va a necesitar y usar memoria y recursos del procesador (como tiempo de cpu para ejecutarse). He parcheado el kernel para cambiar el planificador de tareas (y los mutex, entre otras cosas), y afinar las respuestas de las aplicaciones que ejecutan en tiempo real. Y entonces surgen algunas dudas que evocan a alguna clase de Sistemas Operativos: ¿con qué prioridades se lanzarán dichas tareas? No me puedo quedar «colgado» en medio de una grabación. Bueno, mi kernel es PREEMTIBLE y esto asegura que el propio kernel puede abandonar la cpu para que otro proceso con gran demanda de cpu ejecute. Esto nos alivia de esas partes del código del kernel que son largas o poco eficientes.

Dado que las aplicaciones para grabar audio las lanzo como un usuario normal, ¿con qué prioridad ejecutarán? ¿será suficiente? Las aplicaciones de audio son las que necesitan tiempo real en mi caso. Oye, y si el proceso que yo lanzo lo lanzo como un usuario pero este proceso tiene acceso al procesador de forma voraz (no olvido que mi kernel es PREEMPTIBLE), ¿cómo aseguro que una tarea que se vuelve loca no colapsa al resto de mis tareas? Ah, para esto estaban las prioridades.

O, ya desde un punto de vista más cercano al código y al sistema, ¿cómo puedo reservar memoria como un usuario normal desde esas tareas de tiempo real sin perjudicar el rendimiento? ¿qué límites le pongo a la cantidad de memoria máxima que puede reservar cada proceso?

De todo esto se encarga PAM, y los rlimits. Yo pensaba que PAM se encargaba únicamente del tema de autenticación, pero fíjate tú que el bicho ha evolucionado (supongo que debido a SELinux, podría buscar sobre esto, que ya me ha picado la curiosidad :) ) y ahora también se pueden controlar los recursos disponibles para tareas (incluso hace una pequeña distinción entre tareas hard y soft, con mayor y menor prioridad respectivamente). O sea, que lo del tiempo real va avanzando dentro del kernel (aunque sigue sin ser tan potente como RTAI, también es verdad que escribir aplicaciones y montar un sistema debe ser más simple).

Vamos al lío. Hay muchas cosas que he hecho para ver qué le pasaba a mi sistema, y hago aquí un resumen de ellas (de algunas me habré olvidado).

Después de parchear el kernel ejecuté jackd y no podía arrancar en tiempo real. Ya me pasó hace unos meses, y lo solucioné instalando el realtime-lsm y editando el /etc/limits.conf (o algo así). Con PAM es casi lo mismo, ya que creo que sus funcionalidades son parecidas. Siguiendo algunos foros y listas por internet edito el fichero /etc/security/limits.conf. Está todo comentado, así que añado al final las líneas:

#* – nice -10
#* – rtprio 99
#* – memlock 4000000
@audio – nice -10
@audio – rtprio 99
@audio – memlock 4000000

Observo lo que acabo de hacer. Las tres directivas se aplican al grupo «audio», que ya existe en mi sistema, y al cual perteneces mi usuario (esto lo veo en /etc/group). Así que los programas que ejecute un usuario de este grupo tendrán un nice de -10 (no es -20 pero es bueno para el planificador). La prioridad para las aplicaciones de tiempo real que lance un usuario de este grupo, será la máxima, 99. Esto mola :) porque así sé que jackd y ardour van a ejecutar con muchísima prioridad. Se limita la cantidad de memoria que un proceso puede reservar 4 GB. Esto no me cuadra, y me parece muchísimo para una aplicación de tiempo real, pero en varios sitios que miré estaba este valor o incluso alguno superior… Lo dejé en éste. Recopilando, una tarea en este escenario podría ejecutar con máxima prioridad y reservar y llenar de 0’s 4 GB de memoria, lo cual se traducirá en un swapping interesante, cuanto menos. Bueno, lo lógico es suponer que los que programan esto saben lo que hacen :)

Por cierto las líneas comentadas, se aplicarían para cualquier usuario («*») y sólo las usaré si veo que no me funciona el tema de los grupos o me da problemas.

Reinicio el PC y vuelvo a ejecutar jackd. Ahora ejecuta sin problemas y la cosa promete :)

Compruebo que ahora si hay prioridades para tiempo real ahora:

ulimit -a

y me sale:

core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
max nice (-e) 29
file size (blocks, -f) unlimited
pending signals (-i) 8189
max locked memory (kbytes, -l) 4000000
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
max rt priority (-r) 99
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 8189
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

Vale, veo ahora que los límites que he puesto en /etc/security/limits.conf están activos y PAM funcionando correctamente.

Otro tema que se me planteaba es: ¿y cómo puedo hacer que la interrupción asociada a mi tarjeta de sonido tenga más prioridad en ser atendida que la del teclado, por ejemplo? Y otro problema que se cernía sobre mi cabeza: ¿y si mi tarjetón de audio comparte IRQ con algún otro dispositivo? Eso sería un problema, así que vamos a ver qué pasa con esto.

Para saber las interrupciones del sistema hago:

cat /proc/interrupts

Y me suelta:

CPU0
0: 96 IO-APIC-edge timer
1: 2 IO-APIC-edge i8042
6: 3 IO-APIC-edge floppy
7: 0 IO-APIC-edge parport0
8: 0 IO-APIC-edge rtc
9: 0 IO-APIC-fasteoi acpi
12: 4 IO-APIC-edge i8042
14: 102397 IO-APIC-edge ide0
17: 5909 IO-APIC-fasteoi HDA Intel
19: 0 IO-APIC-fasteoi hdsp
20: 47 IO-APIC-fasteoi uhci_hcd:usb1
21: 4 IO-APIC-fasteoi uhci_hcd:usb3, ehci_hcd:usb5
22: 77665 IO-APIC-fasteoi uhci_hcd:usb2
23: 0 IO-APIC-fasteoi uhci_hcd:usb4
24: 1446850 IO-APIC-pcix-fasteoi nvidia
36: 55684 IO-APIC-pcix-fasteoi eth0
NMI: 0
LOC: 9502709
ERR: 0

Me fijo en hdsp, que es mi tarjetón :) Usa la IRQ19 y no la comparte con nadie :)

Vale, ahora a ver con qué prioridad se atienden las IRQ en mi sistema:

top -b -H -n 1 | grep -i irq | sort -n -k3

que me devuelve:

335 root -91 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-8
2005 root -86 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-19
1998 root -85 -5 0 0 0 S 0.0 0.0 0:00.04 IRQ-17
935 root -81 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-20
954 root -80 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-21
953 root -79 -5 0 0 0 S 0.0 0.0 0:01.90 IRQ-22
955 root -78 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-23
372 root -76 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-1
371 root -75 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-12
1018 root -51 -5 0 0 0 S 0.0 0.0 0:01.27 IRQ-14
10 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 softirq-sched/0
11 root -51 -5 0 0 0 S 0.0 0.0 0:00.25 softirq-rcu/0
1978 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-7
2446 root -51 -5 0 0 0 S 0.0 0.0 0:00.31 IRQ-36
3067 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-4
3306 root -51 -5 0 0 0 S 0.0 0.0 0:39.95 IRQ-24
4 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 softirq-high/0
51 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-9
5 root -51 -5 0 0 0 S 0.0 0.0 0:12.75 softirq-timer/0
6 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 softirq-net-tx/
7 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 softirq-net-rx/
8 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 softirq-block/0
952 root -51 -5 0 0 0 S 0.0 0.0 0:00.00 IRQ-6
9 root -51 -5 0 0 0 S 0.0 0.0 0:00.01 softirq-tasklet

La interrupción 19 es la que me interesa, y veo que no está nada mal :) Pero podría ponerle aún más prioridad usando rtirq y chrt.

Asumiendo que la tarjeta de audio tiene siempre la IRQ-19 (no cambia al reiniciar el equipo) ejecuto:

chrt -f -p 90 `pidof «IRQ-19″`

Si vuelvo a ejecutar ulimit -a veo que ahora la IRQ-19 tiene una prioridad de -91. Podría ejecutar esta instrucción con cada arranque del sistema, colocándolo en /etc/init.d o /etc/rcX.d.

Leyendo por ahí, veo que hay otra manera de asignar prioridades a las IRQ, y es a través de rtirq (que hay que instalar con apt). La información acerca de este método la encontré aquí:

http://mggmail.blogspot.com/2007/07/re-rtirq-fixed.html

Dejo un poco de lado el tema de las prioridades de las interrupciones y veo ahora con qué prioridad ejecuta jackd:

top -b -H -n 1 | grep -i jackd | sort -n -k3

3924 fabi 20 0 36568 19m 3284 D 0.0 2.0 0:00.01 jackd
3925 fabi 20 0 36568 19m 3284 S 0.0 2.0 0:00.00 jackd
3926 fabi 20 0 36568 19m 3284 S 0.0 2.0 0:00.00 jackd

Mmm, 20 me resulta muy poco… investigar.

Bueno, el caso es que ya tengo las capabilities habilitadas y jackd funciona en tiempo real sin problemas. Nada mejor que un buen guitarreo como examen ;)

Ahora mismo jackd está funcionando con 2.66 ms de latencia, y 0 xruns en media hora. Y eso grabando tres pistas de audio y reproduciendo otras dos simultáneamente.

Cosas que me quedan pendientes:

– investigar más sobre este tema en general.

– publicar algo sobre configuración de alsa y la RME.

– recompilar un kernel con lo mínimo para hacer pruebas.

– descargar (meter en blacklist) los módulos que no quiera iniciar cuando trabaje con audio, para seguir con la comparativa.

– ver porqué jackd ejecuta con 20

Y enlaces: mientras buscaba información por internet, encontré una página algo obsoleta (referente al kernel 2.4) pero muy interesante:

Which is better — the preempt patch, or the low-latency patch? Both!

Otro enlace interesante es el de un chaval que habla de cómo preparar un sistema debian para trabajar con audio. Habla pricipalmente de eliminar los módulos necesarios y eso:

http://www.restivo.org/blog/archives/113

Uf, esto es todo de momento :)

~ por telemacro en 26 octubre, 2007.

Deja un comentario