lunes, 22 de diciembre de 2008

Manejo de interrupciones y excepciones

Las interrupciones y las excepciones son eventos que indican que se ha producido una condición en algún lugar del sistema, que requiere la atención del procesador. Típicamente resultan en una transferencia de ejecución forzada desde el programa en ejecución hasta una rutina especial de software llamada administrador de interrupciones o de excepciones. La acción llevada a cabo por el procesador en respuesta a la interrupción o excepción se llama servir o mantener la instrucción o la excepción. Las interrupciones y las excepciones son muy similares: la principal diferencia es que las primeras son asíncronas (no se puede preveer su aparición; normalmente se producen por señales de hardware externo) y las segundas son síncronas (se puede preveer su aparición; ocurren normalmente cuando el procesador detecta algún error, ya sea de seguridad, de división por cero...). Cuando se recibe alguna interrupción o se detecta alguna excepción, el programa en ejecución se suspende mientras el procesador ejecuta el administrador de interrupciones o de excepciones. Cuando la ejecución del administrador se completa, el procesador retoma la ejecución del programa interrumpido. Todo esto ocurre sin perder continuidad en el programa, excepto si no es posible la recuperación desde una excepción o si una interrupción causa que no se pueda terminar la ejecución del programa interrumpido.
Para ayudar en el manejo de instrucciones y excepciones, se definen condiciones de excepciones y de interrupciones que requieren un manejo especial desde el procesador. A cada condición se le asigna un único número de identificación, llamado vector. El procesador usa el vector asignado a la excepción o interrupción como un índice en la tabla de descriptores de interrupciones (IDT). La tabla proporciona un punto de entrada a un administrador de interrupciones o de excepciones. El rango disponible para los vectores es de 0 a 255. Los vectores desde el 0 al 31 está reservados por las arquitecturas Intel 64 y IA-32 para las excepciones y interrupciones definidas por la arquitectura, pero no todos los vectores de ese rango tienen una función definida. Los vectores no asignados en ese rango están reservados igualmente, así que no se pueden usar. El resto de vectores (desde el 32 hasta el 255) se pueden usar, y normalmente las interrupciones se asignan a dispositivos externos de E/S para activarlos y que puedan mandar interrupciones al procesador a través de un mecanismo de interrupción externo por hardware. El procesador puede recibir interrupciones de dos fuentes; externas (generadas por hardware), o internas (generadas por software). Las interrupciones externas se reciben a través de pines del procesador o a través del APIC local.
Cualquier interrupción externa que se entrega al procesador es llamada interrupción por hardware, y no puede ser enmascarada.
La instrucción INT n permite interrupciones generadas desde software interno suplantando un número de vector de interrupción por un operando. Por ejemplo, INT 35 fuerza una llamada implícita al administrador de interrupciones para las interrupción 35. Cualquier vector de interrupción desde el 0 hasta el 255 puede ser usado como parámetro en ella. Si el vector NMI predefinido del procesador se está usando, el procesador producirá una respuesta diferente a cuando la interrupción NMI se genera de forma normal. Si el numero de vector 2 (el vector NMI) se usa para esa instrucción, el administrador de interrupciones NMI se invoca pero el hardware del administrador del NMI del procesador no se activa. Las interrupciones generadas por software con la instrucción INT n no se puede enmascarar por la bandera IF del registro EFLAGS.
El procesador recibe excepciones de tres fuentes: de las detecciones de errores de programas del procesador (el procesador genera una o más excepciones cuando detecta errores durante la ejecución de aplicaciones o del sistema operativo), de las generadas por software (las instrucciones INTO, INT 3 Y BOUND generan excepciones por software; INT 'n' permite emularlas), y de comprobaciones de la máquina (los Pentium y los P6 proporcionan mecanismos internos y externos de comprobación de máquina para comprobar la operación del chip hardware interno y las operaciones del bus).
Las excepciones se clasifican en faltas, trampas y abortos dependiendo de la forma en que son reportados y si la instrucción que la causó puede ser reiniciada sin pérdida de la continuidad del programa. Una falta es una excepción que generalmente se puede corregir y no provoca pérdida de continuidad en el programa. Cuando se reporta una falta, el procesador restaura el estado de la máquina al estado anterior para reiniciar la instrucción que la provocó. Una trampa es una excepción que se reporta inmediatamente después de ejecutar la instrucción que la provocó, y tampoco provoca pérdidas en la continuidad de la ejecución. Un aborto es una excepción que no siempre se reporta a la localización precisa de la instrucción que la provocó, y no permite el reinicio del programa que causó la excepción. Los abortos son usados para reportar errores graves, como errores de hardware o valores inconscientes o ilegales en las tablas del sistema.
Para permitir el reinicio del programa después del mantenimiento de una excepción o de una interrupción, todas las excepciones (excepto los abortos) y todas las interrupciones reportan excepciones o interrupciones en un límite de instrucción. Para las instrucciones de falta de clase, el retorno del puntero de instrucción apunta a la instrucción que provocó la falta. Por lo tanto, cuando un programa se reinicia después del mantenimiento de una falta, la instrucción culpable se reejecuta. Cuando ocurre una excepción de trampa de clase, el retorno del puntero de instrucción apunta a la instrucción siguiente a la culpable. Las excepciones de aborto de clase no soportan reinicios fiables del programa. Los administradores de abortos están diseñados para recolectar información de diagnóstico sobre el estado del procesador cuando la excepción ocurre, y entonces apaga la aplicación y el sistema como puede. Las interrupciones soportan reinicio de los programas interrumpidos sin perder continuidad. El puntero de instrucción de retorno guardado para una interrupción apunta a la siguiente instrucción a ejecutar en el límite de la instrucción donde el procesador atendió a la interrupción. Si la instrucción que se acaba de ejecutar tiene un prefijo repetido, la interrupción se atenderá al final de la iteración en curso con el conjunto de registros para ejecutar la siguiente iteración.
La interrupción que no puede ser enmascarada (NMI) se puede generar de dos formas: mediante un pin NMI externo, o cuando el procesador recibe un mensaje en el bus del sistema con un modo de entrega NMI. Cuando se recibe una NMI desde alguna de estas dos fuentes, el procesador la atiende inmediatamente llamando al administrador NMI apuntado por el vector de interrupciones número 2. El procesador también activa un conjunto de condiciones hardware para asegurar que no haya otras interrupciones, incluyendo interrupciones NMI, hasta que el administrador NMI completa su ejecución.
El procesador inhibe la creación de algunas interrupciones, dependiendo del estado del procesador y de las banderas IF y RF del registro EFLAGS. La bandera IF puede desactivar el servicio de interrupciones enmascaradas por hardware recibidas en el pin INTR del procesador a través del APIC local. Si la bandera está a cero, el procesador inhibe las interrupciones en ese pin. La bandera IF no afecta a las NMIs. La bandera RF en el registro EFLAGS controla la respuesta del procesador a las condiciones de breakpoint de instrucción. Cuando está activo, previene un breakpoint de instrucción generando una excepción de depuración; cuando no esta activo, los breakpoints de instrucción generarán las excepciones de depuración. La principal función de RF es prevenir al procesador de caer en un bucle de excepciones de depuración en un breakpoint de instrucción.
Una tabla de descriptores de interrupciones (IDT) asocia cada vector de excepción o interrupción con un descriptor de puerta para el procedimiento usado para servir a la excepción o interrupción asociada. Como las GDTs o las LDTs, la IDT es un array de descriptores de 8 bytes (en modo protegido). Para formar un índice en el IDT, el procesador escala el vector de excepción o de interrupción por ocho. Como solo hay 256 vectores de interrupción o de excepción, el IDT no necesita contener más de 256 descriptores (puede contener menos). La base de direcciones del IDT debe estar alineada en un límite de 8 bytes para maximizar el rendimiento de llenado de los caches.
La IDT contiene tres tipos de descriptores de puertas: de tareas, de interrupciones, y de trampa. Estos dos últimos son muy similares a las puertas de llamada, excepto en que el procesador administra la bandera IF del registro EFLAGS.
El procesador maneja las llamadas a los administradores de excepciones y a interrupciones de forma similar a las llamadas a procedimientos con CALL. Cuando se responde a una excepción o a una interrupción, el procesador usa el vector de excepción o de interrupción como un índice hacia un descriptor en el IDT. Si el índice apunta a una puerta de interrupción o a una puerta trampa, llama al administrador de interrupciones o de excepciones de manera similar que un CALL a una puerta de llamada. Si el índice apunta a una puerta de tarea, el procesador ejecuta un cambio de tarea al administrador de excepciones o de interrupciones de forma similar al CALL. Una puerta de interrupciones o de trampa referencia a un procedimiento de administración de excepciones o de interrupciones que se ejecuta en el contexto de el programa en ejecución. El selector de segmento de la puerta apunta a un descriptor de segmentos para un segmento de código ejecutable en cualquier GDT o en la LDT actual. El campo offset del descriptor de puerta apunta al principio del procedimiento de administración de excepciones o interrupciones. Cuando se accede a un administrador de excepciones o de interrupciones a través de una puerta de tarea en el IDT, resulta un cambio de tarea. Administrar una excepción o una interrupción en tareas separadas tiene múltiples ventajas: El contexto entero del programa interrumpido se guarda automáticamente, un nuevo TSS permite la administración para usar una nueva pila de privilegios de nivel 0, o que el administrador puede ser aislado de otros programas separándolo en el espacio de direcciones.
Cuando una condición de excepción está relacionada a un segmento específico, el procesador genera un código de error en la pila del administrador de excepciones. El código tiene un formato especial, y se parece a un selector de segmento; sin embargo, a diferencia de una bandera TI y un campo RPL, el código de error contiene 3 banderas (de evento externo, de localización de descriptor, y de GDT/LDT). El formato de este código es distinto para las excepciones de falta de páginas.
El mantenimiento de excepciones e interrupciones en el modo de 64 bits es muy parecido al descrito, excepto en esto: todos los administradores de interrupciones apuntadas por el IDT están en código de 64 bits; el tamaño de los pushes de interrupciones de pila se fijan en 64 bits; el puntero a pila actúa incondicionalmente sobre interrupciones; la nueva SS se pone a cero si hay cambios en el CPL; cambia el comportamiento de IRET; hay un nuevo mecanismo de cambio de pila de interrupciones; el alineamiento de los marcos de la pila de interrupciones es diferente.

No hay comentarios: