25.06.2014 Views

Núcleo de un Sistema Operativo

Núcleo de un Sistema Operativo

Núcleo de un Sistema Operativo

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Departamento <strong>de</strong> Arquitectura y Tecnología <strong>de</strong> Computadores<br />

Konputagailuen Arkitektura eta Teknologia Saila<br />

_________________________________________<br />

Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s<br />

_________________________________________<br />

Núcleo <strong>de</strong> <strong>un</strong> <strong>Sistema</strong><br />

<strong>Operativo</strong><br />

Alberto Lafuente<br />

Febrero 2006


Contenido<br />

1 Introducción<br />

2 Una estructura en capas para el sistema operativo<br />

3 Estructura <strong>de</strong> <strong>un</strong> núcleo <strong>de</strong> sistema operativo<br />

3.1 Rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware<br />

3.2 Rutinas <strong>de</strong> manejo <strong>de</strong> colas y auxiliares<br />

3.3 Rutinas para la gestión <strong>de</strong> procesos<br />

3.4 Definiciones y estructuras <strong>de</strong> datos<br />

3.5 Primitivas <strong>de</strong>l núcleo<br />

4 F<strong>un</strong>cionamiento <strong>de</strong>l núcleo<br />

4.1 Gestión <strong>de</strong> procesos<br />

4.2 Primitivas bloqueantes<br />

4.3 Rutinas <strong>de</strong> tratamiento <strong>de</strong> interrupciones<br />

4.4 Primitivas no bloqueantes<br />

4.5 Primitivas <strong>de</strong> sincronización<br />

5 Puesta en marcha<br />

A.1 Prueba <strong>de</strong>l núcleo<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 2


1 Introducción<br />

Un sistema operativo se <strong>de</strong>fine <strong>de</strong>s<strong>de</strong> dos p<strong>un</strong>tos <strong>de</strong> vista. En primer lugar, el sistema<br />

operativo constituye la interfaz entre el usuario <strong>de</strong> <strong>un</strong> computador y los recursos <strong>de</strong> éste<br />

(hardware y software), proporcionando <strong>un</strong>a visión f<strong>un</strong>cional <strong>de</strong>l sistema en forma <strong>de</strong> llamadas<br />

al sistema. En seg<strong>un</strong>do lugar, el sistema operativo es el encargado <strong>de</strong> gestionar eficientemente<br />

la utilización <strong>de</strong> los recursos por los usuarios.<br />

Los servicios que <strong>un</strong> sistema operativo gestiona suelen dividirse en cuatro: procesador,<br />

memoria, dispositivos y ficheros. La complejidad inherente a la gestión <strong>de</strong> alg<strong>un</strong>o <strong>de</strong> estos<br />

servicios hace necesario estructurar el sistema operativo en varias capas o niveles, cada <strong>un</strong>a<br />

ofreciendo <strong>un</strong> conj<strong>un</strong>to <strong>de</strong> primitivas a la inmediatamente superior. Por ejemplo, el sistema <strong>de</strong><br />

ficheros resi<strong>de</strong> sobre el dispositivo disco, por lo que la gestión <strong>de</strong> ficheros se especificará en<br />

base a las primitivas que proporcione la gestión <strong>de</strong>l disco, que será la que programe el<br />

hardware <strong>de</strong>l dispositivo.<br />

El nivel básico <strong>de</strong> <strong>un</strong> sistema operativo, que oculta las características hardware <strong>de</strong> la máquina,<br />

se conoce como núcleo o kernel. En este documento se proporciona <strong>un</strong>a <strong>de</strong>scripción completa<br />

<strong>de</strong> la estructura <strong>de</strong>l núcleo <strong>de</strong> <strong>un</strong> sistema operativo multiprogramado, que incluye gestión <strong>de</strong><br />

procesos basada en priorida<strong>de</strong>s, gestión <strong>de</strong> dispositivos (disco flexible, teclado, pantalla,<br />

impresora, línea serie y reloj), y primitivas <strong>de</strong> sincronización (semáforos). Se ha escogido<br />

como plataforma soporte la arquitectura PC basada en la familia i80x86. Ya que la mayor<br />

parte <strong>de</strong> las características <strong>de</strong>pendientes <strong>de</strong> la arquitectura están encapsuladas en <strong>un</strong> conj<strong>un</strong>to<br />

<strong>de</strong> rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware, esta elección no es especialmente <strong>de</strong>terminante para el<br />

diseño <strong>de</strong>l sistema operativo. La difusión <strong>de</strong> esta arquitectura es la única razón para su<br />

elección. Por otra parte, las rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware proporcionan <strong>un</strong>a interfaz C<br />

para su uso en el núcleo, lo que facilita la portabilidad a otras plataformas.<br />

2 Una estructura en capas para el sistema operativo<br />

El esquema general propuesto es <strong>un</strong>a estructura en tres niveles o capas, <strong>de</strong> abajo arriba:<br />

1 Nivel Núcleo. Gestión básica <strong>de</strong> procesos: planificación a corto plazo, cambio <strong>de</strong><br />

contexto. Primitivas <strong>de</strong> sincronización. Gestión <strong>de</strong> E/S y tiempo, rutinas <strong>de</strong> atención.<br />

2 Nivel <strong>de</strong>l <strong>Sistema</strong> Básico <strong>de</strong> Ficheros, BFS. <strong>Sistema</strong> básico <strong>de</strong> ficheros: ubicación en<br />

disco, directorios. Rutinas <strong>de</strong> E/S, servidores <strong>de</strong> dispositivos.<br />

3 Nivel <strong>Sistema</strong>. Implementación <strong>de</strong> las llamadas al sistema; in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong>l<br />

dispositivo (tablas <strong>de</strong> canales); gestión <strong>de</strong> buffers para acceso a ficheros; carga,<br />

ejecución y finalización <strong>de</strong> procesos.<br />

La Figura 1 muestra la estructura en niveles <strong>de</strong> <strong>un</strong> sistema operativo. Para <strong>un</strong> nivel n sólo son<br />

visibles las primitivas <strong>de</strong>l nivel n-1. En general <strong>un</strong> nivel utilizará a<strong>de</strong>más <strong>un</strong>a serie <strong>de</strong> rutinas<br />

auxiliares internas que no son visibles <strong>de</strong>s<strong>de</strong> el nivel superior.<br />

3 Estructura <strong>de</strong> <strong>un</strong> núcleo <strong>de</strong> sistema operativo<br />

Como ejemplo <strong>de</strong> núcleo <strong>de</strong> sistema operativo, en esta sección <strong>de</strong>scribiremos las rutinas<br />

internas, estructuras <strong>de</strong> datos y primitivas <strong>de</strong> <strong>un</strong> núcleo multiprogramado para la arquitectura<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 3


i80x86. Este núcleo es el que usaremos en el laboratorio. El f<strong>un</strong>cionamiento <strong>de</strong>l núcleo <strong>de</strong><br />

presentará en la siguiente sección.<br />

USUARIO<br />

Rutinas<br />

<strong>de</strong><br />

Librería<br />

NIVEL<br />

SISTEMA<br />

Rutinas<br />

internas<br />

Nivel <strong>Sistema</strong><br />

Primitivas<br />

Nivel <strong>Sistema</strong><br />

NIVEL<br />

BFS<br />

Rutinas<br />

internas<br />

Nivel BFS<br />

Primitivas<br />

Nivel BFS<br />

NIVEL<br />

NUCLEO<br />

Rutinas<br />

internas<br />

Nivel Núcleo<br />

Primitivas<br />

Nivel Núcleo<br />

Rutinas<br />

<strong>de</strong>pendientes<br />

<strong>de</strong>l Hardware<br />

HARDWARE<br />

Figura 1. Niveles en <strong>un</strong> sistema operativo<br />

3.1 Rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware<br />

Son las rutinas <strong>de</strong>l núcleo <strong>de</strong> más bajo nivel. Distinguiremos dos grupos. Las más elementales<br />

están escritas en ensamblador y tratan con direcciones específicas <strong>de</strong> E/S o <strong>de</strong> memoria (para<br />

los dispositivos memory-mapped), o bien inhiben/activan interrupciones o manipulan<br />

directamente la pila <strong>de</strong>l programa para proporcionar los cambios <strong>de</strong> contexto. Sus prototipos<br />

se <strong>de</strong>scriben en la Figura 2. El seg<strong>un</strong>do grupo <strong>de</strong> rutinas internas <strong>de</strong>l núcleo, que programan<br />

los dispositivos, están codificadas en C. Estas se <strong>de</strong>scriben en la Figura 3.<br />

Las rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware se llamarán únicamente <strong>de</strong>s<strong>de</strong> el núcleo. Encapsulan<br />

las características <strong>de</strong> la arquitectura.<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 4


RUTINAS BASICAS DEL NUCLEO<br />

int flag inhibir ();<br />

void <strong>de</strong>sinhibir (int flag);<br />

int in_port (int puerto);<br />

void out_port (int puerto, int dato);<br />

void strobe (int puerto, char mascara);<br />

void eoi ();<br />

void vector_int (int num_int , void (*rutina )());<br />

int mascara_anterior enmascarar (int mascara);<br />

void salvar_flags ();<br />

void salvar_reg ();<br />

void restaurar ();<br />

void reset ();<br />

void timer (int valor);<br />

Figura 2. Rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware en ensamblador<br />

RELACIONADAS CON EL TERMINAL<br />

char leer_teclado ();<br />

void escribir_pantalla (int fila, int columna, char car, char atrib);<br />

void posicionar_cursor (int fila , int columna);<br />

void scroll (int fila_ini , int fila_fin);<br />

int estado escribir_impresora (char car);<br />

void beep (long tono_sonido);<br />

RELACIONADAS CON LA LINEA SERIE<br />

void ini_ls ();<br />

int estado escribir_ls (char car);<br />

char leer_ls ();<br />

int estado consulta_ls_iir ();<br />

int estado consulta_ls_lsr ();<br />

int estado consulta_ls_msr ();<br />

RELACIONADAS CON EL DISCO<br />

void motor_on (int drive);<br />

void motor_off ();<br />

void posicionar_pista (int pista, int drive);<br />

