Sie sind auf Seite 1von 16

Departamento de Arquitectura y Tecnología de Computadores

Konputagailuen Arkitektura eta Teknologia Saila

_________________________________________

Laboratorio de Sistemas Operativos


_________________________________________

Núcleo de un Sistema
Operativo

Alberto Lafuente

Febrero 2006
Contenido

1 Introducción

2 Una estructura en capas para el sistema operativo

3 Estructura de un núcleo de sistema operativo

3.1 Rutinas dependientes del hardware

3.2 Rutinas de manejo de colas y auxiliares

3.3 Rutinas para la gestión de procesos

3.4 Definiciones y estructuras de datos

3.5 Primitivas del núcleo

4 Funcionamiento del núcleo

4.1 Gestión de procesos

4.2 Primitivas bloqueantes

4.3 Rutinas de tratamiento de interrupciones

4.4 Primitivas no bloqueantes

4.5 Primitivas de sincronización

5 Puesta en marcha

A.1 Prueba del núcleo

UPV/EHU ATC Laboratorio de Sistemas Operativos 2


1 Introducción
Un sistema operativo se define desde dos puntos de vista. En primer lugar, el sistema
operativo constituye la interfaz entre el usuario de un computador y los recursos de éste
(hardware y software), proporcionando una visión funcional del sistema en forma de llamadas
al sistema. En segundo lugar, el sistema operativo es el encargado de gestionar eficientemente
la utilización de los recursos por los usuarios.

Los servicios que un sistema operativo gestiona suelen dividirse en cuatro: procesador,
memoria, dispositivos y ficheros. La complejidad inherente a la gestión de alguno de estos
servicios hace necesario estructurar el sistema operativo en varias capas o niveles, cada una
ofreciendo un conjunto de primitivas a la inmediatamente superior. Por ejemplo, el sistema de
ficheros reside sobre el dispositivo disco, por lo que la gestión de ficheros se especificará en
base a las primitivas que proporcione la gestión del disco, que será la que programe el
hardware del dispositivo.

El nivel básico de un sistema operativo, que oculta las características hardware de la máquina,
se conoce como núcleo o kernel. En este documento se proporciona una descripción completa
de la estructura del núcleo de un sistema operativo multiprogramado, que incluye gestión de
procesos basada en prioridades, gestión de dispositivos (disco flexible, teclado, pantalla,
impresora, línea serie y reloj), y primitivas de sincronización (semáforos). Se ha escogido
como plataforma soporte la arquitectura PC basada en la familia i80x86. Ya que la mayor
parte de las características dependientes de la arquitectura están encapsuladas en un conjunto
de rutinas dependientes del hardware, esta elección no es especialmente determinante para el
diseño del sistema operativo. La difusión de esta arquitectura es la única razón para su
elección. Por otra parte, las rutinas dependientes del hardware proporcionan una interfaz C
para su uso en el núcleo, lo que facilita la portabilidad a otras plataformas.

2 Una estructura en capas para el sistema operativo


El esquema general propuesto es una estructura en tres niveles o capas, de abajo arriba:

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


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

2 Nivel del Sistema Básico de Ficheros, BFS. Sistema básico de ficheros: ubicación en
disco, directorios. Rutinas de E/S, servidores de dispositivos.

3 Nivel Sistema. Implementación de las llamadas al sistema; independencia del


dispositivo (tablas de canales); gestión de buffers para acceso a ficheros; carga,
ejecución y finalización de procesos.

La Figura 1 muestra la estructura en niveles de un sistema operativo. Para un nivel n sólo son
visibles las primitivas del nivel n-1. En general un nivel utilizará además una serie de rutinas
auxiliares internas que no son visibles desde el nivel superior.

3 Estructura de un núcleo de sistema operativo


Como ejemplo de núcleo de sistema operativo, en esta sección describiremos las rutinas
internas, estructuras de datos y primitivas de un núcleo multiprogramado para la arquitectura

UPV/EHU ATC Laboratorio de Sistemas Operativos 3


