Minikernel - Escuela de Ingeniería Civil en Informática

Anuncio
Minikernel
Material complementario para la asignatura de Sistemas Operativos
Preparado por Gabriel Astudillo Muñoz
Escuela de Ingeniería Civil Informática
Universidad de Valparaíso
1
Resumen
Este documento tiene como objetivo describir el entorno de desarrollo que se utilizará para las tareas
prácticas de la asignatura de Teoría de Sistemas Operativos, relativas a gestión de procesos.
2
Introducción
El software que se utiliza para el tema de Gestión de Procesos, es un kernel, denominado “minikernel”,
que es utilizado en el libro “Prácticas de Sistemas Operativos: de la base al Diseño” [1] y que su diseño se
basa en una estructura de Hardware Virtual [2]. Desde este punto de vista, consiste en una virtualización a
nivel de sistema operativo.
Debido a lo anterior, el minikernel está organizado en dos capas: una que implementa el hardware virtual,
denominal HAL (Hardware Abstraction Layer)1, y otra que implementa el sistema operativo propiamente
tal.
3
Entorno de desarrollo
En esta sección, se describen los principales módulos que componen el minikernel. En forma concreta,
posee tres componentes principales, cada una de ellas representada por un directorio, cuya descripción
está en la Tabla 3.1.
Directorio
Raíz
Directorio
boot/
minikernel/
kernel/
usuario/
Descripción
Cargador del
sistema
Entorno
relacionado
con el kernel
Programas
que generan
los procesos
de la capa de
usuario
Archivos
boot
HAL.o
kernel.c
include/
lib/
init.c
simple.c
excep_arit.c
excep_mem.c
Descripción
Programa cargador del sistema
operativo
Módulo que representa el
Hardware Virtual
Sistema Operativo
Cabeceras utilizas por el kernel
Bibliotecas utilizas por los
programas de usuario
Proceso inicial del sistema.
Procesos de ejemplo
Tabla 3.1
Debido a que el objetivo de la utilización de este entorno es poner en práctica los conocimientos de
gestión de procesos, no se incluyen los códigos fuentes del cargador (boot) ni de la capa de abstracción
de hardware (HAL.o).
3.1
Módulo HAL.o
Este módulo implementa mediante software los componentes de hardware que tiene un computador, tales
como el procesador, memoria, reloj temporizador, pantalla y teclado. En el caso de los dos últimos, el
sistema utliza la pantalla y el teclado del computador real. Además, implementa interrupciones y nivel de
interrupciones y ofrece una API que permite acceder a los recursos implementados.
1
La capa HAL es parte del diseño de los sistemas operativos Unix y similares. Actualmente, HAL ha sido
reemplazo por la capa udev.
1
3.1.1
3.1.1.1
Implementación de componentes de hardware Procesador El procesador tiene dos modos de operación: modo privilegiado y modo usuario [2]. En el primero, se
ejecuta código propio del sistema operativo mientras que en segundo permite la ejecución de los códigos
de los procesos de usuario. El cambio de modo de usuario a privilegiado ocurre sólo cuando exite algún
tipo de interrupción.
Existe un registro de estado del procesador, que almacena el modo de ejecución del mismo. Para pasar
de modo privilegerado a modo usuario, el sistema operativo debe modificar ese valor dentro de éste
registro.
Por último, dispone de seis registros de propósito general de 32 bits. Se utilizan para el paso de
parámetros en las llamadas al sistema. La cantidad de registros está definido en la macro NREGS, en el
arhivo de cabecera del kernel HAL.h.
3.1.1.2
Dispositivos de I/O El sistema tiene implementado dos dispositivos de I/O basados en interrupciones: el terminal y el reloj.
El terminal es el tipo mapeado en memoria y está formado por dos dispositivos independientes: pantalla
y teclado. La salida de datos a la pantalla se realiza escribiendo directamente en su memoria de video y
no se utilizan interrupciones. La entrada de datos desde el teclado está comandada por interrupciones que
se producen cada vez que se pulsa una tecla.
El reloj temporizador tiene una frecuencia de interrupción programable. Esto significa que este
temporizador interrumpe periódicamente y su período es modificable.
3.1.1.3
Interrupciones El tratamiento de interrupciones se realiza mediante el uso de una tabla de vectores de interrupción.
Existen seis vectores disponibles, los que se muestran en la Tabla 3.2.
Vector
0
1
2
3
4
5
Descripción
Excepción
Aritmética
Excepción
por
acceso no válido a
memoria
Interrupción de reloj
Interrupción
del
terminal
Llamada al sistema
Interrupción
por
Software
Modo de manejo
Con excepciones
Valores Nemotécnicos
EXC_ARITM
EXC_MEM
Interrupciones de
I/O
INT_RELOJ
INT_TERMINAL
Interrupciones de
software
LLAM_SIS
INT_SW
Tabla 3.2 Vectores disponibles. Los valores nemotécnicos están definidos en la cabecera const.h.
El procesador tiene múltiples niveles de interrupción, los que tienen asignado un número. Mientras mayor
sea, su prioridad es más alta. Los niveles disponibles se muestran en la Tabla 3.3.
Nivel
Descripción
3
2
1
Interrupción de Reloj
Interrupción de terminal
Interrupción de software o
llamada al sistema
Ejecución en modo usuario
0
Valores
Nemotécnicos
NIVEL_3
NIVEL_2
NIVEL_1
N/A
Tabla 3.3 Niveles de interrupción. Los valores nemotécnicos están definidos en la cabecera const.h.
En cada momento el procesador ejecuta código en un determinado nivel de interrupción, cuyo valor está
almacenado en el registro de estado. Cabe destacar que sólo admite interrupciones de una nivel superior
al actual.
2
Inicialmente, el procesador ejecuta código en modo privilegiado y en el nivel de interrupción máximo,
por lo que todas las interrupciones están inhibidas. Por otro lado, cuando el procesador ejecuta código en
modo usuario, lo hace en un nivel 0, lo que significa que todas las interrupciones están habilitadas.
3.1.2
3.1.2.1
API ofrecidas al sistema operativo. Iniciación de los controladores de dispositivos void iniciar_cont_teclado();
Inicia el controlador del teclado.
void iniciar_con_reloj(int ticks_por_segundo);
Permite iniciar el controlador de reloj. El parámetro ticks_por_segundo es la
frecuencia de interrupción deseada, medido en cantidad de interrupcione por segundo.
unsigned long long int leer_reloj_CMOS();
Permite leer la hora almacenada en el reloj del sistema real.
3.1.2.2
Manejo de interrupciones void iniciar_cont_int();
Permite iniciar el controlador de interrupciones.
void instal_man_int(int nvector, void (*manej)() );
Permite que la función manjec() sea la que maneje la intrrupción correspondiente a
nvector (ver tabla 3.2).
int fijar_nivel_int(int nivel)
Fija el nivel de interrupción del procesador devolviendo el nivel previo. Los niveles están
descritos en la 3.3.
void activar_int_SW()
Activa las interrupciones por software
int viene_de_modo_usuario()
Consulta el registro de estado salvado por la interrupción actual y permite conocer si
previamente se estaba ejecutando en modo usuario, devolviendo un valor 1 en tal caso.
3.1.2.3
Gestión de la información del contexto de los procesos El contexto de un proceso se representa por una estructura denominada contexto_t, la que almacena
una copia de los registros del procesador con los valores correspondientes a la última vez que ejecutó el
proceso. La estructura se muestra en la Tabla 3.4.
/* Contexto "hardware" de un proceso */
typedef struct {
ucontext_t ctxt;
long registros[NREGS];
} contexto_t;
Tabla 3.4 Estructura que permite almacenar el contexto de un proceso. (Archivo HAL.h)
Las funciones que crean el contexto inicial de un proceso nuevo, así como las que permiten realizar un
cambio de contexto [2] son:
void fijar_contexto_ini(void *memoria, void *inicia_pila,
tam_pila, void *pc_inicial, contexto_t *contexto_ini);
int
Crea el contexto inicial. Establece los valores iniciales del contador de programa
(pc_inicial), puntero de la pila (inicia_pila) y su tamaño (tam_pila). Como
parámetro de entrada, debe recibir una referencia al mapa de memoria del proceso
(memoria), creado con anterioridad. Como salida, retorna un contexto iniciado
(contexto_ini) de acuerdo con los valores especificados.
3
void cambio_context(contexto_t
*contexto_a_restaurar);
*contexto_a_salvar,
contexto_t
Básicamente, esta función guarda el contexto de un proceso (contexto_a_salvar) y
restaura el de otro (contexto_a_restaurar).
3.1.2.4
Gestión de memoria de los procesos void *crear_imagen(char *prog, void **dir_ini);
Crea el mapa de memoria a partir del ejecutable especificado por prog. Para esto, debe
procesar el archivo ejecutable y crear las regiones de memoria correspondientes. Devuelve
un identificador del mapa de memoria creado, así como la dirección de inicio del programa
en el parámetro de salida dir.
void liberar_imagen(void *mem);
Libera una imagen de memoria previamente creada, representada por el parámetro mem.
void *crear_pila(int tam);
Reserva una zona de memoria para la región de pila (stack). Se especifica el tamaño de la
misma (parámetro tam). Devuelve la dirección inicial de la zona reservada.
void liberar_pila(void *pila);
Reserva una zona de memoria para la región de pila (stack). Se especifica el tamaño de la
misma (parámetro tam). Devuelve la dirección inicial de la zona reservada.
3.1.2.5
Funciones varias int leer_registro(int nreg);
int escribir_registro(int nreg, int valor);
Permiten leer y escribir en los registros de propósito general del prosesador.
char leer_puerto(int dir_puerto);
Lee y retorna un byte del puerto de I/O especificado por dir_puerto. El sistema posee un
solo puerto definido en la macro DIR_TERMINAL, que tiene el valor 1.
void halt();
Detiene el procesador hasta que se active una interrupción
void escribir_ker(char *buffer, unsigned int longi)
Perminete escribir en pantalla los datos especificados en el parámetro buffer y cuyo tamaño
es longi. Para ello, copia en la memoria de video del terminal dichos datos. El código es:
/* Funcion interna que escribe en la pantalla */
void escribir_ker(char *texto, unsigned int longi)
{
int nivel_previo;
nivel_previo=fijar_nivel_int(NIVEL_3);
write(1, texto, longi);
fijar_nivel_int(nivel_previo);
}
Tabla 3.5
int printk(const char *, ...)
Función que llama internamente a escribir_ker(). Funciona como la función
printf().
void panico(char *mens)
Escribe el mensaje especificado por el parámetro mens por pantalla y termina la ejecución
del sistema operativo.
4
3.2
Módulo Kernel
3.3
Carga del sistema operativo
En el directorio raíz, se debe ejecutar la intrucción:
./boot/boot minikernel/kernel
3.3.1
Inicio del sistema operativo Una vez cargado en memoria el sistema operativo, el programa cargador pasa el control al punto de
entrada del kernel, que es la función main(). En este momento, el sistema inicia sus estructuras de
datos, dispositivos de hardware e instala los manejadores en la tabla de procesos. Por último crea el
proceso inicial, denominado init, y lo activa pasándole el control del sistema.
Duranta la fase descrita anteriormente, el procesador ejecuta código en modo privilegiado y las
interrupciones están inhibidas (nivel de interrupción 3). Cabe destacar que cuando se activa el proceso
init, el procesador pasa a ejecutar código en modo usuario y se habilitan todas las interrupciones (nivel
0). Además, una vez hecho el cambio de contexto (ver línea 22 del código de kernel mostrado en la Tabla
3.6), el kernel no puede volver nunca a su función main(). A partir de este evento, el sistema operativo
sólo se ejecutará cuando se produzca una llamda al sistema, una excepción o una interrupción de un
dispositivo.
1: int main(){
2:
/* se llega con las interrupciones prohibidas */
3:
iniciar_tabla_proc();
4:
5:
instal_man_int(EXC_ARITM, exc_arit);
6:
instal_man_int(EXC_MEM, exc_mem);
7:
instal_man_int(INT_RELOJ, int_reloj);
8:
instal_man_int(INT_TERMINAL, int_terminal);
9:
instal_man_int(LLAM_SIS, tratar_llamsis);
10:
instal_man_int(INT_SW, int_sw);
11:
12:
iniciar_cont_int();
/* inicia cont. interr. */
13:
iniciar_cont_reloj(TICK); /* fija frec. del reloj */
14:
iniciar_cont_teclado();
/* inicia cont. teclado */
15:
16:
/* crea proceso inicial */
17:
if (crear_tarea((void *)"init")<0
18:
panico("no encontrado el proceso inicial");
19:
20: /* activa proceso inicial */
21: p_proc_actual=planificador();
22: cambio_contexto(NULL, &(p_proc_actual->contexto_regs));
23: panico("S.O. reactivado inesperadamente");
24: return 0;
25: }
Tabla 3.6 Código principal del kernel a utilizar.
3.3.2
Tratamiento de interrupciones En el caso de interrupciones externas, las únicas fuentes son el reloj y el terminal. Las rutinas de atención
instaladas, tanto para interrupciones externas y de software, sólo muestran un mensaje por pantalla
indicando la ocurrencia del evento. En el caso de la interrupción del teclado, la rutina de atención, además
usa la función leer_puerto() para obtener el carácter ingresado.
static void int_terminal(){
printk("-> TRATANDO INT. DE TERMINAL %c\n",
leer_puerto(DIR_TERMINAL));
return;
}
static void int_reloj(){
printk("-> TRATANDO INT. DE RELOJ\n");
return;
}
Tabla 3.7 Ejemplo de rutinas de tratamiento de interrupciones.
5
3.3.3
Tratamiento de excepciones Las dos posibles excepciones presentes en el sistema tienen un tratamiento común que depende del modo
que se ejecutaba el procesador antes de producirse la excepción. Si estaba en modo usuario, se termina la
ejecución del proceso actual. En caso contrario, se trata de un error del propio sistema operativo, por lo
que se invoca a la función panico() para termina su ejecución.
3.3.4
Llamadas al sistema Existe una única rutina de atención para todas las llamadas a sistema, denominada
tratar_llamsis()(ver Tabla 3.8). Tanto el código de la llamada como sus parámetros se pasan
mediante registros. Por convención, el código se pasa en el registro 0 y los parámetros en los siguientes,
considerando hasta cinco parámetros. El resultado de la llamada se devuelve en el registro 0. Esta rutina
invoca indirectamente, a través de la estructura tabla_sevicios[](ver Tabla 3.9 y Tabla 3.10), a la
función correspondiente. Dicha tabla almacena en cada posición la dirección de la función del sistema
operativo que lleva a cabo la llamada al sistema cuyo código corresponde con dicha posición.
static void tratar_llamsis(){
int nserv, res;
nserv=leer_registro(0);
if (nserv<NSERVICIOS)
res=(tabla_servicios[nserv].fservicio)();
else
res=-1;
/* servicio no existente */
escribir_registro(0,res);
return;
}
Tabla 3.8 Función de atención de llamadas a sistema. Archivo kernel.c.
/* Numero de llamadas disponibles */
#define NSERVICIOS 3
#define CREAR_PROCESO 0
#define TERMINAR_PROCESO 1
#define ESCRIBIR 2
Tabla 3.9 Macros utlizados por la estructura tabla_servicios[]. Archivo llamsis.h.
typedef struct{
int (*fservicio)();
} servicio;
servicio tabla_servicios[NSERVICIOS]={
{sis_crear_proceso},
{sis_terminar_proceso},
{sis_escribir}
};
Tabla 3.10 Tabla de Servicio. Archivo kernel.h.
6
Por ejemplo, el llamada a sistema CREAR_PROCESO, está relacionada con la función
sis_crear_proceso(). Esta función se muestra en la Tabla 3.11. Se debe notar que el nombre del
proceso a crear se pasa a través del registro 1 y la función que realmente lo crea es crear_tarea().
Todas las funciones de atención a llamadas de sistemas, están programadas en el archivo kernel.c.
int sis_crear_proceso(){
char *prog;
int res;
}
prog=(char *)leer_registro(1);
res=crear_tarea(prog);
return res;
Tabla 3.11 Función sis_crear_proceso(). Archivo kernel.c.
3.4
Programas de usuario
En el subdirectorio usuario existe inicialmente un conjunto de programas de ejemplo que usan los
servicios del minikernel. El más importante, al igual que los sistemas operativos reales, es el proceso init,
que es el primer programa que ejecuta el sistema operativo. Este proceso es el que va a lanzar o ejecutar
otros procesos, que básicamente serán muy simples, no como los procesos de los sistemas reales.
Los programas tienen acceso a las llamadas del sistema como funciones de bibioteca. Para este fin, existe
una biblioteca estática, denominada libserv.a, que contiene las funciones de interfaz (ver Tabla 3.12)
para las tres llamadas básicas al sistema, mencionadas en la sección anterior. Esta biblioteca se crea a
través de los objetos misc.o y serv.o. El alumno sólo tiene acceso al código de éste último objeto. Se
debe tener presente que por convención, los programas de usuario no deben usar llamadas al sistema
directamente. Lo deben hacer a través de bibliotecas.
int crear_proceso(char *prog){
return llamsis(CREAR_PROCESO, 1, (long)prog);
}
int terminar_proceso(){
return llamsis(TERMINAR_PROCESO, 0);
}
int escribir(char *texto, unsigned int longi){
return llamsis(ESCRIBIR, 2, (long)texto, (long)longi);
}
Tabla 3.12 Interfaces para las llamadas básicas al sistema. Archivo serv.c.
La función llamsis() está definido en misc.o y tiene la estructura que se muestra en la Tabla 3.13.
Aunque se pueden implementar las funciones de llamadas a sistema directamente, es preferible, en modo
usuario, utilizar esta función para realizar dicha tarea. Nótese que los argumentos de las llamadas a
sistema se pasan a través de registros internos del procesador.
int llamsis(int llamada, int nargs, ... /* args */)
{
va_list args;
int registro;
escribir_registro(0, llamada);
va_start(args, nargs);
for ( registro=1; nargs; nargs--, registro++) {
escribir_registro(registro, va_arg(args, long));
}
va_end(args);
trap();
return leer_registro(0);
}
Tabla 3.13 Estructura de la función llamsis() para realizar llamadas a sistema en modo usuario.
Finalmente, está definida la función start(), que constituye el punto real de inicio del programa. Su
estructura se encuentra en la Tabla 3.14. Esta función se encarga de invocar a la función main, que es el
punto de inicio del programa para el programador. Este truco permite asegurarse de que el programa
siempre invoca la llamada al sistema de finalización, terminar_proceso(), al terminar. Si no lo ha
hecho la función main(), lo hará start() al terminar la función main.
7
void start(void (*fini) (void)) {
fini();
terminar_proceso();
}
Tabla 3.14 Función start. Incluída en misc.o.
Todos los programas de usuario utlizan el archivo usuario/include/servicios.h, que contiene
los prototipos de las funciones de interfaz a las llamadas al sistema.
8
4
Código fuente de apoyo
En la Tabla 4.1 se describe detalladamento los archivos que se utillizarán en las tareas.
Directorio Raíz
Archivo o
Directorio
Makefile
boot/
boot/boot
minikernel/
Makefile
kernel
HAL.o
kernel.c
include/
HAL.h
const.h
llamsis.h
minikernel/
kernel.h
usuario/
Makefile
init.c
*.c
include/
servicios.h
lib/
Makefile
libserv.a
serv.c
misc.o
Descripción
Makefile general del entorno. Invoca a los
archivos Makefile de los subdirectorios.
Carga del sistema.
Programa de arranque del sistema operativo.
Entorno relacionado con el kernel.
Permite generar el ejecutable del sistema
operativo.
Ejecutable del sistema operativo.
Archivo que contiene las funciones relativas a la
capa de hardware.
Archivo que contiene la funciones del sistema
operativo. Este archivo debe ser modificado por
el alumno para incluir la funcionalidad solicitada
en cada tarea.
Archivos de cabecera
Contiene prototipos de las funciones del módulo
HAL. No debe ser modificado.
Contiene algunas constantes, como los vectores
de interrupción del sistema.
Contiene los códigos numéricos asignados a cada
llamada del sistema. Este archivo debe ser
modificado para incluir nuevas llamadas.
Contiene definiciones usadas por el módulo
kernel.c, como la tabla de servicios. Este archivo
debe ser modificado.
Programas de usuario.
Permite compilar los programas de usuario.
Primer programa que ejecuta el sistema
operativo. Se debe modifcar para que invoque a
los programas que se consideren oportunos.
Programas que son ejecutados por init.c.
Archivos de cabecera.
Contiene los prototipos de las funciones que
sirven de interfaz a las llamadas al sistema. Debe
ser modificado para incluir la interfaz para
nuevas llamadas.
Módulos que permiten generar la biblioteca que
utilizan los programas de usuario.
Genera la biblioteca de servicios.
Biblioteca de servicios.
Contiene la interfaz a los servicios del sistema
operativo. Debe ser modificado para incluir la
interfaz a nuevas llamadas.
Contiene otros funciones de bibliotecas
auxiliares.
Tabla 4.1 Descripción de los archivos utlizados por el entorno minikernel.
9
5
Gestión de procesos
La gestión de procesos del sistema corresponde al de un sistema monoprogramado. Esto significa que
aunque se puedan crear y cargar en memoria múltiples programas, el proceso en ejecución continúa hasta
que termina, ya sea voluntariamento o involutariamente a través de una excepción. Las llamadas al
sistema disponibles no pueden causar que el proceso pase a un estado bloqueado.
5.1
Características de la gestión de procesos del minikernel
La tabla de procesos (tabla_procs) es un arreglo de tamaño fijo (MAXPROC) de BCP.
BCP tabla_procs[MAX_PROC];
El BCP tiene la siguiente estructura (minikernel/include/kernel.h):
typedef struct BCP_t {
int id;
/* ident. del proceso */
int estado;
/* TERMINADO|LISTO|EJECUCION|BLOQUEADO*/
contexto_t contexto_regs; /* copia de regs. de UCP */
void * pila;
/* dir. inicial de la pila */
BCPptr siguiente; /* puntero a otro BCP */
void *info_mem;
/* descriptor del mapa de memoria */
} BCP;
El BCP dispone de un puntero (siguiente) que permite que el sistema operativo construya listas de
BCP que tengan alguna característica en común. Una lista de este tipo es la de procesos en estado listo
(lista_listos). Esta lista incluye al que se está ejecutándose actualmente.
El tipo usado para la fila de listos (tipo lista_BCP) permite construir listas con enlace simple,
almacenando referencias al primero y al último elemento de la lista. Para facilitar su gestión, se ofrecen
funciones que permiten eliminar e insertar BCP en una lista de este tipo. Este tipo puede usarse para otras
listas del sistema operativo (por ejemplo, para un semáforo). Este tipo de datos exige que un BCP no
pueda estar en dos listas simultáneamente.
La variable p_proc_actual apunta al BCP del proceso en ejecución. Este BCP está incluido en la fila de
listos.
La función que crea procesos (crea_proceso()) realiza los siguiente pasos:
•
•
•
•
•
•
Busca una entrada libre.
Crea el mapa de memoria a partir del ejecutable.
Reserva la pila del proceso.
Crea el contexto inicial.
Rellena el BCP adecuadamente.
Pone el proceso como listo para ejecutar y lo coloca al final de la fila de listos.
Cuando un proceso termina, se liberan sus recursos (imagen de memoria, pila y BCP). Luego se invoca al
planificador para que elija otro proceso y hacer un cambio de contexto a este nuevo proceso.
El planificador está determinado por la funcion planificador(). Debido a que el sistema es
monoprogramado, no se llama hasta que el proceso actual termina. Su política es FIFO. En el caso que
todos los procesos estén bloqueado (por ejemplo, si se cambia la política), se llamaría a la función
espera_int(), de la que no se volvería hasta que se produjese una interrrupción.
5.2
Cambios de contexto
El cambio de contexto es la operación que cambia el proceso asignado al procesdor por otro. Existen dos
tipos de cambios de contexto.
•
•
Cambio de contexto Voluntario: se produce cuando el proceso en ejecución pasa al estado
bloqueado debido a que tiene que esperar por algún tipo de evento. Sólo pueden ocurrir dentro
de una llamada a sistema. No pueden darse nunca en una función de interrupción, ya que éste
generalmente no está relacionada con el proceso que está actualmente ejecutando.
Cambio de contexto Involuntario: se produce cuando el proceso en ejecución tiene que pasar
al estado Listo, ya que debe dejar el procesador por algún motivo (por ejemplo, cuando se le
acaba su ventana de tiempo de ejecución o por que hay otro proceso con más prioridad que hay
que ejecutar).
10
5.3
Cambio de contexto Voluntario
En este caso, el programador incluye llamadas a la función cambio_contexto() cuando lo requiera.
Por ejemplo:
{
...
//Bloquear el proceso por que se
//necesitan datos
bloquear() //Llamada a sistema
...
}
y la funcion bloquear(), tendría la forma:
bloquear(){
proc_anterior = proc_actual;
proc_anterior->estado = BLOQUEADO;
proc_actual = planificador();
cambio_contexto(proc_anterior, proc_actual);
}
Nótese que la función bloquear() debería elevar al máximo el nivel de interrupción del procesador,
puesto que está modificando la lista de procesos listos y es prácticamente suguro que todas las funciones
de interrupción manipulan esta lista.
5.4
Cambio de contexto Involuntario
Los cambios de contexto involuntario se producen a través de interrupciones de software. Algunos
procesadores proporcionan este mecanismo, que consiste en una instrucción especial, que sólo puede
ejecutarse en modo privilegiado, que causa una interrupción de mínima prioridad.
Con la interrupción de software, el cambio de contecto involuntario es trivial: cuando dentro del código
de una llamada o una interrupción se detecta que hay que realizar, por motivo que sea, un cambio de
contexto involuntario, se activa la interrupción de software respectiva. Dado que se trata de una
interrupción del nivel mínimo, su rutina de tratamiento no se activará hasta que el nivel de interrupción
del procesador sea 0 (esto es, modo usuario). Esta función de tratamiento se encargará de realizar el
cambio de contexto involuntario.
6
Modalidad de trabajo
Originalmente, el minikernel se entregaba en formato comprimido estándar de unix (.tar.gz). El único
requisito es que la persona que desee utilizarlo, lo debe instalar en un sisema operativo Linux. Para esto,
la Escuela de Ingeniería Civil Informática posee computadores con Linux instalado. Pero para promover
la movilidad y el trabajo en otros lugares que no sean las salas de computadores y que no sea necesario
tener Linux instalado en un computador personal, se ha decidido entregar el minikernel junto con una
distribución de Linux en formato de Máquina Virtual. Especificamente, se entrega un “appliance”, que
básicamente consiste en un archivo que tiene todas las definiciones y archivos para que el usuario final
disponga de un computador virtual sin la necesidad de instalarlo. Para esto, sólo es necesario instalar el
software de virtualización VirtualBox [3] y luego importar el archivo que constituye el appliance. Es
recomendable instalar adicionalmente el software VirtualBox Extension Pack. Ambos se pueden bajar
de la página de VirtualBox.
7
Bibliografía
[1] Prácticas de Sistemas Operativos, Jesús Carretero Pérez, MC Graw-Hill, 2003.
[2] Operating system concepts, Abraham Silberschatz, Peter Baer Galvin, Greg Gagne, 6 ed.
[3] Virtual Box. http://www.virtualbox.org
8
Anexo: Utilización de Virtual box.
Se asume que el sistema operativo que usted utiliza es alguna versión de windows superior a XP y que el
software Virtual Box está instalado en su sistema. Además, usted necesitará el programa WinSCP, el que
lo puede bajar de http://portableapps.com/apps/internet/winscp_portable. Finalmente, el archivo
appliance se llama INC303.ova, el que estará publicado en un servidor que se dará a conocer a través del
Aula Virtual.
11
Paso 1: Importar el appliance en Virtual Box: Menú Archivo->Importar Servicio virtualizado. Luego
seleccionar el archivo ramosinc.ova. Inicie la máquina virtual. Si sale un error con respecto a las
interfaces de red, sólo coloque configurar las interfaces, acepte los cambios propuesto e inicie la máquina
nuevamente.
Paso 2: Ejecutar WinSCP, y seleccionar Sesión del tipo SFTP. En la IP del servidor, debe ingresar
192.168.56.10. El puerto de comunicación es 22. El usuario es root y su contraseña es admin. Una vez
realizado lo anterior, debe presionar “Conectar”. Luego de conectar, sus archivos locales estarán en el
lado izquierdo de la ventana del programa, y los archivos de la máquina virtual, es el lado derecho. Para
copiar de un lado a otro, sólo arrastre y suelte.
Observaciones:
Para compilar, es necesario que usted ingrese directamente a la máquina virtual. Para esto, debe ingresar a
ella con el usuario root y su contraseña admin. Usted deberá averiguar comandos básicos de Linux para
moverse en los directorios, en modo consola.
La distribución entregada es Debian, versión Jessie, con entorno gráfico XFCE. Para acceder a dicho
entorno, cuando usted ingrese al sistema, debe ejecutar el comando startxfce4. El software instalado
en la máquina virtual, está resumido en el campo “descripción” de la misma.
12
Descargar