void programar_disco (int op, int cara, int pista, int sector, int drive);<br />

int estado programar_dma (int operacion, char *buffer );<br />

void comando_disco (int comando);<br />

int estado comprobar_posicionado ();<br />

int estado comprobar_trasferencia ();<br />

int estado leer_status ();<br />

void recalibrar (int drive);<br />

Figura 3. Rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware en C<br />

3.2 Rutinas <strong>de</strong> manejo <strong>de</strong> colas y auxiliares<br />

La mayoría se utilizan en el núcleo para el manejo <strong>de</strong> los PCBs. F<strong>un</strong>damentalmente son<br />

rutinas <strong>de</strong> acceso a colas, cuyos prototipos se <strong>de</strong>scriben en la Figura 4. Otras rutinas<br />

auxiliares, <strong>de</strong> apoyo a la programación, se ilustran en la Figura 5. En general, las rutinas<br />

auxiliares se utilizarán también en los niveles superiores. Por esta razón se <strong>de</strong>finen las<br />

estructuras item y cola <strong>de</strong> forma genérica, sin incluir información específica <strong>de</strong> los elementos<br />

que enlazan (en general procesos).<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 5


RUTINAS PARA MANEJO DE COLAS<br />

struct item {<br />

struct item *siguiente;<br />

struct item *anterior;<br />

int prioridad;<br />

};<br />

struct cola {<br />

struct item *primero;<br />

struct item *ultimo;<br />

};<br />

void inicializar_cola (struct cola *cola);<br />

int vacia (struct cola *cola);<br />

struct item *primero (struct cola *cola);<br />

/* Elimina y <strong>de</strong>vuelve el primer elemento <strong>de</strong> cola */<br />

struct item *extraer (struct cola *cola, int prio);<br />

/* Elimina y <strong>de</strong>vuelve el primero <strong>de</strong> los <strong>de</strong> prioridad prio */<br />

struct item *extirpar (struct cola *cola, struct item *elem);<br />

/* Elimina y <strong>de</strong>vuelve el elemento ap<strong>un</strong>tado por elem */<br />

void encolar (struct cola *cola, struct item *elem);<br />

/* Encola elem al final <strong>de</strong> cola */<br />

void insertar(struct cola *cola, struct item *elem, int prio);<br />

/* Encola elem al final <strong>de</strong> los <strong>de</strong> prioridad prio */<br />

void meter(struct cola *cola, struct item *elem, int estado);<br />

/* Inserta elem actualizando estado en el PCB */<br />

RUTINAS AUXILIARES DIVERSAS<br />

Figura 4. Rutinas auxiliares para manejo <strong>de</strong> colas<br />

void copiar (char *str1 , char *str2 , int lon);<br />

int comparar (char *str1, char *str2 , int lon);<br />

int buscar (char *str, char car, int lon);<br />

void rellenar (char *str, char car, int lon);<br />

void mayuscula (char *str, int lon);<br />

void minuscula (char *str, int lon);<br />

int long_a_ascii (long num, char *str, int base, int lon);<br />

int int_a_ascii (int num, char *str, int base, int lon);<br />

long ascii_a_long (char *str, int lon, int base);<br />

int ascii_a_ int (char *str, int lon, int base);<br />

Figura 5. Otras rutinas auxiliares<br />

3.3 Rutinas para la gestión <strong>de</strong> procesos<br />

La gestión <strong>de</strong> procesos está soportada por rutinas que manejan colas y proporcionan cambios<br />

<strong>de</strong> estado en los procesos: bloquear(), scheduler(), dispatcher. Otras rutinas inicializan<br />

estructuras <strong>de</strong> datos (PCB y pila) para los procesos en su creación. Estas rutinas se muestran<br />

en la Figura 6 y se estudiarán más a<strong>de</strong>lante.<br />

RUTINAS PARA LA GESTION DE PROCESOS<br />

void bloquear (struct cola *cola, int estado);<br />

struct item *scheduler();<br />

void dispatcher (struct item este_item)<br />

void crear_pcb(void (*codigo)(), int *pila, int prio, int q, int id_proc));<br />

void crear ();<br />

Figura 6. Rutinas para la gestión <strong>de</strong> procesos<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 6


3.4 Definiciones y estructuras <strong>de</strong> datos<br />

Se refieren a la estructura <strong>de</strong>l PCB y a las colas que mo<strong>de</strong>lan el f<strong>un</strong>cionamiento <strong>de</strong>l núcleo.<br />

Aparecen en la Figura 7. Existe a<strong>de</strong>más <strong>un</strong>a serie <strong>de</strong> <strong>de</strong>finiciones <strong>de</strong> símbolos para los estados<br />

<strong>de</strong> los procesos, dispositivos, etc, que no se muestran en la figura.<br />

#<strong>de</strong>fine NUM_PCBS … /* numero maximo <strong>de</strong> PCB */<br />

#<strong>de</strong>fine TAM_PILA … /* tamaño <strong>de</strong> la pila <strong>de</strong> <strong>un</strong> proceso */<br />

struct pcb {<br />

struct item elemento_cola;<br />

int id_proceso;<br />

int ax;<br />

int dx;<br />

int far *pila;<br />

int quantum;<br />

long cpu_time;<br />

int status;<br />

};<br />