i80x86. Este núcleo es el que usaremos en el laboratorio. El funcionamiento del núcleo de
presentará en la siguiente sección.

Rutinas
USUARIO de
Librería

NIVEL
SISTEMA Rutinas
internas Primitivas
Nivel Sistema Nivel Sistema

NIVEL
BFS Rutinas
internas Primitivas
Nivel BFS Nivel BFS

NIVEL
NUCLEO Rutinas Primitivas
internas Nivel Núcleo
Nivel Núcleo

Rutinas
dependientes
del Hardware

HARDWARE

Figura 1. Niveles en un sistema operativo

3.1 Rutinas dependientes del hardware


Son las rutinas del núcleo de más bajo nivel. Distinguiremos dos grupos. Las más elementales
están escritas en ensamblador y tratan con direcciones específicas de E/S o de memoria (para
los dispositivos memory-mapped), o bien inhiben/activan interrupciones o manipulan
directamente la pila del programa para proporcionar los cambios de contexto. Sus prototipos
se describen en la Figura 2. El segundo grupo de rutinas internas del núcleo, que programan
los dispositivos, están codificadas en C. Estas se describen en la Figura 3.

Las rutinas dependientes del hardware se llamarán únicamente desde el núcleo. Encapsulan
las características de la arquitectura.

UPV/EHU ATC Laboratorio de Sistemas Operativos 4


RUTINAS BASICAS DEL NUCLEO
int flag inhibir ();
void desinhibir (int flag);
int in_port (int puerto);
void out_port (int puerto, int dato);
void strobe (int puerto, char mascara);
void eoi ();
void vector_int (int num_int , void (*rutina )());
int mascara_anterior enmascarar (int mascara);
void salvar_flags ();
void salvar_reg ();
void restaurar ();
void reset ();
void timer (int valor);

Figura 2. Rutinas dependientes del hardware en ensamblador

RELACIONADAS CON EL TERMINAL


char leer_teclado ();
void escribir_pantalla (int fila, int columna, char car, char atrib);
void posicionar_cursor (int fila , int columna);
void scroll (int fila_ini , int fila_fin);
int estado escribir_impresora (char car);
void beep (long tono_sonido);

RELACIONADAS CON LA LINEA SERIE


void ini_ls ();
int estado escribir_ls (char car);
char leer_ls ();
int estado consulta_ls_iir ();
int estado consulta_ls_lsr ();
int estado consulta_ls_msr ();

RELACIONADAS CON EL DISCO

void motor_on (int drive);


void motor_off ();
void posicionar_pista (int pista, int drive);
void programar_disco (int op, int cara, int pista, int sector, int drive);
int estado programar_dma (int operacion, char *buffer );
void comando_disco (int comando);
int estado comprobar_posicionado ();
int estado comprobar_trasferencia ();
int estado leer_status ();
void recalibrar (int drive);

Figura 3. Rutinas dependientes del hardware en C

3.2 Rutinas de manejo de colas y auxiliares


La mayoría se utilizan en el núcleo para el manejo de los PCBs. Fundamentalmente son
rutinas de acceso a colas, cuyos prototipos se describen en la Figura 4. Otras rutinas
auxiliares, de apoyo a la programación, se ilustran en la Figura 5. En general, las rutinas
auxiliares se utilizarán también en los niveles superiores. Por esta razón se definen las
estructuras item y cola de forma genérica, sin incluir información específica de los elementos
que enlazan (en general procesos).

UPV/EHU ATC Laboratorio de Sistemas Operativos 5


RUTINAS PARA MANEJO DE COLAS
struct item {
struct item *siguiente;
struct item *anterior;
int prioridad;
};
struct cola {
struct item *primero;
struct item *ultimo;
};
void inicializar_cola (struct cola *cola);
int vacia (struct cola *cola);
struct item *primero (struct cola *cola);
/* Elimina y devuelve el primer elemento de cola */
struct item *extraer (struct cola *cola, int prio);
/* Elimina y devuelve el primero de los de prioridad prio */
struct item *extirpar (struct cola *cola, struct item *elem);
/* Elimina y devuelve el elemento apuntado por elem */
void encolar (struct cola *cola, struct item *elem);
/* Encola elem al final de cola */
void insertar(struct cola *cola, struct item *elem, int prio);
/* Encola elem al final de los de prioridad prio */
void meter(struct cola *cola, struct item *elem, int estado);
/* Inserta elem actualizando estado en el PCB */

