next up previous contents
Next: Arquitectura del Sistema Up: Estructura del Computador Previous: Estructura del Computador

Arquitectura Lógica del Computador

La arquitectura lógica del computador es la visión que tiene un programador de la máquina sobre la cual se pretende programar el núcleo de un sistema operativo. En esta visión no interesa si el computador tiene memoria caché o si el procesador usa pipeline para ejecutar las instrucciones de máquina, puesto que éstas son características que no influyen en el cómo se programa la máquina (salvo si se considera la eficiencia).

En cambio en la arquitectura lógica de un computador sí interesa cuál es su lenguaje de máquina, cómo se comanda/examinan los dispositivos, cómo se reciben las interrupciones, etc. Un programador del sistema operativo sí necesita conocer esta información para saber cómo programarlo.

Espacio de Direcciones Reales

El programador debe ser capaz de direccionar cada palabra en memoria real, cada puerta de dispositivo que posea el computador, cada pixel de la memoria de video y cada palabra de la memoria ROM para el bootstrap del sistema. El conjunto de direcciones que se pueden usar para estos fines se denomina el espacio de direcciones reales del computador (ver figura gif).

  
Figure: Espacio de direcciones de un computador.

Un programador comanda o examina un dispositivo escribiendo o leyendo en la dirección asignada a este dispositivo en el espacio de direcciones. Esto es lo que se denomina Entrada/Salida mapeada en la memoria (lo que se debe interpretar como el espacio de direcciones reales).

Estrategias de E/S

Busy-waiting

Cuando el programador necesita ser informado de la ocurrencia de un evento, entra en un ciclo de consulta constante del dispositivo, leyendo la puerta de control del dispositivo que le informa si tal evento ocurrió o no. En esta estrategia, este ciclo de consulta ocupa el 100% de tiempo de CPU y de ahí el nombre de busy-waiting.

A modo de ejemplo supongamos que tenemos un dispositivo de entrada que se lee en bloques de 512 bytes. El dispositivo tiene una puerta de control en la dirección 0xe0000 que indica si hay un byte para leer y una puerta de datos en la dirección 0xe0001 por donde se reciben los datos. Un programa lee un bloque llamando a la rutina ReadBlock suministrando la dirección de un área de 512 bytes en donde se debe dejar el resultado. La siguiente es una implementación de ReadBlock.

void ReadBlock(char *pbuff)
{
  char* puerta_control= (char*)0xe0000;
  char* puerta_datos=puerta_control+1;
  int nbytes=512;

  while (nbytes--)
  {
    /* Ciclo de busy-waiting */
    while (*puerta_control==0)
      /* todavia no llega un byte */ ;
    /* Ahora si llego un byte */
    /* Leemos el byte y lo colocamos
       en el area suministrada */
    *pbuff++= *puerta_datos;
} }

Interrupciones

En esta estrategia, se programa el dispositivo y el controlador de interrupciones para que produzcan una interrupción en el momento de ocurrir el evento esperado en este dispositivo. Luego, el sistema operativo puede desligarse completamente del dispositivo y dedicarse a otras actividades.

Cuando se produce la interrupción, el programa en ejecución se suspende y se ejecuta una rutina de atención encargada de realizar las acciones que requiere el dispositivo que interrumpe. Cuando esta rutina de atención retorna, se retoma el programa interrumpido.

A pesar que una interrupción es completamente transparente para el programa interrumpido (si la rutina de atención está bien programada) desde el punto de vista funcional, un usuario sí puede percibir una degradación en la rapidez de proceso si el número de interrupciones es muy elevado.

En este mecanismo cada byte o palabra transferidos entre el procesador y un dispositivo provoca una interrupción. Dado que un procesador de hoy en día no puede atender más de 100.000 interrupciones por segundo la velocidad de transferencia entre procesador y dispositivos usando interrupciones está limitada a no más de 100.000 bytes/palabras por segundo. Esta velocidad límite es inferior a la del mecanismo de busy-waiting.