struct pcb proc[NUM_PCBS];<br />

int pilas[NUM_PCBS][TAM_PILA];<br />

struct info_proc {<br />

int quantum;<br />

int prioridad;<br />

long t_cpu;<br />

int status;<br />

};<br />

struct cola libres,<br />

ready,<br />

r<strong>un</strong>,<br />

teclado,<br />

disco,<br />

retardo,<br />

lectura_ls,<br />

escritura_ls;<br />

int ntr = 0; /* numero <strong>de</strong> ticks <strong>de</strong> reloj */<br />

#<strong>de</strong>fine NSEM … /* semáforos */<br />

struct semaforo {<br />

struct cola s;<br />

int cont;<br />

};<br />

struct semaforo sem[NSEM];<br />

Figura 7. Definiciones f<strong>un</strong>damentales <strong>de</strong>l núcleo<br />

Otro conj<strong>un</strong>to <strong>de</strong> <strong>de</strong>finiciones (que tampoco se muestran aquí) especifican la configuración <strong>de</strong><br />

la máquina concreta (direcciones <strong>de</strong> pantalla, mapa <strong>de</strong> teclado, etc).<br />

3.5 Primitivas <strong>de</strong>l núcleo<br />

Las primitivas <strong>de</strong>l núcleo constituyen el conj<strong>un</strong>to <strong>de</strong> rutinas visibles <strong>de</strong>s<strong>de</strong> el nivel superior.<br />

Se <strong>de</strong>finen en C y utilizan las rutinas <strong>de</strong>pendientes <strong>de</strong>l hardware y a las rutinas auxiliares<br />

introducidas anteriormente. Las diferenciaremos <strong>de</strong>nominándolas con el sufijo _nuc. Se<br />

pue<strong>de</strong>n dividir en los siguientes grupos:<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 7


1 Control <strong>de</strong> dispositivos<br />

(a) disco<br />

void motor_on_nuc (int drive)<br />

void motor_off_nuc ()<br />

int posicionar_pista_nuc (int n_pista, int drive)<br />

int leer_sector_nuc (int n_cara, int n_sector, char *p_buff)<br />

int escribir_sector_nuc (int n_cara, int n_sector, char *p_buff)<br />

int recalibrar_nuc (int drive)<br />

(b) terminal<br />

char leer_teclado_nuc ()<br />

int escribir_pantalla_nuc (int lin, int col, char car, char atributo)<br />

int scroll_nuc (int lin_sup, int lin_inf)<br />

(c) línea serie<br />

int leer_l_s_nuc ()<br />

void escribir_l_s_nuc (char car)<br />

void init_l_s_nuc ()<br />

(d) impresora<br />

int escribir_impresora_nuc (char car)<br />

2 Control <strong>de</strong> procesos<br />

int crear_pcb_nuc (void (*cod)(), int *pila, int prio, int quantum, int pid)<br />

int <strong>de</strong>struir_pcb_nuc (int id_proc)<br />

int quisoc_nuc ()<br />

int info_proc_nuc (int id_proceso, struct info_proc *p_info)<br />

int modif_proc_nuc (int id_proceso, struct info_proc *p_info)<br />

3 Espera por tiempo<br />

void retardo_nuc (int n_tics)<br />

4 Sincronización entre procesos<br />

5 Reset<br />

int wait_nuc (int n_sem)<br />

int signal_nuc (int n_sem)<br />

int init_sem_nuc (int n_sem, int valor_inicial)<br />

void reset_nuc ()<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 8


4 F<strong>un</strong>cionamiento <strong>de</strong>l núcleo<br />

4.1 Gestión <strong>de</strong> procesos<br />

Incluiremos aquí planificación, creación y <strong>de</strong>strucción <strong>de</strong> procesos. Un proceso se representa<br />

en <strong>un</strong> PCB (struct pcb) que se i<strong>de</strong>ntifica por <strong>un</strong> entero (índice en la tabla <strong>de</strong> PCBs).<br />

3.4.1 Planificación <strong>de</strong> procesos<br />

En la versión <strong>de</strong>l núcleo propuesta, se proporciona planificación <strong>de</strong> procesos <strong>de</strong> tiempo<br />

compartido y priorida<strong>de</strong>s, con expulsión por <strong>de</strong>sbloqueo <strong>de</strong> <strong>un</strong> proceso. Esto implica que la<br />

gestión <strong>de</strong> procesos involucra a las rutinas <strong>de</strong> atención (muy en particular a la <strong>de</strong> reloj para<br />

control <strong>de</strong> fin <strong>de</strong> quantum), a<strong>de</strong>más <strong>de</strong> las que hacen que <strong>un</strong> proceso pase a estado bloqueado<br />

(más a<strong>de</strong>lante nos referiremos a estas rutinas como bloqueantes).<br />

Los procesos preparados para ejecución se encolan en la cola ready <strong>de</strong> acuerdo a su prioridad,<br />

por lo que la rutina <strong>de</strong>l scheduler se limita a coger el primer elemento <strong>de</strong> la cola, que pasa al<br />