Figura 4. Rutinas auxiliares para manejo de colas

RUTINAS AUXILIARES DIVERSAS


void copiar (char *str1 , char *str2 , int lon);
int comparar (char *str1, char *str2 , int lon);
int buscar (char *str, char car, int lon);
void rellenar (char *str, char car, int lon);
void mayuscula (char *str, int lon);
void minuscula (char *str, int lon);
int long_a_ascii (long num, char *str, int base, int lon);
int int_a_ascii (int num, char *str, int base, int lon);
long ascii_a_long (char *str, int lon, int base);
int ascii_a_ int (char *str, int lon, int base);

Figura 5. Otras rutinas auxiliares

3.3 Rutinas para la gestión de procesos


La gestión de procesos está soportada por rutinas que manejan colas y proporcionan cambios
de estado en los procesos: bloquear(), scheduler(), dispatcher. Otras rutinas inicializan
estructuras de datos (PCB y pila) para los procesos en su creación. Estas rutinas se muestran
en la Figura 6 y se estudiarán más adelante.

RUTINAS PARA LA GESTION DE PROCESOS


void bloquear (struct cola *cola, int estado);
struct item *scheduler();
void dispatcher (struct item este_item)
void crear_pcb(void (*codigo)(), int *pila, int prio, int q, int id_proc));
void crear ();

Figura 6. Rutinas para la gestión de procesos

UPV/EHU ATC Laboratorio de Sistemas Operativos 6


3.4 Definiciones y estructuras de datos
Se refieren a la estructura del PCB y a las colas que modelan el funcionamiento del núcleo.
Aparecen en la Figura 7. Existe además una serie de definiciones de símbolos para los estados
de los procesos, dispositivos, etc, que no se muestran en la figura.

#define NUM_PCBS … /* numero maximo de PCB */


#define TAM_PILA … /* tamaño de la pila de un proceso */
struct pcb {
struct item elemento_cola;
int id_proceso;
int ax;
int dx;
int far *pila;
int quantum;
long cpu_time;
int status;
};
struct pcb proc[NUM_PCBS];
int pilas[NUM_PCBS][TAM_PILA];
struct info_proc {
int quantum;
int prioridad;
long t_cpu;
int status;
};
struct cola libres,
ready,
run,
teclado,
disco,
retardo,
lectura_ls,
escritura_ls;
int ntr = 0; /* numero de ticks de reloj */
#define NSEM … /* semáforos */

struct semaforo {
struct cola s;
int cont;
};
struct semaforo sem[NSEM];

Figura 7. Definiciones fundamentales del núcleo

Otro conjunto de definiciones (que tampoco se muestran aquí) especifican la configuración de


la máquina concreta (direcciones de pantalla, mapa de teclado, etc).

3.5 Primitivas del núcleo


Las primitivas del núcleo constituyen el conjunto de rutinas visibles desde el nivel superior.
Se definen en C y utilizan las rutinas dependientes del hardware y a las rutinas auxiliares
introducidas anteriormente. Las diferenciaremos denominándolas con el sufijo _nuc. Se
pueden dividir en los siguientes grupos:

UPV/EHU ATC Laboratorio de Sistemas Operativos 7


1 Control de dispositivos

(a) disco

void motor_on_nuc (int drive)


void motor_off_nuc ()
int posicionar_pista_nuc (int n_pista, int drive)
int leer_sector_nuc (int n_cara, int n_sector, char *p_buff)
int escribir_sector_nuc (int n_cara, int n_sector, char *p_buff)
int recalibrar_nuc (int drive)

(b) terminal

char leer_teclado_nuc ()
int escribir_pantalla_nuc (int lin, int col, char car, char atributo)
int scroll_nuc (int lin_sup, int lin_inf)

