Libertonia
Portada · Todo · Software Libre · Desarrolladores · Comunidad · Internet · Tecnología · Meta · Diarios
gestion de interrupciones

ridiculum's Diary
Por ridiculum
departamento toc toc, hay alguien? , Sección Diarios
Puesto a las Sun May 8th, 2005 at 08:33:53 PM CET
Primero de todo, dar las gracias por las ideas del post sobre continuidad. Ahora mismo el tema esta medio resuelto mirando cuantos puntos de esta operacion: x[n+1]-x[n] son cero. Si supera un umbral, es salto, sino, es rampa. Por ahora funciona bien, incluso en condiciones de ruido, pero vamos, se que no es la mejor solucion y debe haber otra.
Por cierto, en tiempo discreto se puede derviar, o al menos, en un principio se puede diseñar un sistema que derive, aunque no lo he probado aun. Luego lo cuento, que sino, no me centro.
Y ahora, al tema de esta entrada

 


Hace unos dias, A. Cox respondia a un correo en la lkml que opinaba que tenia un hardware con un fallo de diseño, ya que tenia las interrupciones por nivel, een lugar de por flanco. A. Cox decia algo asi a: "pero tio, si las interrupciones por nivel son lo mejor del mundo".
Yo he tenido que lidiar con interrupciones y ademas, por nivel, y aun me surge una duda, que posiblemente, en Linux este restuelta, pero en el contexto en el que yo estaba creo que no lo esta. El tema es el siguiente.

Las interrupciones se ejecutan en un contexto especial desde donde no se suele poder realizar ciertas operaciones, como un delay(), usar semaforos, o usar un printk o equivalente. Por eso (y otros motivos) es normal separalas en dos partes, una que se ejecuta en el contexto de interrupcion, y otro que se ejecuta en un contexto de proceso que en terminologia unix se ha llamado tradicionalmente bottom-half.
Una vez hecha esta ligera y necesaria introduccion para situar al personal en escena, veamos un esbozo del asunto. Adelanto que los nombres que voy a usar no existen (que yo sepa) en ningun SO, y solo prentender dar una idea.

void Interrupt8()
{
   wake_up(pid)
}

void bottom_half()
{
  while(1)
  {
     sleep();
     value16=read_hardware(reg16)
     if (value16 ==0x0800)
     {
        value32=read_hardware(reg32);
        if (value32 == 1)
           do_something1();
     }
     erase_irq()
  }
}
Bueno, la idea es que Interrupt8() es el manejador, que se instalara con la pertienente funcion que nos ofrezca la plataforma. Con wake_up(pid) despertamos al proceso/tarea que tiene como identificador 'pid'. Bottom_half() es precisamente eso y se ejecutara cuando se arranque la tipica funcion de inicializacion del hardware. Para efectos del ejemplo, podemos suponer que es un proceso/tarea/llamalo_como_quieras. Tiene un bucle infinito y la funcion sleep() que manda a dormir a este proceso. read_hardware accede al hardware que ha generado la irq y erase_irq lo que hace es volver a habilitar las interrupciones. Esto es asi, por que en las irq por nivel, es necesario volver a levantar la linea para que nos puedan volver a interrupir.

El meollo del asunto es el tiempo que transcurre entre la ultima instruccion de erase_irq(), donde ya nos pueden interrumpir, y la finalizacion de sleep(), donde ya estamos dormidos y nos pueden despertar.
Que sucede si en ese fragmento de tiempo, que aunque es muy pequeño no es nulo, nos interrumpen?. Pues la secuencia seria algo asi a:

  • el hardware se entera del evento,
  • se lo pasa al SO,
  • el saca de la CPU a lo que se este ejecutando (supongamos que es la primera instruccion de sleep() ),
  • el SO llama a Interrupt8,
  • Interrup8 intenta despertar al bottom_half y se termina todo.
Como el bottom_half no esta dormido, el intento de despertarlo queda en el limbo, y despues de eso, el SO le vuelve a dar la CPU para que se continue ejecutando, y ahora si termina de dormirse.

La situacion en la que nos hemos quedado es bastante comprometida: hemos perdido una interupcion, y la linea de irq se ha quedado a nivel bajo para siempre, con lo que el dispositivo que esta unido a esa linea no nos va a poder interrumpir nunca jamas. Hemos perdido la comunicacion con ese hardware.

