next up previous contents
Next: Envío de mensajes Up: Los portales de Off Previous: Shuttles y portales

Upcalls

La parte más interesante del envío mediante portales es la realización de upcalls [42]. Consisten éstas en llamadas del núcleo a las aplicaciones, o intuitivamente, la inversa de una llamada al sistema. Este mecanismo primario se emplea no sólo para enviar excepciones e interrupciones a las aplicaciones sino también para implementar los envíos de mensajes a portales tal y como se muestra en la figura 3.18. En dicha figura podemos ver a rasgos generales cómo procede el envío de interrupciones, excepciones, mensajes y PCTs emplando upcalls en algunos casos.

  figure4317
Figure 3.18: Uso de upcalls como base para eventos e invocaciones de portales.

En la figura 3.18-a vemos como un upcall es sencillamente una ``llamada al sistema a la inversa''. En 3.18-b vemos que la notificación de eventos síncronos, con respecto a la ejecución de instrucciones de la aplicación que los sufre, es simplemente un upcall originado por la ocurrencia de un suceso (naturalmente, éste upcall lo causa en envío de un mensaje a un portal). En 3.18-c tenemos un envío de mensaje asíncrono (que podría originarse dentro del tex2html_wrap_inline8127kernel ante la ocurrencia de una interrupción--como en el paso 1 de la figura), la entrega del mismo se efectúa de forma asíncrona mediante un upcall, como veremos en el apartado siguiente. Por último, en 3.18-d vemos una transferencia protegida de control cuyo funcionamiento detallaremos en el apartado 3.4.2.

Como cabe suponer, una llamada al sistema puede dar lugar a un upcall, ésta a una nueva llamada al sistema, etc.

Para implementar upcalls (ver figura 3.19) aprovechamos el mecanismo (suministrado por el hardware) de traps. Para realizar la primera parte de un upcall (la invocación de la rutina de usuario a la que se desea llamar) construimos en la pila del kernel una estructura similar a la que salva el procesador cuando se produce una llamada al sistema. Dado que el hardware emplea esta estructura para recuperar, en el retorno de una llamada al sistema, el estado del usuario antes de que se produjese esta llamada, ejecutamos una instrucción iret (como si de un retorno se tratase). El procesador toma de la pila del kernel el ``falso'' estado que hemos dejado preparado y lo recupera. Por supuesto, antes de invocar a la rutina salvamos el estado del procesador para poder recuperarlo cuando se retorne del upcall, de tal modo que el tex2html_wrap_inline8127kernel siga ejecutando la instrucción posterior a la llamada a rutina de usuario.

  figure4332
Figure 3.19: Funcionamiento de un upcall: llamada.

Con este simple mecanismo conseguimos ``saltar'' a la rutina que deseamos llamar en área de usuario (denominada ``func. usr.'' en la figura). Paradójicamente, mediante una instrucción de retorno y no mediante una de llamada.

La parte más complicada es retornar de nuevo al núcleo. Para conseguirlo (ver figura 3.20) hemos de conseguir que se produzca una llamada al sistema justo tras el retorno de la rutina de usuario que hemos invocado. Esto se consigue alterando la dirección de retorno de la rutina invocada, en la pila del usuario (``ret. falso'', en la figura), antes de invocarla. Esta dirección de retorno (que determinará la dirección en la que se seguirá ejecutando instrucciones cuando la rutina termine) se hace apuntar a una instrucción (almacenada directamente por el tex2html_wrap_inline8127kernel en la pila del usuario) que produce la llamada al sistema deseada.

  figure4341
Figure 3.20: Funcionamiento de un upcall: retorno.

Esta llamada al sistema (sys_prtl_ret), toma el estado del núcleo que salvaguardamos anteriormente y lo restaura. Como efecto lateral de la restauración, la pila del kernel vuelve a su posición original (antes de efectuar el upcall). El upcall se ha completado en este punto y, a partir de ese momento, se continúa con la llamada al sistema que originó el upcall. Cuando ésta retorne encontrará el estado de usuario correspondiente a dicha llamada al sistema y el siguiente iret (que finaliza la llamada al sistema) restaurará el estado del shuttle para que este continúe justo después de su llamada al sistema.

