¿Por qué el código / hilo del núcleo que se ejecuta en el contexto de interrupción no puede dormir?

StackOverflow https://stackoverflow.com/questions/1053572

  •  20-08-2019
  •  | 
  •  

Pregunta

Estoy leyendo el siguiente artículo de Robert Love

http://www.linuxjournal.com/article/6916

que dice

" ... Analicemos el hecho de que las colas de trabajo se ejecutan en el contexto del proceso. Esto contrasta con los otros mecanismos de la mitad inferior, que se ejecutan en un contexto de interrupción. El código que se ejecuta en el contexto de interrupción no puede dormir o bloquearse, porque el contexto de interrupción no tiene un proceso de respaldo con el que reprogramar. Por lo tanto, debido a que los manejadores de interrupciones no están asociados con un proceso, no hay nada que el programador pueda suspender y, lo que es más importante, nada que el programador pueda despertar ... & Quot;

No lo entiendo. AFAIK, el planificador en el núcleo es O (1), que se implementa a través del mapa de bits. Entonces, ¿qué impide que el scehduler suspenda el contexto de interrupción y tome el siguiente proceso programable y le pase el control?

¿Fue útil?

Solución

Creo que es una idea de diseño.

Claro, puede diseñar un sistema en el que pueda dormir interrumpido, pero excepto para hacer que el sistema sea difícil de comprender y complicado (hay muchas situaciones que debe tener en cuenta), eso no ayuda en nada. Entonces, desde una vista de diseño, declarar que el controlador de interrupciones como no puede dormir es muy claro y fácil de implementar.


De Robert Love (un hacker de kernel): http://permalink.gmane.org/gmane.linux.kernel.kernelnewbies/1791

No puede dormir en un controlador de interrupciones porque las interrupciones no tienen un contexto de proceso de respaldo y, por lo tanto, no hay nada que reprogramar dentro. En otras palabras, los manejadores de interrupciones no están asociados con una tarea, así que no hay nada que & "; poner a dormir &"; y (más importante) " nada que despertar " ;. Deben correr atómicamente.

Esto no es diferente a otros sistemas operativos. En la mayoría de los sistemas operativos, las interrupciones no están enhebradas. Sin embargo, las mitades inferiores a menudo lo son.

La razón por la que el manejador de fallos de página puede dormir es porque solo se invoca por código que se ejecuta en el contexto del proceso. Porque el núcleo es propio la memoria no es pagable, solo los accesos a la memoria del espacio del usuario pueden generar error de página Por lo tanto, solo unos pocos lugares determinados (como llamadas a copy_ {to, from} _user ()) puede causar un error de página dentro del kernel. Aquellos todos los lugares deben estar hechos por un código que pueda dormir (es decir, contexto del proceso, sin cerraduras, etcétera).

Otros consejos

  

Entonces, ¿qué impide que el scehduler suspenda el contexto de interrupción y tome el siguiente proceso programable y le pase el control?

El problema es que el contexto de interrupción no es un proceso y, por lo tanto, no se puede suspender.

Cuando se produce una interrupción, el procesador guarda los registros en la pila y salta al inicio de la rutina de servicio de interrupción. Esto significa que cuando el controlador de interrupciones se está ejecutando, se está ejecutando en el contexto del proceso que se estaba ejecutando cuando se produjo la interrupción. La interrupción se está ejecutando en la pila de ese proceso, y cuando el controlador de interrupción se complete, ese proceso continuará ejecutándose.

Si intentas dormir o bloquear dentro de un controlador de interrupciones, terminarías no solo deteniendo el controlador de interrupciones, sino también el proceso que interrumpió. Esto podría ser peligroso, ya que el manejador de interrupciones no tiene forma de saber qué estaba haciendo el proceso interrumpido, o incluso si es seguro suspenderlo.

Un escenario simple donde las cosas podrían salir mal sería un punto muerto entre el manejador de interrupciones y el proceso que interrumpe.

  1. Proceso1 ingresa al modo kernel.
  2. Proceso1 adquiere LockA .
  3. Se produce una interrupción.
  4. ISR comienza a ejecutarse utilizando la pila de Proceso1 .
  5. ISR intenta adquirir LockA .
  6. ISR llama a dormir para esperar a que se libere LockA .