Estoy seguro que este problema tiene solucion, pero no se cual sera, o al menos no se como escribirla. Posiblemente todo el problema venga del hecho de no ejecutar la gestion de interrupciones dentro del nucleo, pero tampoco estoy seguro. Los kernels monoliticos lo hacen asi, y los microkernels, al menos la primera parte de la gestion de irq, tambien es asi. Le he echando un vistazo rapido a la documentacion de QNX y no he salido de dudas.

< Movilización contra las patentes de software en Europa el 27-A (3 comments) | Debian en mac mini (I) (32 comments) >
Enlaces Relacionados
· A. Cox respondia
· a un correo en la lkml
· documentacion de QNX
· More on ridiculum's Diary
· Also by ridiculum

Menu
· crear cuenta
· FAQ
· búsqueda
· Fuentes de Noticias

Login
Nueva cuenta
Usuario:
Contraseña:

Ver: Modo: Orden:
gestion de interrupciones | 9 comentarios (9 temáticos, editoriales, 0 ocultos)
La solución del manual (none / 0) (#1)
por jorginius ("jorginius" en Google Mail) a las Sun May 8th, 2005 at 10:50:10 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

Las interrupciones se ejecutan en un contexto especial desde donde no se suele poder realizar ciertas operaciones, como un delay(), usar semaforos...

¿Por?. Evidentemente no tienes las primitivas POSIX dentro de un manejador de interrupciones, pero sí que tienes primitivas de bloqueo y de exclusión. Ya me dirás si no cómo accedes al hardware de manera controlada. Esto es así en QNX, Linux o el SO que más rabia te dé.

Precisamente, la manera estándar de resolver lo que propones es una cola de interrupciones y un spinlock/cola de espera.

La parte divertida es que, al haber dos spinlocks (el de acceso al hardware y el de la cola donde vas metiendo/leyendo las interrupciones) tienes que evitar el deadlock, pero vamos... Es de libro :-)