(c) línea serie

int leer_l_s_nuc ()
void escribir_l_s_nuc (char car)
void init_l_s_nuc ()

(d) impresora

int escribir_impresora_nuc (char car)

2 Control de procesos
int crear_pcb_nuc (void (*cod)(), int *pila, int prio, int quantum, int pid)
int destruir_pcb_nuc (int id_proc)
int quisoc_nuc ()
int info_proc_nuc (int id_proceso, struct info_proc *p_info)
int modif_proc_nuc (int id_proceso, struct info_proc *p_info)

3 Espera por tiempo

void retardo_nuc (int n_tics)

4 Sincronización entre procesos

int wait_nuc (int n_sem)


int signal_nuc (int n_sem)
int init_sem_nuc (int n_sem, int valor_inicial)

5 Reset

void reset_nuc ()

UPV/EHU ATC Laboratorio de Sistemas Operativos 8


4 Funcionamiento del núcleo
4.1 Gestión de procesos
Incluiremos aquí planificación, creación y destrucción de procesos. Un proceso se representa
en un PCB (struct pcb) que se identifica por un entero (índice en la tabla de PCBs).

3.4.1 Planificación de procesos

En la versión del núcleo propuesta, se proporciona planificación de procesos de tiempo


compartido y prioridades, con expulsión por desbloqueo de un proceso. Esto implica que la
gestión de procesos involucra a las rutinas de atención (muy en particular a la de reloj para
control de fin de quantum), además de las que hacen que un proceso pase a estado bloqueado
(más adelante nos referiremos a estas rutinas como bloqueantes).

Los procesos preparados para ejecución se encolan en la cola ready de acuerdo a su prioridad,
por lo que la rutina del scheduler se limita a coger el primer elemento de la cola, que pasa al
dispatcher para que lo cargue en ejecución (cola run, que tendrá un único elemento). Por
ortogonalidad, se asume la existencia de un proceso nulo, que siempre estará preparado para
ejecución o ejecutándose. La Figura 8 muestra el código de las rutinas scheduler() y el
dispatcher(). La rutina bloquear() es la encargada de sacar a un proceso de la cola, lo que
implica como efecto colateral la actualización del tiempo de CPU. Su código se muestra en la
Figura 9.

struct item *scheduler()


{
return (primero(&ready));
}

void dispatcher (este_item)


struct item *este_item;
{
meter (&run, este_item, RUN);
}

Figura 8. Rutinas scheduler y dispatcher

void bloquear (c, status)


struct cola *c;
int status;
{
((struct pcb *)run.primero)->cpu_time += ntr;
meter(c, primero(&run), status);
ntr=0;
}

Figura 9. Rutina bloquear

Una vez actualizadas la colas de procesos, la multiplexación se completa reestableciendo el


estado del procesador para que ejecute el proceso planificado. Básicamente esto implica hacer
que el SP apunte a la pila del nuevo proceso. La rutina restaurar(), codificada en
ensamblador, es la que se encarga de ello, manipulando el bloque de activación anterior.
Previamente, la rutina salvar_reg() habrá salvado el estado del proceso que estaba en
ejecución.

UPV/EHU ATC Laboratorio de Sistemas Operativos 9


La gestión del tiempo compartido la realiza la rutina de atención al reloj, multiplexar(), como
se ilustra en la Figura 10. Esta rutina incluye otras funciones (apagado automático del motor
de la unidad de disco y control del periodo transitorio tras el encendido, retardo de procesos
por tiempo), que no aparecen en la figura.

void multiplexar ()
{
salvar_reg();
/* FIN DE QUANTUM: */
if (++ntr==((struct pcb*)run.primero)->quantum) cambiar = TRUE;


if (cambiar) {
bloquear (&ready, READY);
dispatcher (scheduler());
}
eoi();
restaurar();
}

Figura 10. Rutina multiplexar. Control de quantum.

3.4.2 Creación, control y destrucción de procesos

La rutina crear_pcb_nuc() asigna un PCB para un nuevo proceso, provocando la expulsión