En este punto, tienes un punto muerto. Proceso1 no puede reanudar la ejecución hasta que el ISR haya terminado con su pila. Pero el ISR está bloqueado esperando que Proceso1 libere LockA .

Porque la infraestructura de conmutación de subprocesos es inutilizable en ese punto. Al dar servicio a una interrupción, solo se pueden ejecutar cosas de mayor prioridad: consulte Manual del desarrollador de software Intel sobre interrupción, tarea y prioridad del procesador . Si permitiste que se ejecutara otro hilo (lo que implicas en tu pregunta que sería fácil de hacer), no podrías dejar que haga nada; si causara un error en la página, tendrías que usar los servicios en el núcleo que no se pueden usar mientras se repara la interrupción (vea a continuación por qué).

Por lo general, su único objetivo en una rutina de interrupción es lograr que el dispositivo deje de interrumpir y ponga en cola algo en un nivel de interrupción más bajo (en Unix, generalmente es un nivel sin interrupción, pero para Windows, es despacho, apc o pasivo nivel) para hacer el trabajo pesado donde tiene acceso a más funciones del kernel / os. Consulte: Implementación de un controlador .

Es una propiedad de cómo deben funcionar los sistemas operativos, no es algo inherente a Linux. Una rutina de interrupción puede ejecutarse en cualquier punto, por lo que el estado de lo que interrumpió es inconsistente. Si interrumpió el código de programación de subprocesos, su estado es inconsistente, por lo que no puede estar seguro de que puede & Quot; sleep & Quot; y cambiar hilos. Incluso si protege el código de cambio de subproceso para que no se interrumpa, el cambio de subproceso es una característica de muy alto nivel de la O / S y si protege todo lo que se basa, una interrupción se convierte en una sugerencia más que el imperativo implícito en su nombre.

  

Entonces, ¿qué impide que el scehduler suspenda el contexto de interrupción y tome el siguiente proceso programable y le pase el control?

La programación ocurre en las interrupciones del temporizador. La regla básica es que solo se puede abrir una interrupción a la vez, por lo que si se va a dormir en & "; Obtendrá datos del dispositivo X &"; interrupción, la interrupción del temporizador no puede ejecutarse para programarla.

Las interrupciones también ocurren muchas veces y se superponen. Si coloca & Quot; obtuvo datos & Quot; interrumpir para dormir, y luego obtener más datos, ¿qué sucede? Es lo suficientemente confuso (y frágil) que la regla general es: no dormir en las interrupciones. Lo harás mal.

Incluso si pudieras poner un ISR a dormir, no querrías hacerlo. Desea que sus ISR sean lo más rápidos posible para reducir el riesgo de perder interrupciones posteriores.

No permitir que un manejador de interrupciones bloquee es una opción de diseño. Cuando hay algunos datos en el dispositivo, el controlador de interrupciones intercepta el proceso actual, prepara la transferencia de datos y habilita la interrupción; antes de que el controlador habilite la interrupción actual, el dispositivo debe colgarse. Queremos mantener nuestra E / S ocupada y nuestro sistema receptivo, entonces es mejor que no bloqueemos el controlador de interrupciones.

No creo que & "; estados inestables &"; Son una razón esencial. Los procesos, sin importar si están en modo de usuario o en modo de núcleo, deben ser conscientes de que pueden ser interrumpidos por interrupciones. Si tanto el manejador de interrupciones como el proceso actual acceden a una estructura de datos en modo kernel, y existe una condición de carrera, entonces el proceso actual debería deshabilitar las interrupciones locales y, además, para arquitecturas de multiprocesador, los bloqueos giratorios deberían usarse durante las secciones críticas .

Tampoco creo que si el manejador de interrupciones estuviera bloqueado, no se puede activar. Cuando decimos & Quot; block & Quot ;, básicamente significa que el proceso bloqueado está esperando algún evento / recurso, por lo que se vincula a una cola de espera para ese evento / recurso. Cada vez que se libera el recurso, el proceso de liberación es responsable de despertar los procesos de espera.