dispatcher para que lo cargue en ejecución (cola r<strong>un</strong>, que tendrá <strong>un</strong> único elemento). Por<br />

ortogonalidad, se asume la existencia <strong>de</strong> <strong>un</strong> proceso nulo, que siempre estará preparado para<br />

ejecución o ejecutándose. La Figura 8 muestra el código <strong>de</strong> las rutinas scheduler() y el<br />

dispatcher(). La rutina bloquear() es la encargada <strong>de</strong> sacar a <strong>un</strong> proceso <strong>de</strong> la cola, lo que<br />

implica como efecto colateral la actualización <strong>de</strong>l tiempo <strong>de</strong> CPU. Su código se muestra en la<br />

Figura 9.<br />

struct item *scheduler()<br />

{<br />

return (primero(&ready));<br />

}<br />

void dispatcher (este_item)<br />

struct item *este_item;<br />

{<br />

meter (&r<strong>un</strong>, este_item, RUN);<br />

}<br />

Figura 8. Rutinas scheduler y dispatcher<br />

void bloquear (c, status)<br />

struct cola *c;<br />

int status;<br />

{<br />

((struct pcb *)r<strong>un</strong>.primero)->cpu_time += ntr;<br />

meter(c, primero(&r<strong>un</strong>), status);<br />

ntr=0;<br />

}<br />

Figura 9. Rutina bloquear<br />

Una vez actualizadas la colas <strong>de</strong> procesos, la multiplexación se completa reestableciendo el<br />

estado <strong>de</strong>l procesador para que ejecute el proceso planificado. Básicamente esto implica hacer<br />

que el SP ap<strong>un</strong>te a la pila <strong>de</strong>l nuevo proceso. La rutina restaurar(), codificada en<br />

ensamblador, es la que se encarga <strong>de</strong> ello, manipulando el bloque <strong>de</strong> activación anterior.<br />

Previamente, la rutina salvar_reg() habrá salvado el estado <strong>de</strong>l proceso que estaba en<br />

ejecución.<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 9


La gestión <strong>de</strong>l tiempo compartido la realiza la rutina <strong>de</strong> atención al reloj, multiplexar(), como<br />

se ilustra en la Figura 10. Esta rutina incluye otras f<strong>un</strong>ciones (apagado automático <strong>de</strong>l motor<br />

<strong>de</strong> la <strong>un</strong>idad <strong>de</strong> disco y control <strong>de</strong>l periodo transitorio tras el encendido, retardo <strong>de</strong> procesos<br />

por tiempo), que no aparecen en la figura.<br />

void multiplexar ()<br />

{<br />

salvar_reg();<br />

/* FIN DE QUANTUM: */<br />

if (++ntr==((struct pcb*)r<strong>un</strong>.primero)->quantum) cambiar = TRUE;<br />

…<br />

}<br />

if (cambiar) {<br />

bloquear (&ready, READY);<br />

dispatcher (scheduler());<br />

}<br />

eoi();<br />

restaurar();<br />

Figura 10. Rutina multiplexar. Control <strong>de</strong> quantum.<br />

3.4.2 Creación, control y <strong>de</strong>strucción <strong>de</strong> procesos<br />

La rutina crear_pcb_nuc() asigna <strong>un</strong> PCB para <strong>un</strong> nuevo proceso, provocando la expulsión<br />

<strong>de</strong>l que está en ejecución. Existen primitivas para extraer información <strong>de</strong> <strong>un</strong> proceso y su<br />

i<strong>de</strong>ntificador (info_proc_nuc(), quisoc_nuc()) y para modificar su quantum y prioridad<br />

(modif_proc_nuc()). Finalmente, en la Figura 11 se incluye el código <strong>de</strong><br />

<strong>de</strong>struir_pcb_nuc(). Nótese las situaciones en que el PCB no se libera (el proceso tiene <strong>un</strong>a<br />

petición pendiente), sino que el proceso queda en estado finalizado (DEAD). La rutina <strong>de</strong><br />

atención correspondiente liberará el PCB <strong>de</strong>l proceso DEAD con la petición pendiente.<br />

4.2 Primitivas bloqueantes<br />

Llamaremos rutinas bloqueantes <strong>de</strong>l núcleo a las que solicitan <strong>un</strong> servicio <strong>de</strong>l hardware (<strong>un</strong><br />

dispositivo, el reloj…) y <strong>de</strong>jan al proceso en estado bloqueado, provocando la planificación<br />

<strong>de</strong> <strong>un</strong> nuevo proceso. Son varias las primitivas que trabajan <strong>de</strong> esta forma, en general aquéllas<br />

cuyo servicio se aten<strong>de</strong>rá mediante interrupciones, como motor_on_nuc(), retardo_nuc() —<br />

por tiempo—; leer_teclado_nuc() —por teclado—; escribir_impresora_nuc() —por<br />

impresora— leer_l_s_nuc, escribir_l_s_nuc() —por línea serie—; posicionar_pista_nuc(),<br />

leer_sector_nuc(), escribir_sector_nuc(), y recalibrar_nuc() —por disco—. Como<br />