del que está en ejecución. Existen primitivas para extraer información de un proceso y su
identificador (info_proc_nuc(), quisoc_nuc()) y para modificar su quantum y prioridad
(modif_proc_nuc()). Finalmente, en la Figura 11 se incluye el código de
destruir_pcb_nuc(). Nótese las situaciones en que el PCB no se libera (el proceso tiene una
petición pendiente), sino que el proceso queda en estado finalizado (DEAD). La rutina de
atención correspondiente liberará el PCB del proceso DEAD con la petición pendiente.

4.2 Primitivas bloqueantes


Llamaremos rutinas bloqueantes del núcleo a las que solicitan un servicio del hardware (un
dispositivo, el reloj…) y dejan al proceso en estado bloqueado, provocando la planificación
de un nuevo proceso. Son varias las primitivas que trabajan de esta forma, en general aquéllas
cuyo servicio se atenderá mediante interrupciones, como motor_on_nuc(), retardo_nuc() —
por tiempo—; leer_teclado_nuc() —por teclado—; escribir_impresora_nuc() —por
impresora— leer_l_s_nuc, escribir_l_s_nuc() —por línea serie—; posicionar_pista_nuc(),
leer_sector_nuc(), escribir_sector_nuc(), y recalibrar_nuc() —por disco—. Como
ejemplo, en la Figura 12 se muestra el código de leer_teclado_nuc(). Todas las rutinas
bloqueantes siguen el protocolo de salvar el contexto del proceso (salvar_flags y
salvar_reg), bloquear al proceso y planificar otro proceso cargando su contexto. Nótese
también que se exige que la cola de bloqueado esté vacía: se sugiere la implementación de un
esquema cliente-servidor en el nivel superior.

UPV/EHU ATC Laboratorio de Sistemas Operativos 10


int destruir_pcb_nuc (id_proceso)
int id_proceso;
{
inhibir();
salvar_flags();
salvar_reg();
id_proceso = id_proceso % NUM_PCBS;
switch (proc[id_proceso].status) {
case LIBRE:
((struct pcb *)run.primero)->ax = -1;
break;
case READY:
proc[id_proceso].status = LIBRE;
encolar (&libres, extirpar(&ready,
&(proc[id_proceso].elemento_cola)));
((struct pcb *)run.primero)->ax = 0;
break;
case RUN:
proc[id_proceso].status = LIBRE;
encolar (&libres,primero (&run));
dispatcher (scheduler());
break;
case BLOQ_TEC :
case BLOQ_DISC:
case BLOQ_RET:
proc[id_proceso].status = DEAD;
((struct pcb *)run.primero)->ax = 0;
break;
default:
proc[id_proceso].status = LIBRE;
encolar(&libres, extirpar (&sem[proc[id_proceso].status].s,
&(proc[id_proceso].elemento_cola)));
((struct pcb *)run.primero)->ax = 0;
break;
}
restaurar();
}

Figura 11. Rutina destruir_pcb_nuc

char leer_teclado_nuc ()
{
inhibir();
salvar_flags();
salvar_reg();
if (vacia (&teclado)) {
bloquear(&teclado, BLOQ_TEC);
dispatcher (scheduler());
restaurar();
}
else crash();
}

Figura 12. Ejemplo de rutina bloqueante

4.3 Rutinas de tratamiento de interrupciones


Aunque la rutina multiplexar puede servir como ejemplo de rutina de tratamiento de
interrupciones, resultará clarificador en este momento introducir el código de la rutina de
tratamiento de las interrupciones de teclado (Figura 13) para completar el ejemplo de
tratamiento de peticiones del apartado anterior. Hay que resaltar alguna cuestión importante,
común al resto de rutinas de este tipo:

UPV/EHU ATC Laboratorio de Sistemas Operativos 11


• Tratamiento de peticiones pendientes de pocesos finalizados. La rutina de atención
detecta que el proceso está en estado DEAD (véase destruir_pcb_nuc()) y libera su
PCB.

