¿Quieres dar formación sobre rendimiento en Solaris/OpenSolaris a tus técnicos?

A raíz del interés mostrado en las últimas charlas he decidido ofrecer mis servicios para consultoría y formación a empresas.

Ponte en contacto conmigo para pedir información.

OpenSolaris: Dispatch Queues

Introducción.

Como veíamos en el artículo de los cambios de contexto, un thread puede ser desalojado de la CPU por varios motivos. Lógicamente llegará un punto en el que tendrá que ser de nuevo alojado para poder continuar con su ejecución. Normalmente las CPUs suelen ser un recurso escaso por lo que fácilmente puede darse el caso que dos o más threads deseen ejecutarse a la vez y no dispongamos de suficientes CPUs libres. Cuando esto sucede el thread de menos prioridad queda a la espera en una dispatch queue, también llamada run queue. Cuando el thread que ocupa la cpu termina, el primero esperando en la queue pasa a ser ejecutado.

Hasta aquí es más o menos sencillo, pero puede darse la situación que un thread de más prioridad que el que esta esperando y menor que el que esta ocupando la CPU desee ejecutarse también por lo que tendrá que ser insertado en la posición de la cola que le corresponda, en este caso delante del thread que teníamos esperando.

¿ Fácil verrdad ?, bueno pues así es como NO funciona OpenSolaris, digamos que sería el modelo de un kernel Linux 2.4.x, sin embargo lo he planteado para que se entienda el problema que debemos resolver.

Dispatch Queues en Solaris.

La implementación de las colas de ejecución en OpenSolaris es algo más compleja que el modelo anterior, básicamente porque este plantea dos grabes problemas, el primero es que con una sola cola en un sistema multiprocesador habría númerosos interbloqueos en ella, además la necesidad de reordenarla continuamente a medida que nuevos threads con prioridades distintas se ponen en estado runnable es un gran gasto de recursos.

Para solventar ambos problemas OpenSolaris crea un set de dispatch queues por cada núcleo de procesador que tenemos. En cada set hay una cola por cada una de las prioridades globales.

La excepción son los threads de la scheduling class real time, para estos solo hay un set de colas para todos los procesadores.

Jugando con kmdb

Primero vamos a obtener información acerca de las cpus disponibles en nuestro equipo

> ::cpuinfo
 ID ADDR        FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD      PROC
  0 3000121a000  1b    0    0  -1   no    no t-0    2a10041dcc0 (idle)
  1 3000121e000  1b    0    0  -1   no    no t-0    2a1004a5cc0 (idle)
  2 30001224000  1b    0    0  59   no    no t-0    30010cc9c80 sshd
  3 0000180c000  1b    0    0  59   no    no t-0    300267d9c60 mdb
 16 30001228000  1b    0    0  49   no    no t-0    30029c3b2c0 oracle
 17 3000122c000  1b    0    0  49   no    no t-2    30010cd0340 oracle
 18 30001232000  1b    0    0   0   no    no t-1    30012caf620 oracle
 19 30001236000  1b    0    0 165   no    no t-0    2a10079fcc0 sched
Como vemos nuestro sistema tenemos 8 procesadores, el dcm cpuinfo nos da información acerca de que thread se esta ejecutando y con que prioridad. El campo ADDR apunta a la estructura cpu_t de cada procesador.

> 3000122c000 ::print cpu_t ! grep disp
    cpu_disp = 0x60001a35980
    cpu_dispthread = 0x30018c53900
    cpu_disp_flags = 0
    cpu_dispatch_pri = 0x3b
Si volcamos esa estructura vemos que tiene varios campos relacionados con las dispatch queues, el puntero cpu_disp es la dirección de memoria de la estructura disp_t con la información más importante de las colas, otro campos interesantes son cpu_dispthread y cpu_dispatch_pri , que son el thread que se esta ejecutando actualmente y su prioridad respectivamente.

> 0x60001a35980 ::print disp_t
{
    disp_lock = 0
    disp_npri = 0xaa
    disp_q = 0x60003914000
    disp_q_limit = 0x60003914ff0
    disp_qactmap = 0x6000296c828
    disp_maxrunpri = 0xffff
    disp_max_unbound_pri = 0xffff
    disp_nrunnable = 0
    disp_cpu = 0x3000122c000
Si volcamos la dirección de memoria de la estructura disp_t vemos más información interesante, disp_q es un puntero a la dirección de memoria donde está el primer registro del set de colas, el array esta ordenado de más prioridad a menos.


Podemos volcar esa posición para ver la estructura del campo.

> 0x60003914000 ::print dispq_t
{
    dq_first = 0
    dq_last = 0
    dq_sruncnt = 0
}
dq_first apunta al primer thread de esta cola, dq_last al último y dq_sruncnt al total de ellos.


Como podéis ver los datos a través del ejemplo no son coherentes eso es debido ha que he hecho un volcado con el sistema ejecutando por lo que la información de las colas ha sido modificado varios miles de veces en el tiempo que yo ejecutaba los comandos.


Finalmente podemos usar el walker cpu_dispq para ver los threads esperando en el array de colas dada una cpu

> ::cpuinfo
 ID ADDR     FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD   PROC
  0 fec220e8  1b    4    0  59   no    no t-1    d49e9c00 mdb
> fec220e8 ::walk cpu_dispq
d8835600
d8f82a00
Creative Commons License
Esta obra está bajo una licencia de Creative Commons.