Ahm, no me había fijado (none / 0) (#2)
por jorginius ("jorginius" en Google Mail) a las Sun May 8th, 2005 at 11:02:58 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

Que en tu ejemplo el hardware se lee desde el bottom_half. Eso chirría un poco. El hardware debería leerse desde el manejador, con las interrupciones inhabilitadas, ¿no?.

El manejador sería algo así cómo:
spin_lock(&hwlock);
leo_registro_y_lo_guardo();
spin_unlock(&hwlock);
wake_up(&hwwait); /* hale, a trabajar */


[ Padre ]


Detalle de implementacion (none / 0) (#6)
por ridiculum a las Tue May 10th, 2005 at 12:55:46 AM CET
(Información Usuario)

Si, esta hecho asi por un motivo simple: delay(). El hardware esta mapeado en memoria y claro, entre lectura y lectura debe transcurrir un tiempo impuesto por el hardware. Eso lo hago con un delay() que normalmente estan prohibidos en los manejadore de interrupcion. Es solo por eso, me facilitaba la vida. El hardware, cuando lo leo desde el bottom half esta con las interrupciones deshabilitadas, al menos las que conciernen al dispositivo.

Supongo que si se mueve el acceso al hardware al manejador, habria que leer todos los registros susceptibles de ser modificiados por una proxima irq y guardarlos en la cola, no?
Supongo que una vez que accedo al hardware, debo hacer un enable_irq()?.

Y ya que estamos, sigamos para bingo. Supongamos que he sido capaz de escribir la cola y todo (y que aparentemente funciona), ¿como se sabe a que proceso hay que darle los datos que, por ejemplo, se han recibido del dispositivo? ¿habra que guardar en algun sitio que procesos estan bloqueado en una IRQ y cual de ellas, no? ¿o se hace por dispositivo por el tema de lineas compartidas?

PD Que conste que ahora mismo el chiringuito esta funcionando y va bien, pero se que siento curiosidad por como se resuelve este problema de forma correcta.

[ Padre ]


 
A veces creo que la vida es mas dificil (none / 0) (#5)
por ridiculum a las Tue May 10th, 2005 at 12:44:36 AM CET
(Información Usuario)

Puse semaforos por que estaba pensando en mi entorno particular. Solo tengo semaforos (y gracias). No tengo spinlocks ni ninguna otra primitiva de sincronizacion (bueno, supongo que el rendevouz de ada no cuenta). Evidentemente el API no es POSIX.

Y tu dirás, por que no te haces tu propio spinlock?. Bueno supongo que podria, aunque no me saldria tan decente como los de que hacen la gente que sabe, con una espera activa se podria atajar.

En la cola de interrupciones (supongo que una por linea de IRQ) tendras un aviso de que hubo una irq y que se debera comprobar en el bottom half antes de irse a dormir si la cola esta vacia, no?.

[ Padre ]


En líneas generales... (none / 0) (#9)
por jorginius ("jorginius" en Google Mail) a las Tue May 10th, 2005 at 05:20:33 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

Hago un remix y te contesto por aquí a los dos posts.

Supongo que una vez que accedo al hardware, debo hacer un enable_irq()?.

En principio sí, lo único que tiene que ser atómico es el acceso al hardware y a lo que compartan las interrupciones y el bottom half, si lo hay.

Luego cada dispositivo es un mundo y hay que calibrar lo que tiene que ir en el manejador y lo que puede ser ejecutado con las interrupciones activadas.

¿como se sabe a que proceso hay que darle los datos que, por ejemplo, se han recibido del dispositivo?

¿Quién y a qué proceso?, ¿el sistema operativo?, ¿a un proceso de usuario o a un hilo del sistema?. Hay mil formas. No es problema.

Solo tengo semaforos (y gracias). No tengo spinlocks ni ninguna otra primitiva de sincronizacion Un spinlock es, a fin de cuentas, un semáforo binario. Uno rápido, con espera activa preferentemente.

Si tienes semáforos lo tienes todo.

En la cola de interrupciones (supongo que una por linea de IRQ)

No tiene por qué. Depende del diseño, de donde reconozcas la interrupción, etc. Puedes guardar un identificador y que haya un dispatcher que esté recorriendo la cola y llamando al manejador adecuado según el id.

A veces resulta que es el hardware mismo el que encola las interrupciones.

Una "cola global" es bastante típica. Por ejemplo: al llegar una interrupción hardware, el top half, aparte de hablar con el hardware y pasar lo que le dé el dispositivo a un buffer, encola una tarea (el bottom half o manejador de segundo nivel). Luego el planificador se ocupará de ejececutar esas tareas ya con las interrupciones habilitadas.

Ah, una cosa que te aseguran todos los SO AFAIK es que, aunque los bottom half pueden ser interrumpidos, uno no puede estar ejecutándose más de una vez simultáneamente (las nuevas interrupciones que llegan por la misma línea se encolan pero no se procesan hasta que no se acaba con las antiguas).

tendras un aviso de que hubo una irq y que se debera comprobar en el bottom half antes de irse a dormir si la cola esta vacia, no?.

Bueno, la idea era un semáforo que lleve la cuenta de las interrupciones que quedan pendientes por procesar en la cola. El manejador lo incrementa y, si no hay más interrupciones, el bottom half se bloquea en el semáforo.

No sé, es un ejemplo: la idea es esa pero luego lo implementas como quieras. Este esquema tal cual no funciona si quieres más de un bottom half por interrupción pero se puede adaptar.

[ Padre ]


 
Todo sea por conseguir trabajo. (none / 0) (#3)
por jamarier a las Sun May 8th, 2005 at 11:41:11 PM CET
(Información Usuario) http://barbacana.net/blog/

Cuando yo dí interrupciones, la única conclusión fue: cortas y sal en cuanto puedas. ;-) Pero aviso que no llegamos a diseñar sistemas operativos así que lo que díga a partir de ahora puede que no valga ni los bytes que ocupa:

Si es necesario ejecutar funciones no disponibles en el «modo interrupción», una de las soluciones es no hacerlo en el modo de interrupción. Un sistema sería implementar una máquina de estados. Cuando se activa la interrupción, se varía el valor del estado del sistema. En puntos estratégicos, al estilo de los watchdogs, se comprueba el valor de la variable de estado y se bifurca el flujo del programa en el sentido adecuado. Aunque la interrupción se procesa en el momento que se genera, la función se ejecuta cuando puede. Podríamos llamarlo ¿interrupción cooperativa? por su similitud a los sistemas de multitarea cooperativa. Dependiendo de la complejidad del sistema se puede desarrollar una cola de eventos de interrupciones, etc.

Respecto al problema de que se llame una interrupción antes de que esté lista la bottom_half, se me ocurren 2 sistemas. El último micro que usé podias seleccionar las interrupciones que estaban activas. puedes desabilitar todas las interrupciones salvo una alarma de pocos microsegundos de valor (en otro vector de interrupción), pasados esos micro segundos se activan todas las interrupciones, y así hemos dado tiempo de ejecutar completo el sleep.

Quizás este otro sistema sea más interesante: (hay que depurar el reset del flag de irq
void Interrupt8()
{
   push_pila_direcciones(boton_half);
   erase_irq()
}

void bottom_half()
{
  value16=read_hardware(reg16)
  if (value16 ==0x0800)
  {
     value32=read_hardware(reg32);
     if (value32 == 1)
        do_something1();
  }
}


Es decir, la interrupción introduce fraudulentamente en la pila de direcciones la dirección de nuestra botton_half. Así que cuando la interrupción devuelve el control en vez de retornar al código desde la que se produce, salta a nuestra función botton_half. Al terminar está, se retorna a el punto donde se generó la introducción. Hay un detalle que no he plasmado y es que en función del sistema, en la pila de programa, se pueden introducir más información que en una llamada de función general (creo que se guardaba la dirección de retorno y el estado de los flags en el momento del salto. Así que hay que ajustar estas llamadas y los retornos para que el SO no se de cuenta de la función que has colado.

Si esto funciona, no te olvides que son ideas originales mías y que estoy buscando trabajo de altos vuelos.

-----
Opinión expresada por alguien que puede que no sea yo.



Similar a lo contado, no? (none / 0) (#8)
por ridiculum a las Tue May 10th, 2005 at 01:26:32 AM CET
(Información Usuario)

Creo que lo que propones es bastante parejo a lo que se ha comentado, aunque con cosas de muy bajo nivel. Por ejemplo, el push() que propones es muy similar a hacer un wake_up(), aunque claro, tal y como tu lo has escrito, el enable_irq() solo se ejecuta cuando termina el bottom_half, que solucionaria el tinglado.
Pero claro, tal y como tu llamas a bottom_half, este se ejecuta en el mismo contexto que la interrupcion, conlo que tienes los mismo problemas que si haces una llamada normal, creo yo. ¿Es asi, o me he perdido algo?

[ Padre ]


 
A propósito del tema (none / 0) (#4)
por shamkao a las Mon May 9th, 2005 at 05:53:59 PM CET
(Información Usuario)

En este enlace http://atc1.aut.uah.es/~assooit/PDFs/04%20-%20Interrupciones%20-%202005.pdf (un pdf de una asignatura que tuve), se trata el tema de las interrupciones en linux.

A lo mejor os sirve para aclarar conceptos. Además en esa misma página http://atc1.aut.uah.es/~assooit/downloads.html también podréis encontrar más información sobre el kernel de linux, como por ejemplo, las tareas bottom halves, que están en desuso pero que podréis encontrar en el pdf nº 5: "Tareas diferidas".



Esa me la perdi (none / 0) (#7)
por ridiculum a las Tue May 10th, 2005 at 01:14:44 AM CET
(Información Usuario)

Esa asignatura se quedo en el tintero y entre pitos y flautas no la curse. En un vistazo rapido se da un aire a uno de los capitulos de Understanding de Linux Kernel. Tengo un par de libros del kernel de Linux esperando a ser leidos, a ver si consigo algo de tiempo y termino los tengo entre manos y me pongo con ellos. Leere las trasparencias con atencion, a ver si las entiendo.
De todos modos, no estaria de mas tener una charla con Chan que hace tiempo que no paso por la escuela.

[ Padre ]


 
gestion de interrupciones | 9 comentarios (9 temáticos, editoriales, 0 ocultos)
Ver: Modo: Orden:

ecol Logo Powered by Scoop
Todas las Marcas Registradas y copyrights de esta página son propiedad de sus respectivos dueños.
Los comentarios son propiedad del que los escribe.
Los iconos de las noticias y el logotipo son propiedad de Javier Malonda.
El Resto © 2002 Escomposlinux.org y aledaños.

Puedes sindicar los contenidos de libertonia en formato RSS 1.0 y RDF 0.9. También se puede sindicar la cola de envíos pendientes de moderación.

El proyecto escomposlinux.org está dedicado a la memoria de tas

crear cuenta | faq | búsqueda