ejemplo, en la Figura 12 se muestra el código <strong>de</strong> leer_teclado_nuc(). Todas las rutinas<br />

bloqueantes siguen el protocolo <strong>de</strong> salvar el contexto <strong>de</strong>l proceso (salvar_flags y<br />

salvar_reg), bloquear al proceso y planificar otro proceso cargando su contexto. Nótese<br />

también que se exige que la cola <strong>de</strong> bloqueado esté vacía: se sugiere la implementación <strong>de</strong> <strong>un</strong><br />

esquema cliente-servidor en el nivel superior.<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 10


int <strong>de</strong>struir_pcb_nuc (id_proceso)<br />

int id_proceso;<br />

{<br />

inhibir();<br />

salvar_flags();<br />

salvar_reg();<br />

}<br />

id_proceso = id_proceso % NUM_PCBS;<br />

switch (proc[id_proceso].status) {<br />

case LIBRE:<br />

((struct pcb *)r<strong>un</strong>.primero)->ax = -1;<br />

break;<br />

case READY:<br />

proc[id_proceso].status = LIBRE;<br />

encolar (&libres, extirpar(&ready,<br />

&(proc[id_proceso].elemento_cola)));<br />

((struct pcb *)r<strong>un</strong>.primero)->ax = 0;<br />

break;<br />

case RUN:<br />

proc[id_proceso].status = LIBRE;<br />

encolar (&libres,primero (&r<strong>un</strong>));<br />

dispatcher (scheduler());<br />

break;<br />

case BLOQ_TEC :<br />

case BLOQ_DISC:<br />

case BLOQ_RET:<br />

proc[id_proceso].status = DEAD;<br />

((struct pcb *)r<strong>un</strong>.primero)->ax = 0;<br />

break;<br />

<strong>de</strong>fault:<br />

proc[id_proceso].status = LIBRE;<br />

encolar(&libres, extirpar (&sem[proc[id_proceso].status].s,<br />

&(proc[id_proceso].elemento_cola)));<br />

((struct pcb *)r<strong>un</strong>.primero)->ax = 0;<br />

break;<br />

}<br />

restaurar();<br />

Figura 11. Rutina <strong>de</strong>struir_pcb_nuc<br />

char leer_teclado_nuc ()<br />

{<br />

inhibir();<br />

salvar_flags();<br />

salvar_reg();<br />

if (vacia (&teclado)) {<br />

bloquear(&teclado, BLOQ_TEC);<br />

dispatcher (scheduler());<br />

restaurar();<br />

}<br />

else crash();<br />

}<br />

Figura 12. Ejemplo <strong>de</strong> rutina bloqueante<br />

4.3 Rutinas <strong>de</strong> tratamiento <strong>de</strong> interrupciones<br />

A<strong>un</strong>que la rutina multiplexar pue<strong>de</strong> servir como ejemplo <strong>de</strong> rutina <strong>de</strong> tratamiento <strong>de</strong><br />

interrupciones, resultará clarificador en este momento introducir el código <strong>de</strong> la rutina <strong>de</strong><br />

tratamiento <strong>de</strong> las interrupciones <strong>de</strong> teclado (Figura 13) para completar el ejemplo <strong>de</strong><br />

tratamiento <strong>de</strong> peticiones <strong>de</strong>l apartado anterior. Hay que resaltar alg<strong>un</strong>a cuestión importante,<br />

común al resto <strong>de</strong> rutinas <strong>de</strong> este tipo:<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 11


• Tratamiento <strong>de</strong> peticiones pendientes <strong>de</strong> pocesos finalizados. La rutina <strong>de</strong> atención<br />

<strong>de</strong>tecta que el proceso está en estado DEAD (véase <strong>de</strong>struir_pcb_nuc()) y libera su<br />

PCB.<br />

• Valores <strong>de</strong> retorno. El carácter leído (o <strong>un</strong> código <strong>de</strong> error en otras rutinas) <strong>de</strong>be ser<br />

<strong>de</strong>vuelto a través <strong>de</strong>l PCB <strong>de</strong>l proceso (campo ax), que será lo que entregue como<br />

valor <strong>de</strong> retorno la primitiva que produjo el bloqueo (leer_teclado en nuestro ejemplo)<br />

al restaurarse el contexto <strong>de</strong>l proceso (rutina restaurar()) cuando éste sea planificado<br />

nuevamente.<br />

void int_teclado()<br />

{<br />

salvar_reg();<br />

if ((c=leer_teclado()) != -1)<br />

if (!vacia(&teclado))<br />

if (((struct pcb *)teclado.primero)->status == DEAD) {<br />

((struct pcb *)teclado.primero)->status= LIBRE;<br />

encolar(&libres, primero(&teclado));<br />

}<br />

else {<br />

((struct pcb *)teclado.primero)->ax= c;<br />

meter(&ready, primero(&teclado), READY);<br />

bloquear(&ready, READY);<br />

dispatcher (scheduler());<br />

}<br />

else beep();<br />

eoi();<br />

restaurar();<br />

}<br />

Figura 13. Ejemplo <strong>de</strong> rutina <strong>de</strong> tratamiento <strong>de</strong> interrupciones<br />

4.4 Primitivas no bloqueantes<br />