Hay un detalle que es preciso considerar en este punto. Como es natural, el servidor de Shuttles ofrece puntos de entrada para que sea posible obtener (o modificar) el contenido de los registros (de procesador) de un shuttle cualquieragif, siempre que se presenten los correspondientes derechos de acceso. La pregunta que surge es ¿De qué registros estamos hablando? Las llamadas al sistema pueden dar lugar a llamadas a rutinas de usuario y éstas de nuevo a llamadas al sistema con lo que puede haber varias llamadas al sistema en curso (anidadas) en un mismo shuttle. ¿A qué ``registros'' nos referimos pues?, ¿A los correspondientes a la primera llamada al sistema o a la más externa? ¿A los correspondientes a la última o más interna?. Es decir, dado que pueden existir múltiples estados de ejecución anidados unos dentro de otros (como sucede con los estados de la ejecución de un programa cuando tenemos llamadas anidadas a funciones), puede existir más de un juego de registros (tal y como los ve el usuario) para un mismo shuttle. Naturalmente, sólo uno de estos juegos corresponderá al estado actual del shuttle.

En realidad, dado que las llamadas anidadas no corresponden sólo a upcalls, sino también a llamadas al sistema, tenemos una sucesión de juegos de registros correspondientes al estado del usuario y otra sucesión correspondiente al estado del núcleo. Los primeros corresponden a los instantes en que se produjo una llamada al sistema (y se salvaguardó el estado del usuario) y los segundos a los instantes en que se produjo un upcall (y se salvaguardó el estado del núcleo).

En la implementación actual sólo es posible acceder al juego de registros más externo (el más reciente, o más superficial en la pila) de cada shuttle. Más concretamente, es posible acceder al juego más externo correspondiente a modo usuario y al juego más externo correspondiente a modo kernel.

Para conseguir esto, los contextos salvaguardados se enlazan entre sí en dos listas, una para contextos de usuario y otra para contextos de kernel. El mantenimiento de éstas listas se reduce a unas pocas operaciones en la entrada al núcleo y otras pocas en la salida del mismo. La existencia de éstas listas permite que con escasas modificaciones al código actual sea factible acceder a cualquier contexto de usuario o núcleo de un shuttle dado, no sólo al más externo como en la actualidad.

Una alternativa a la implementación efectuada habría sido una implementación tradicional en la que no es necesario mantener éstas listas. Consistiría en implementar un modelo similar al de los procesos UNIX, donde el núcleo, que ejecuta en el contexto de un determinado proceso, no realiza tareas sólo para el proceso en cuyo contexto ejecuta, sino que también realiza tareas en favor de otros procesos.

Nosotros en cambio hemos tratado de compartimentar la ejecución del núcleo de modo que éste trabaje sólo en favor de aquella aplicación en cuyo contexto ejecuta. Esto es, un shuttle que ejecuta código de usuario, ejecuta periódicamente código del núcleo en modo núcleo para realizar actividades (de núcleo) en favor de sí mismo. O dicho de otro modo, cada shuttle se hace su propio trabajo esté en área de usuario o en área de núcleo. Con el modelo implementado es factible expulsar al núcleo de forma indefinida de tal modo que sólo se perjudique al shuttle en que éste ejecutaba. Con el modelo implementado, el núcleo es una entidad pasiva en la que cada shuttle entra periódicamente mediante PCTsgif.

La simplicidad de las estructuras de datos mantenidas por núcleo (téngase en cuenta que la practica totalidad de ellas no requieren de memoria dinámica) junto con el modelo de un núcleo pasivo en el que se mantiene en la medida de lo posible la independencia entre shuttles (cada ejecución en el núcleo actúa en un shuttle y en favor de dicho shuttle) permite que el fallo de un shuttle dentro del núcleo no afecte inmediatamente al resto del sistema. Esto ha resultado ser muy conveniente para el desarrollo y depuración del tex2html_wrap_inline8127kernel, tanto que no ha sido preciso emplear depurador alguno. La independencia entre shuttles facilita así mismo la movilidad de los recursos del sistema, dado que puede obviarse el hecho de que un shuttle esté ejecutando en modo usuario o kernel a la hora de capturar su estado.


next up previous contents
Next: Envío de mensajes Up: Los portales de Off Previous: Shuttles y portales

Francisco J. Ballesteros
Fri Dec 19 17:18:03 MET 1997