El sistema operativo también puede inhibir las interrupciones por cortos períodos cuando necesita ejecutar trozos de código que no puede ser interrupidos. Para ello un bit en la palabra de estado del procesador indica si las interrupciones están inhibidas o no.

Localización de un dispositivo

Para ubicar el dispositivo que interrumpió existen las técnicas de polling y vector de interrupciones.

En la técnica de polling el sistema operativo no sabe cuál dispositivo interrumpió y por lo tanto tiene que consultar secuencialmente todos los dispositivos hasta dar con aquel o aquellos que interrumpen. Este mecanismo tiene asociado un tiempo prolongado de reacción a las interrupciones.

El segundo mecanismo se basa en que cada fuente de interrupción tiene asociado un índice. Este índice se usa para subindicar una tabla de rutinas de atención (ver figura gif). Por lo tanto cada fuente de interrupción tiene asociada una única rutina de atención. Esta tabla se denomina vector de interrupciones y se ubica en algún lugar de la memoria real del computador.

  
Figure: Vector de interrupciones.

También existen esquemas mixtos en donde varios dispositivos tienen asociado una misma posición en el vector de interrupciones (debido a que usan la misma línea de interrupción). En ese caso el sistema operativo debe recurrir a la técnica de polling para ubicar entre estos dispositivos cuál fue el que interrumpió.

Adaptemos el ejemplo anterior modificando ReadBlock de modo que la lectura del próximo bloque se realice en paralelo con el bloque que ya se leyó. Para esto después de terminar la lectura de un bloque habilitamos las interrupciones del dispositivo. Los bytes que se vayan recibiendo se acumulan en un área escondida ( pbuff2). Con suerte se reciben los 512 bytes de un bloque antes del próximo llamado a ReadBlock. De esta forma en la próxima llamada el ciclo de busy-waiting no se ejecutará y se copiará de inmediato el área escondida hacia el área del llamador.

/* Puertas del dispositivos */
char* puerta_control=(char*)0xe0000;
char* puerta_datos= puerta_control+1;

/* El buffer de bytes */
int nbytes=0;
char pbuff2[512];

void RutinaAtencionDispositivoX()
{
  pbuff2[nbytes++]= *puerta_datos;
  if (nbytes==512)
    InhibirIntDispositivoX();
}

void ReadBlock(char *pbuff)
{
  if (nbytes==0)
    HabilitarIntDispositivoX();
  while (nbytes<512)
    /* busy-waiting */;
  bcopy(pbuff,pbuff2,nbytes);
  nbytes=0;
  HabilitarIntDispositivoX();
}

Canales de E/S

Cuando se requiere transferir grandes bloques de datos entre memoria y dispositivos existen coprocesadores especializados para estos fines llamados canales de E/S o canales DMA (de Direct Memory Access).

Para iniciar este tipo de transferencias el sistema operativo indica a algún canal --a través de puertas en el espacio de direcciones-- la dirección de memoria en donde comienza el bloque de datos, el taman o del bloque, el sentido de la transferencia (memoria a dispositivo o viceversa) y la línea por la cual el dispositivo anunciará que hay un byte/palabra para transferir. Luego se comanda al canal para que inicie la transferencia.

De esta forma, el sistema operativo puede desligarse completamente de la transferencia, la que se realiza con mínima degradación del procesador al ejecutar los programas. Al terminar la transferencia, el canal interrumpe al sistema operativo para que determine las acciones a seguir.

La velocidad de transferencia entre memoria y dispositivos está limitada únicamente por la velocidad del bus que los separa (típicamente de 3 a 100 MBytes por segundo).

El siguiente código es una implementación de ReadBlock que usa la técnica de buffering (vista en la introducción).

/* Puertas del dispositivo */
char* puerta_control=(char*)0xe0000;
char* puerta_datos= puerta_control+1;

/* Puertas del canal */
char**puerta_canalX_inicio= ...;
int*  puerta_canalX_nbytes= ...;
int*  puerta_canalX_disp= ...;
char* puerta_canalX_control= ...;

/* El buffer de bytes */
int  leido= FALSE;
int  programado= FALSE;
char pbuff2[512];