Llamaremos rutinas no bloqueantes a las que no <strong>de</strong>jan al proceso bloqueado. En general serán<br />

<strong>de</strong> este tipo aquellas primitivas que no se sirven mediante interrupciones, como es el caso <strong>de</strong><br />

esribir_pantalla_nuc(), que se muestra en la Figura 14. Otras primitivas <strong>de</strong> este tipo son:<br />

scroll_nuc(), motor_off_nuc(), quisoc_nuc() e info_proc_nuc(). Estas primitivas se<br />

resuelven como simples llamadas a f<strong>un</strong>ciones y no producen cambios <strong>de</strong> contexto.<br />

int escribir_pantalla_nuc (linea, columna, caracter, atributo)<br />

int linea, columna;<br />

char caracter, atributo;<br />

{<br />

if (0


4.5 Sincronización entre procesos<br />

El núcleo propuesto ofrece semáforos como mecanismo básico <strong>de</strong> sincronización entre<br />

procesos. Un semáforo se i<strong>de</strong>ntifica por <strong>un</strong> entero. Se introduce <strong>un</strong>a nueva condición <strong>de</strong><br />

bloqueo para los procesos, con tantas colas como semáforos disponibles, NSEM. El bloqueo<br />

por semáforo presenta la particularidad <strong>de</strong> que <strong>de</strong>be seguir estrictamente <strong>un</strong>a disciplina FCFS,<br />

por lo que el mecanismo <strong>de</strong> bloqueo estándar introducido para las rutinas bloqueantes no se<br />

pue<strong>de</strong> aplicar en la primitiva <strong>de</strong> espera por semáforo, wait_nuc(), <strong>de</strong>scrita en la Figura 15. La<br />

primitiva signal_nuc() se ha implementado como expulsora.<br />

int wait_nuc (n_semaforo)<br />

int n_semaforo;<br />

{<br />

inhibir();<br />

salvar_flags();<br />

salvar_reg();<br />

if (0 0) sem[n_semaforo].cont--;<br />

else {<br />

((struct pcb *)r<strong>un</strong>.primero)->status = n_semaforo;<br />

((struct pcb *)r<strong>un</strong>.primero)->cpu_time += ntr;<br />

encolar (&sem[n_semaforo].s, primero(&r<strong>un</strong>));<br />

ntr=0;<br />

dispatcher (scheduler());<br />

}<br />

}<br />

else ((struct pcb *)r<strong>un</strong>.primero)->ax = -1;<br />

restaurar();<br />

}<br />

5 Puesta en marcha<br />

Figura 15. Rutina wait_nuc<br />

Sobre el núcleo propuesto se pue<strong>de</strong> construir <strong>un</strong> sistema operativo completo. Como no<br />

disponemos <strong>de</strong> <strong>un</strong> sistema operativo completo con todas sus herramientas para soportar<br />

aplicaciones, probaremos el f<strong>un</strong>cionamiento <strong>de</strong>l núcleo con <strong>un</strong> conj<strong>un</strong>to <strong>de</strong> programas <strong>de</strong><br />

prueba que utilicen sus primitivas y monitoricen su f<strong>un</strong>cionamiento. En cualquier caso, se<br />

requiere <strong>un</strong> programa que instale el núcleo y lance los procesos <strong>de</strong>l sistema. Este programa<br />

incluirá:<br />

• El código completo <strong>de</strong>l núcleo, que incluye primitivas, rutinas internas y librerías <strong>de</strong><br />

bajo nivel.<br />

• El código <strong>de</strong> la prueba <strong>de</strong>l núcleo, que es <strong>un</strong> conj<strong>un</strong>to <strong>de</strong> f<strong>un</strong>ciones en código C que se<br />

ejecutarán asíncronamente, como threads o procesos a ser gestionados por el núcleo.<br />

• El procedimiento principal (main) <strong>de</strong> puesta en marcha.<br />

El procedimiento principal realizará las siguientes acciones:<br />

(1) Instalación <strong>de</strong> las rutinas <strong>de</strong> tratamiento <strong>de</strong> interrupciones, mediante vector_int().<br />

(2) Inicialización <strong>de</strong> las colas <strong>de</strong>l núcleo (r<strong>un</strong>, ready, teclado, semáforos, …).<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 13


(3) Inicialización <strong>de</strong> los PCBs como libres, encolándolos en la cola <strong>de</strong> libres.<br />

(4) Creación <strong>de</strong> los procesos <strong>de</strong>l sistema. Al menos son necesarios dos: el proceso nulo, y<br />

<strong>un</strong> proceso <strong>de</strong> inicialización, que lanzará el resto ya sobre el núcleo. Estos procesos se<br />

crean rellenando directamente los campos <strong>de</strong> los PCBs y utilizando la rutina<br />

crear_pcb() para asignar la pila correspondiente y ap<strong>un</strong>tar al código <strong>de</strong>l proceso.<br />

(5) Planificar (dispatcher y scheduler) y cargar el estado <strong>de</strong>l proceso seleccionado<br />

(mediante restaurar()). Este proceso será el <strong>de</strong> inicialización, ya que el nulo se habrá<br />

creado con la prioridad mínima.<br />

Ya que no disponemos <strong>de</strong> <strong>un</strong> dispositivo <strong>de</strong> arranque propio, la puesta en marcha <strong>de</strong>l núcleo<br />

requiere <strong>de</strong> <strong>un</strong> sistema operativo anfitrión, MS-DOS. El programa <strong>de</strong> prueba permite volver a<br />

MS-DOS (al menos si no se provocan situaciones <strong>de</strong> fallo que requieran reiniciar el sistema).<br />

Naturalmente, el núcleo pue<strong>de</strong> ser lanzado <strong>de</strong>s<strong>de</strong> cualquier sistema operativo Windows 95 o<br />

98, lo que facilita el <strong>de</strong>sarrollo y manejo <strong>de</strong>l software, pero no <strong>de</strong>s<strong>de</strong> <strong>un</strong> sistema Windows<br />

basado en NT (2000 o XP). En este último caso, aún se pue<strong>de</strong> recurrir a <strong>un</strong> emulador<br />

(VMware o Virtual PC) que soporte MS-DOS.<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 14


A1 Prueba <strong>de</strong>l núcleo<br />

A1.1 Compilación y montaje<br />

Los ficheros fuente <strong>de</strong>l núcleo que se proporcionan son:<br />

rutinas.h<br />

maquina.h<br />

nucleo.h<br />

Definiciones f<strong>un</strong>damentales <strong>de</strong>l núcleo; <strong>de</strong>claraciones <strong>de</strong> rutinas auxiliares y<br />

<strong>de</strong> manejo <strong>de</strong> colas, y <strong>de</strong>claraciones <strong>de</strong> rutinas ensamblador y C<br />

<strong>de</strong>pendientes <strong>de</strong>l hardware.<br />

Definiciones (condicionales) <strong>de</strong>pendientes <strong>de</strong> la pantalla y el teclado.<br />

Primitivas <strong>de</strong>l núcleo.<br />

Para probar esta versión <strong>de</strong>l núcleo, se proporciona también <strong>un</strong> programa <strong>de</strong> prueba,<br />

pruebas.h (que hace las veces <strong>de</strong> nivel superior). pruebas.h se incluye j<strong>un</strong>to a nucleo.h en <strong>un</strong><br />

fichero fuente fase0.c, que contiene el código para poner en marcha el sistema. Un resumen<br />

<strong>de</strong> la organización <strong>de</strong> los módulos fuente pue<strong>de</strong> verse en la Figura A1.1.<br />

Se proporciona <strong>un</strong> comando para compilar el código:<br />

ccc nombre_programa<br />

Para la prueba <strong>de</strong> la versión propuesta, nombre_programa será fase0<br />

Se proporciona otro comando para montar el código obtenido con los módulos objeto<br />

proporcionados, <strong>de</strong> acuerdo al esquema <strong>de</strong> la Figura A1.2:<br />

A1.2 Ejecución<br />

li nombre_programa<br />

Una vez compilado y montado el código, se pue<strong>de</strong> probar el f<strong>un</strong>cionamiento <strong>de</strong>l núcleo con<br />

ayuda <strong>de</strong>l código ejecutable obtenido. Al ejecutar fase0 se ofrecen mediante <strong>un</strong> menú las<br />

pruebas <strong>de</strong> diferentes aspectos <strong>de</strong>l núcleo:<br />

• Control <strong>de</strong> procesos. Permite manejar priorida<strong>de</strong>s y quanta <strong>de</strong> procesos.<br />

• Control <strong>de</strong>l disco flexible. Permite operar el motor, posicionar el cabezal y<br />

transferir información entre disco y memoria.<br />

La introducción <strong>de</strong> otras opciones se realizará modificando el código <strong>de</strong> pruebas.h.<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 15


utinas.h<br />

nucleo.h<br />

fase0.c<br />

Rutinas Definiciones, auxiliares<br />

rutinas Definiciones auxiliares,<br />

Rutinas ensambador etc<br />

(namejo <strong>de</strong>l Hw)<br />

Rutinas <strong>de</strong>pendientes<br />

<strong>de</strong> la máquina<br />

#inclu<strong>de</strong> "rutinas.h"<br />

#inclu<strong>de</strong> "maquina.h"<br />

Código <strong>de</strong> las<br />

primitivas <strong>de</strong>l núcleo<br />

Programas que<br />

usan el núcleo<br />

#inclu<strong>de</strong> "nucleo.h"<br />

#inclu<strong>de</strong> "pruebas.h"<br />

Programa <strong>de</strong><br />

activación <strong>de</strong>l núcleo<br />

maquina.h<br />

pruebas.h<br />

Figura A1.1. Organización <strong>de</strong> los módulos fuente<br />

fase0. c<br />

nucleo.h<br />

pruebas.h<br />

main()<br />

…<br />

ccc fase0<br />

fase0. obj<br />

li fase0<br />

fase0. exe<br />

solib. lib<br />

Figura A1.2. Esquema para compilar y montar el núcleo.<br />

UPV/EHU ATC Laboratorio <strong>de</strong> <strong>Sistema</strong>s <strong>Operativo</strong>s 16

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!