Sin embargo, lo realmente molesto es que el proceso bloqueado no puede hacer nada durante el tiempo de bloqueo; no hizo nada malo para este castigo, que es injusto. Y nadie seguramente podría predecir el tiempo de bloqueo, por lo que el proceso inocente tiene que esperar por razones poco claras y por tiempo ilimitado.

Por naturaleza, la pregunta es si en el controlador de interrupciones puede obtener un " current " (dirección a la estructura de tareas del proceso actual), en caso afirmativo, es posible modificar el contenido allí en consecuencia para convertirlo en " sleep " estado, que el planificador puede volver más tarde si el estado se cambia de alguna manera. La respuesta puede depender del hardware.

Pero en ARM, es imposible ya que 'actual' es irrelevante para procesar en modo de interrupción. Vea el código a continuación:

#linux/arch/arm/include/asm/thread_info.h 
94 static inline struct thread_info *current_thread_info(void)
95 {
96  register unsigned long sp asm ("sp");
97  return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
98 }

sp en modo USER y modo SVC son " same " (" same " aquí no significa que son iguales, en cambio, el modo sp del usuario apunta a la pila de espacio del usuario, mientras que sp r13_svc del modo svc apunta a la pila del núcleo, donde la estructura de tareas del proceso del usuario se actualizó en cambio de tarea anterior, cuando se produce una llamada al sistema, el proceso ingresa nuevamente al espacio del kernel, cuando el sp (sp_svc) todavía no se modifica, estos 2 sp están asociados entre sí, en este sentido, son 'iguales'), entonces bajo el modo SVC, el código del núcleo puede obtener el "actual" válido. Pero bajo otros modos privilegiados, digamos modo de interrupción, sp es 'diferente', apunta a una dirección dedicada definida en cpu_init (). La 'corriente' calculada en este modo será irrelevante para el proceso interrumpido, acceder a él dará lugar a comportamientos inesperados. Es por eso que siempre se dice que la llamada al sistema puede dormir pero que el controlador de interrupción no, la llamada al sistema funciona en el contexto del proceso pero no interrumpe.

Los manejadores de interrupciones de alto nivel enmascaran las operaciones de todas las interrupciones de menor prioridad, incluidas las de la interrupción del temporizador del sistema. En consecuencia, el controlador de interrupciones debe evitar involucrarse en una actividad que pueda hacer que se duerma. Si el manejador duerme, entonces el sistema puede bloquearse porque el temporizador está enmascarado e incapaz de programar el hilo de dormir. ¿Tiene sentido esto?

Si una rutina de interrupción de nivel superior llega al punto en que lo siguiente que debe hacer tiene que suceder después de un período de tiempo, entonces debe colocar una solicitud en la cola del temporizador, solicitando que se ejecute otra rutina de interrupción ( en un nivel de prioridad inferior) algún tiempo después.

Cuando se ejecuta esa rutina de interrupción, elevaría el nivel de prioridad al nivel de la rutina de interrupción original y continuaría la ejecución. Esto tiene el mismo efecto que un sueño.

El kernel de Linux tiene dos formas de asignar la pila de interrupciones. Uno está en la pila del núcleo del proceso interrumpido, el otro es una pila de interrupción dedicada por CPU. Si el contexto de interrupción se guarda en la pila de interrupción dedicada por CPU, entonces el contexto de interrupción no está completamente asociado con ningún proceso. & Quot; current & Quot; la macro producirá un puntero no válido al proceso actual en ejecución, ya que " current " Las macros con alguna arquitectura se calculan con el puntero de la pila. El puntero de la pila en el contexto de interrupción puede apuntar a la pila de interrupción dedicada, no a la pila del núcleo de algún proceso.

Es solo una elección de diseño / implementación en el sistema operativo Linux. La ventaja de este diseño es simple, pero puede no ser buena para los requisitos del sistema operativo en tiempo real.

Otros sistemas operativos tienen otros diseños / implementaciones.

Por ejemplo, en Solaris, las interrupciones pueden tener diferentes prioridades, lo que permite que la mayoría de las interrupciones de los dispositivos se invoquen en subprocesos de interrupción. Los hilos de interrupción permiten dormir porque cada uno de los hilos de interrupción tiene una pila separada en el contexto del hilo. El diseño de hilos de interrupción es bueno para hilos en tiempo real que deberían tener prioridades más altas que las interrupciones.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top