int RutinaAtencionCanalX()
{
  leido= TRUE;
}

void ReadBlock(char *pbuff)
{
  if (!programado)
    ProgramarCanalX();

  while (!leido)
    /* busy-waiting */ ;
  bcopy(pbuff, pbuff2, 512);
  leido= FALSE;
  ProgramarCanalX();
}

void ProgramarCanalX()
{
  *puerta_CanalX_inicio= pbuff2;
  *puerta_CanalX_nbytes= 512;
  *puerta_CanalX_disp=
     <nro. linea DRQ del dispositivo>;
  *puerta_CanalX_control= LEER;
  HabilitarIntCanalX();
  programado= TRUE;
}

Observe que la rutina de atención atiende las interrupciones del canal y no las del dispositivo. Del mismo modo, son las interrupciones del canal las que se habilitan, no las del dispositivo. Esto se hace para que efectivamente se reciba una sola interrupción al final del bloque y no una interrupción por cada byte transferido.

Modo Dual

Un programa que corre en un sistema mono-usuario como un PC/DOS puede leer o escribir cualquier dirección en la memoria. Por lo tando puede alterar el código de DOS, modificar el vector de interrupciones o la memoria de video y comandar cualquiera de los dispositivos.

En cambio en un sistema multiprogramado un programa de un usuario no puede influir en el programa de otro usuario. Para implementar esto todos los micro-procesadores modernos (Motorola 68000 e Intel 286 en adelante) incorporan un modo dual:

El núcleo de un sistema operativo siempre corre en el modo sistema mientras que los programas del usuario corren en modo usuario. Si un programa del usuario intenta ejecutar una de estas instrucciones se produce una interrupción ( trap) que causa la invocación de una rutina de atención provista por el núcleo. Esta rutina de atención se ejecuta en modo sistema y tiene el poder como para abortar el programa que intentó ejecutar la instrucción inhibida.

El registro de estado del procesador indica el modo en que se trabaja (ver figura gif).

  
Figure: Palabra de estado del procesador

Ejemplos de instrucciones privilegiadas son la instrucción que inhibe las interrupciones y la instrucción que cambia el registro de estado.

Observe que este mecanismo no es suficiente para prohibir el acceso a los dispositivos puesto que estos se comandan con las mismas instrucciones de lectura y escritura en memoria.

Espacio de Direcciones Virtuales

Cada programa del usuario corre en su propio espacio de direcciones virtuales independiente. Es el núcleo --con ayuda del Hardware-- el que se encarga de emular varios espacios de direcciones en un solo espacio de direcciones real del procesador.

Esto significa que cada vez que un programa especifica una dirección en donde leer o escribir una palabra, esa dirección está referida al espacio de direcciones virtuales de ese programa. Esa dirección no corresponde a la dirección real que ocupa la palabra en la memoria real del computador. Además la palabra de dirección virtual 100 de un programa en ejecución tiene una dirección real distinta de la palabra de dirección virtual 100 en otro programa.

El Hardware del computador se encarga de traducir eficientemente las direcciones virtuales a direcciones reales en todo acceso a la memoria o dispositivos. Para ello, el núcleo le suministra los parámetros sobre cómo realizar esa traducción.

  
Figure: Espacio de direcciones virtuales de un programa en Unix

El espacio de direcciones típico de un programa en Unix es el que se muestra en la figura gif. Las características de este espacio son:

De esta forma los programas del usuario no pueden ver los dispositivos, el vector de interrupciones, o las estructuras de datos que maneje internamente el núcleo. Este es el mecanismo de protección más importante que debe poseer un sistema operativo.

Cronómetro regresivo

El hardware del computador debe incluir un cronómetro regresivo o timer. Éste es un dispositivo que el núcleo puede comandar para que invoque una interrupción después de transcurrido un cierto tiempo. De esta forma el núcleo cuenta con un mecanismo para entregar el procesador a una aplicación por un tiempo acotado.



next up previous contents
Next: Arquitectura del Sistema Up: Estructura del Computador Previous: Estructura del Computador



José M. Piquer
Fri Apr 9 15:57:37 CLT 1999