• Valores de retorno. El carácter leído (o un código de error en otras rutinas) debe ser
devuelto a través del PCB del proceso (campo ax), que será lo que entregue como
valor de retorno la primitiva que produjo el bloqueo (leer_teclado en nuestro ejemplo)
al restaurarse el contexto del proceso (rutina restaurar()) cuando éste sea planificado
nuevamente.

void int_teclado()
{
salvar_reg();
if ((c=leer_teclado()) != -1)
if (!vacia(&teclado))
if (((struct pcb *)teclado.primero)->status == DEAD) {
((struct pcb *)teclado.primero)->status= LIBRE;
encolar(&libres, primero(&teclado));
}
else {
((struct pcb *)teclado.primero)->ax= c;
meter(&ready, primero(&teclado), READY);
bloquear(&ready, READY);
dispatcher (scheduler());
}
else beep();
eoi();
restaurar();
}

Figura 13. Ejemplo de rutina de tratamiento de interrupciones

4.4 Primitivas no bloqueantes


Llamaremos rutinas no bloqueantes a las que no dejan al proceso bloqueado. En general serán
de este tipo aquellas primitivas que no se sirven mediante interrupciones, como es el caso de
esribir_pantalla_nuc(), que se muestra en la Figura 14. Otras primitivas de este tipo son:
scroll_nuc(), motor_off_nuc(), quisoc_nuc() e info_proc_nuc(). Estas primitivas se
resuelven como simples llamadas a funciones y no producen cambios de contexto.

int escribir_pantalla_nuc (linea, columna, caracter, atributo)


int linea, columna;
char caracter, atributo;
{
if (0<=linea && linea<=24 && 0<=columna && columna<=79 &&
(atributo==UNDERSCORE || atributo==INVERSE ||
atributo==BLINK || atributo==NORMAL)) {
escribir_pantalla (linea, columna, caracter, atributo);
return (0);
}
else return (-1);
}

Figura 14. Ejemplo de rutina no bloqueante

UPV/EHU ATC Laboratorio de Sistemas Operativos 12


4.5 Sincronización entre procesos
El núcleo propuesto ofrece semáforos como mecanismo básico de sincronización entre
procesos. Un semáforo se identifica por un entero. Se introduce una nueva condición de
bloqueo para los procesos, con tantas colas como semáforos disponibles, NSEM. El bloqueo
por semáforo presenta la particularidad de que debe seguir estrictamente una disciplina FCFS,
por lo que el mecanismo de bloqueo estándar introducido para las rutinas bloqueantes no se
puede aplicar en la primitiva de espera por semáforo, wait_nuc(), descrita en la Figura 15. La
primitiva signal_nuc() se ha implementado como expulsora.

int wait_nuc (n_semaforo)


int n_semaforo;
{
inhibir();
salvar_flags();
salvar_reg();
if (0<=n_semaforo && n_semaforo<NSEM) {
((struct pcb *)run.primero)->ax = 0;
if (sem[n_semaforo].cont > 0) sem[n_semaforo].cont--;
else {
((struct pcb *)run.primero)->status = n_semaforo;
((struct pcb *)run.primero)->cpu_time += ntr;
encolar (&sem[n_semaforo].s, primero(&run));
ntr=0;
dispatcher (scheduler());
}
}
else ((struct pcb *)run.primero)->ax = -1;
restaurar();
}

Figura 15. Rutina wait_nuc

5 Puesta en marcha
Sobre el núcleo propuesto se puede construir un sistema operativo completo. Como no
disponemos de un sistema operativo completo con todas sus herramientas para soportar
aplicaciones, probaremos el funcionamiento del núcleo con un conjunto de programas de
prueba que utilicen sus primitivas y monitoricen su funcionamiento. En cualquier caso, se
requiere un programa que instale el núcleo y lance los procesos del sistema. Este programa
incluirá:

• El código completo del núcleo, que incluye primitivas, rutinas internas y librerías de
bajo nivel.

• El código de la prueba del núcleo, que es un conjunto de funciones en código C que se


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

• El procedimiento principal (main) de puesta en marcha.

El procedimiento principal realizará las siguientes acciones:

(1) Instalación de las rutinas de tratamiento de interrupciones, mediante vector_int().

(2) Inicialización de las colas del núcleo (run, ready, teclado, semáforos, …).

UPV/EHU ATC Laboratorio de Sistemas Operativos 13


(3) Inicialización de los PCBs como libres, encolándolos en la cola de libres.

(4) Creación de los procesos del sistema. Al menos son necesarios dos: el proceso nulo, y
un proceso de inicialización, que lanzará el resto ya sobre el núcleo. Estos procesos se
crean rellenando directamente los campos de los PCBs y utilizando la rutina
crear_pcb() para asignar la pila correspondiente y apuntar al código del proceso.

(5) Planificar (dispatcher y scheduler) y cargar el estado del proceso seleccionado


(mediante restaurar()). Este proceso será el de inicialización, ya que el nulo se habrá
creado con la prioridad mínima.

Ya que no disponemos de un dispositivo de arranque propio, la puesta en marcha del núcleo


requiere de un sistema operativo anfitrión, MS-DOS. El programa de prueba permite volver a
MS-DOS (al menos si no se provocan situaciones de fallo que requieran reiniciar el sistema).
Naturalmente, el núcleo puede ser lanzado desde cualquier sistema operativo Windows 95 o
98, lo que facilita el desarrollo y manejo del software, pero no desde un sistema Windows
basado en NT (2000 o XP). En este último caso, aún se puede recurrir a un emulador
(VMware o Virtual PC) que soporte MS-DOS.

UPV/EHU ATC Laboratorio de Sistemas Operativos 14


A1 Prueba del núcleo
A1.1 Compilación y montaje
Los ficheros fuente del núcleo que se proporcionan son:

rutinas.h Definiciones fundamentales del núcleo; declaraciones de rutinas auxiliares y


de manejo de colas, y declaraciones de rutinas ensamblador y C
dependientes del hardware.

maquina.h Definiciones (condicionales) dependientes de la pantalla y el teclado.

nucleo.h Primitivas del núcleo.

Para probar esta versión del núcleo, se proporciona también un programa de prueba,
pruebas.h (que hace las veces de nivel superior). pruebas.h se incluye junto a nucleo.h en un
fichero fuente fase0.c, que contiene el código para poner en marcha el sistema. Un resumen
de la organización de los módulos fuente puede verse en la Figura A1.1.

Se proporciona un comando para compilar el código:

ccc nombre_programa

Para la prueba de la versión propuesta, nombre_programa será fase0

Se proporciona otro comando para montar el código obtenido con los módulos objeto
proporcionados, de acuerdo al esquema de la Figura A1.2:

li nombre_programa

A1.2 Ejecución
Una vez compilado y montado el código, se puede probar el funcionamiento del núcleo con
ayuda del código ejecutable obtenido. Al ejecutar fase0 se ofrecen mediante un menú las
pruebas de diferentes aspectos del núcleo:

• Control de procesos. Permite manejar prioridades y quanta de procesos.

• Control del disco flexible. Permite operar el motor, posicionar el cabezal y


transferir información entre disco y memoria.

La introducción de otras opciones se realizará modificando el código de pruebas.h.

UPV/EHU ATC Laboratorio de Sistemas Operativos 15


rutinas.h nucleo.h fase0.c
#include "rutinas.h" #include "nucleo.h"
Rutinas auxiliares
Definiciones,
#include "maquina.h" #include "pruebas.h"
rutinas auxiliares,
Definiciones
etc
Rutinas ensambador
(namejo del Hw) Programas que
usan el núcleo
Código de las
primitivas del núcleo Programa de
activación del núcleo
Rutinas dependientes
de la máquina

maquina.h pruebas.h

Figura A1.1. Organización de los módulos fuente

fas e0. c fas e0. obj


nucleo.h
pruebas.h

main() ccc fase0


li fase0

fas e0. exe s olib. lib

Figura A1.2. Esquema para compilar y montar el núcleo.

UPV/EHU ATC Laboratorio de Sistemas Operativos 16

Das könnte Ihnen auch gefallen