PROTECCIÓN



Ant: Interrupciones Arriba: Funcionamiento

En general, un programa puede alterar el normal funcionamiento del sistema de las siguientes formas:

Para evitar esto, es indispensable el apoyo del hardware, a través de los siguientes mecanismos.

Operación dual

Para asegurar una operación adecuada, se debe proteger al sistema operativo y todos los programas del malfuncionamiento de cualquier otro programa. Para eso, el hardware debe proveer al menos dos modos de operación.

El bit de modo indica el modo de operación actual. Cuando se enciende el computador y se carga el sistema operativo, se comienza en modo sistema. El sistema operativo siempre cambia a modo usuario antes de pasar el control a un proceso de usuario. Cuando ocurre una interrupción, el hardware siempre cambia a modo sistema. De esta menera, el sistema operativo siempre ejecuta en modo sistema. ¿Cual es la gracia? Que hay ciertas operaciones críticas que sólo pueden ser ejecutadas en modo sistema.

Protección de I/O

Para prevenir que un usuario ejecute instrucciones de I/O que puedan provocar daño, la solución es simple: las instrucciones de I/O sólo pueden ejecutarse en modo sistema. Así los usuarios no pueden ejecutar I/O directamente, sino que deben hacerlo a través del sistema, quien puede filtrar lo que sea del caso.

Para que este mecanismo de protección sea completo, hay que asegurarse que los programas de usuario no puedan obtener acceso a la CPU en modo sistema. Por ejemplo, un programa de usuario podría poner una dirección que apunte a una rutina propia en el vector de interrupciones. Así, cuando se produzca la interrupción, el hardware cambiaría a modo sistema, y pasaría el control a la rutina del usuario. O, también, el programa de usuario podría reescribir el servidor de la interrupción. Se requiere entonces...

Protección de memoria

No sólo hay que proteger el vector de interrupciones y las rutinas servidoras, sino que, en general, queremos proteger las áreas de memoria utilizadas por el sistema operativo y por otros programas. Esto lo vamos a ver con más detalle más adelante, pero una forma es usando dos registros: base y límite, que establecen el rango de direcciones de memoria que el programa puede accesar legalmente.

Para que esto funcione, hay que comparar cada dirección accesada por un programa en modo usuario con estos dos registros. Si la dirección está fuera del rango, se genera una interrupción que es atendida por el sistema operativo, quien aborta el programa (usualmente con un lacónico mensaje "segmentation fault"). Esta comparación puede parecer cara, pero teniendo presente que se hace en hardware, no lo es tanto. Obviamente, el programa usuario no debe poder modificar estos registros: las instrucciones que los modifican son instrucciones protegidas.

Protección de CPU

La único que falta, es asegurarse que el sistema operativo mantenga el control del buque, o sea, hay que prevenir, por ejemplo, que un programa entre en un ciclo infinito y nunca devuelva el control al sistema operativo. Esto se logra con un timer, tal como vimos en el ejemplo de multiprogramación.

Instrucciones privilegiadas

Entonces, ¿cómo hace I/O un programa de usuario, si sólo el sistema operativo puede hacerlo? El programa le pide al sistema operativo que lo haga por él. Tal solicitud se conoce como llamada al sistema, que en la mayoría de los sistemas operativos se hace por medio de una interrupción de software o trap a una ubicación específica del vector de interrupciones. Ejemplo hipotético, (parecido a MSDOS), para borrar un archivo:

  1. Poner en registro A el código de la operación: 41h = borrar archivo
  2. Poner puntero al nombre del archivo en registro B
  3. INT 21h (generar interrupción de software)

Como la llamada es a través de una interrupción, se hace lo de siempre: cambiar a modo protegido, y pasar control a la dirección en la posición 4*21h del vector de interrupciones, o sea, a la rutina que maneja las llamadas al sistema (o tal vez, la rutina que maneja las llamadas que tienen que ver con archivos; puede que otras clases de llamadas sean atendidas por medio de otras interrupciones). Dicha rutina examina el registro A para determinar qué operación debe ejecutar, y después determina el nombre del archivo a través del registro B. En seguida, decide si la operación es válida: puede que el archivo no exista o pertenezca a otro usuario; según eso, borra el archivo o "patalea" de alguna manera.

Cuando uno hace esto en un lenguaje de alto nivel, como C, simplemente escribe:

   char* Nombre = "Datos";
   remove(Nombre);

El compilador de C es quien se encarga de traducir esto a una llamada al sistema, ocultando el detalle de la interfaz con el sistema operativo.



Ant: Interrupciones Arriba: Funcionamiento