ProyectoFC Anibal Ramirez Garcia
ProyectoFC Anibal Ramirez Garcia
ProyectoFC Anibal Ramirez Garcia
INGENIERA INFORMTICA
Diseo e implementacin
de un Sistema Operativo
para fines didcticos
AGRADECIMIENTOS
En primer lugar quiero mostrar todo mi agradecimiento a mi tutor Francisco Javier Garca Blas
por la confianza e inters que me ha prestado, pues pienso que posiblemente sin su apoyo no
hubiera podido culminar este proyecto.
Agradezco tambin igualmente a Jos Daniel Garca Snchez por sus consejos, su inters y su
apoyo, porque me ha ayudado en gran medida a seguir adelante con el proyecto.
Deseo mencionar tambin el apoyo y soporte prestados por mi compaero de trabajo Pedro Pablo Lpez Rodrguez, cuya ayuda ha sido inestimable y sin la cual este trabajo hubiera resultado
bastante ms arduo.
No quiero olvidar a mis compaeros y amigos de trabajo que me han mostrado en todo momento
su confianza, no dejndome con sus palabras de nimo, que desatendiera este trabajo.
A todos los que me he dejado sin mencionar, que sepan que tambin les tengo muy presentes y
les agradezco todo su apoyo mostrado.
Y por supuesto, quiero agradecer tambin a mi hija y mi mujer todo el nimo y apoyo que en
todo momento me han dado.
Resumen
Resumen
Actualmente podemos encontrar en el mercado una gran variedad de sistemas operativos que
satisfacen mltiples propsitos. Algunos de estos sistemas fueron diseados teniendo como uno
de sus principales objetivos el servir de herramienta de apoyo en entornos educativos, y ms precisamente, unos pocos de ellos se disearon con la finalidad bsica de servir como herramienta
de apoyo en la imparticin de la asignatura "Sistemas Operativos". No obstante, debido a la diversidad de estos entornos educativos, en algunos casos es difcil, si no imposible, encontrar un
sistema lo suficientemente satisfactorio. Para estos casos, puede estar justificado el desarrollo de
un S.O. a la medida.
Este proyecto presenta el diseo e implementacin de un sistema operativo con capacidades
bsicas, pero suficientes, como para poder ser utilizado como herramienta de apoyo a la enseanza de la asignatura "Sistemas Operativos". El proyecto cubre las necesidades especficas de
un entorno de enseanza concreto. Algunas de stas son: uso del lenguaje 'C'; un entorno de desarrollo amigable con facilidades para la edicin, compilacin y depuracin integradas; uso de
mquinas virtuales mediante software de emulacin; diseo e implementacin dando preferencia
a la sencillez frente a la eficiencia; una amplia y detallada documentacin; etc.
El sistema se ha desarrollado para una plataforma Intel x86 en modo real, pudiendo correr tanto
en hardware desnudo tipo PC compatible, como en mquina virtual. Algunas de sus caractersticas ms relevantes son: gestin de procesos con capacidad multitarea y reubicacin dinmica,
gestin de memoria con particiones de tamao variable, sistema de ficheros tipo FAT12/16, sistema de mltiples ventanas terminal tipo texto, etc.
El trabajo finaliza presentando unas conclusiones y lneas de trabajo futuro, y tambin algunas
ideas y trabajos prcticos a realizar por el estudiante sobre este sistema.
Abstract
Abstract
Today we can find on the market a variety of operating systems which serve multiple purposes.
Some of these systems were designed with the main target of being used as a teaching support
tool, and more precisely, a few of them were designed with the basic purpose of serving as a
support tool in teaching the subject "Operating Systems"; however, due to the variety of educational environments, in some cases, it is difficult if not impossible, to find a good-enough system
to be pleased. In these cases, it may be justifiable to develop a customized O.S.
This work presents the design and implementation of an operating system with basic skills but
good enough to be used as a teaching support tool for the subject: "Operating Systems". The project covers the specific needs of a particular environment teaching. Some of them are: use of "C"
language; a friendly development environment with integrated features for editing, compiling
and debugging; use of a virtual machine by means of software emulation; design and implementation preferring simplicity versus efficiency; wide and detailed documentation; etc.
The system has been developed for an Intel x86 real mode, being able to run both on bare hardware PC compatible as in a virtual machine. Some of its more relevant features are: process
management with multitasking and dynamic relocation, memory management with variable
sized partitions, FAT 12/16 file system, multiple terminal text windows, etc.
The project ends by presenting some conclusions and future lines of work, and also some ideas
and practical works to be carried out by the student on this system.
ndice
NDICE DE CONTENIDOS
1. INTRODUCCIN .................................................................................................................. 10
1.1 Motivacin. ........................................................................................................................ 11
1.2 Objetivos............................................................................................................................ 12
1.3 Definiciones y Acrnimos. ................................................................................................ 12
1.3.1 Definiciones. ............................................................................................................... 13
1.3.2 Acrnimos. .................................................................................................................. 13
1.4 Descripcin de captulos.................................................................................................... 14
2. ESTADO DE LA CUESTIN ............................................................................................... 16
2.1 Fundamentos de los Sistemas Operativos. ........................................................................ 16
2.1.1 Qu es un Sistema Operativo ..................................................................................... 16
2.1.2 Breve descripcin de los componentes de un ordenador ........................................... 18
2.1.3 Conceptos bsicos de los Sistemas Operativos .......................................................... 23
2.2 Sistemas Operativos para la enseanza. ............................................................................ 26
2.2.1 Minix ........................................................................................................................... 26
2.2.2 Linux ........................................................................................................................... 29
2.2.3 Otros sistemas operativos .......................................................................................... 32
3. GESTIN DEL PROYECTO ............................................................................................... 40
3.1 Introduccin. ...................................................................................................................... 40
3.2 Realizacin del proyecto ................................................................................................... 45
4. ANLISIS DE REQUISITOS ............................................................................................... 49
5. DISEO E IMPLEMENTACIN ........................................................................................ 53
5.1 Estudio preliminar de las herramientas de desarrollo........................................................ 53
5.2 Modelo de diseo. ............................................................................................................. 55
5.3 Principales componentes del Sistema. ............................................................................... 57
5.3.1
5.3.2
5.3.3
5.3.4
ndice
Pg. ii
Ilustraciones y Tablas
ILUSTRACIONES Y TABLAS
Tabla 1: Definiciones y Acrnimos ............................................................................................. 14
Figura 1: Etapas del inicio de una operacin de E/S ..................................................................... 22
Figura 2: Estructura de niveles de Minix ...................................................................................... 28
Figura 3: Modelo de cascada puro (ciclo de vida software) ......................................................... 42
Figura 4: Modelo cascada realimentado (ciclo de vida software) ................................................. 42
Figura 5: Modelo genrico evolutivo incremental (ciclo vida software) ...................................... 43
Figura 6: Modelo espiral (ciclo vida software) ............................................................................. 44
Figura 7: Ventana resultado ejecucin comando 'git gui'.............................................................. 46
Figura 8: Ventana resultado ejecucin comando 'gitk' .................................................................. 47
Figura 9: Ventana resultado ejecucin comando 'git log'.............................................................. 48
Figura 10: Esquema de diseo del S.O. ........................................................................................ 56
Figura 11: Diagrama de estados de un proceso ............................................................................. 62
Figura 12: Esquema de colas de recursos...................................................................................... 63
Figura 13: Transicin de un proceso entre las colas del sistema .................................................. 64
Figura 14: Esquema de cambio de contexto entre procesos .......................................................... 65
Figura 14: Esquema de cambio de contexto entre procesos .......................................................... 67
Figura 15: Pantalla con varias ventanas. ....................................................................................... 77
Figura 16: Lista "3D" de ventanas ................................................................................................ 78
Figura 17: Varios ejemplos de interseccin de segmento y ventana. ............................................ 79
Figura 18: Principales tablas y estructuras del sistema de ficheros .............................................. 83
Figura 19: Ejecucin comando ayuda ......................................................................................... 105
Figura 20: Ejemplo de compilacin de un programa de usuario ................................................. 107
Figura 21: Ejecucin de varias aplicaciones de usuario .............................................................. 109
Figura 22: Resultado de estimacin del coste por "sloccount" ................................................... 116
Figura 23: Recuento de lneas de cdigo segn UCC ................................................................. 117
Tabla 2: Valoracin subjetiva del proyecto................................................................................. 119
Pg. I
Introduccin
1. INTRODUCCIN
El presente trabajo desarrolla e implementa un sistema operativo bsico, cuyo objetivo primordial es su uso como herramienta en la enseanza de los fundamentos del diseo y construccin
de sistemas operativos para ordenadores. Este sistema tiene como alguna de sus caractersticas:
estar programado en lenguaje C, es multitarea, dispone de gestin de memoria dinmica, gestin de ficheros, operaciones de entrada/salida, mltiples ventanas de texto, etc.
Este sistema operativo est diseado para correr en un tipo de ordenador PC compatible, basado en el procesador Intel 8088/86, que aunque es ya bastante antiguo, sigue vigente como modelo usado en la enseanza, dada su relativa simplicidad y la amplia gama de herramientas y documentacin disponible, todo ello de fcil acceso. Por mencionar alguna de estas facilidades,
cabe citar la gran disponibilidad actual de acceso al software de emulacin de mquinas virtuales, como por ejemplo: DOSBox, Qemu, Virtual PC, VirtualBox, VMWare, etc. Gracias a ello,
tanto el entorno de desarrollo, como la posible realizacin de prcticas para dicho sistema, se
tornan muy asequibles, y aportan al conjunto caractersticas muy deseables e interesantes para su
uso en el entorno enseanza.
Pg. 10
Introduccin
1.1 Motivacin.
La idea de este proyecto surgi debido a la necesidad de contar con un sistema operativo lo suficientemente sencillo como para no perderse en los detalles y complejidades del software que lo
compone, y lo suficientemente capaz y verstil como para ser puesto como ejemplo de las posibilidades, y capacidades que aporta un sistema operativo a la mquina, al transformarla en una
mquina extendida de uso ms simple y transparente desde el punto de vista del usuario.
La necesidad de un sistema as en la enseanza parece natural, ya que los sistemas operativos
comerciales son enormemente complejos, y es difcil mostrar los detalles de su diseo y construccin sin tener que entrar en muchas complicaciones. Adems el acceso al cdigo fuente de
los mismos no suele estar muchas veces disponible, aunque es cierto, que en la actualidad existe
bastante software abierto que podra utilizarse a tal efecto, si obviamos tanto la complejidad del
mismo como la necesidad de tener que estudiarlo en profundidad para su adecuada exposicin
en clase.
Como se ha comentado anteriormente, existen ya algunos sistemas no muy complejos que estn
dedicados principalmente a la enseanza. Un ejemplo claro de ello lo constituye el sistema operativo Minix, al menos en sus primeras versiones. Dicho sistema no es demasiado complejo, y
existe bastante informacin sobre el mismo como para ser un buen candidato a ser utilizado como herramienta de apoyo en la enseanza de los sistemas operativos. De hecho, el autor de este
Proyecto Fin de Carrera lo ha utilizado para ese propsito durante bastante tiempo. No obstante,
desde cierto punto de vista, adolece todava del inconveniente principal, su complejidad. La realizacin de trabajos prcticos requiere un extenso conocimiento del mismo y se hace difcil pedir
al alumno que realice modificaciones y mejoras sobre el cdigo, pues como puede suponerse,
pedir una modificacin suele requerir el conocimiento previo de lo ya existente, y eso es algo
que muchas veces no es trivial. Otro inconveniente que se ha encontrado es que las herramientas
y tiles de desarrollo, tales como el compilador, editor, depurador, etc. suelen ser bastante complejas, constituyendo una barrera ms a sortear por parte de alumno, para la adquisicin de nuevo
conocimiento sobre el sistema operativo. Todo esto es bastante accesorio, por lo que el alumno
no debera tener que emplear demasiado tiempo en su aprendizaje, para as poder dedicarse plenamente al aprendizaje de los conceptos fundamentales.
Pg. 11
Introduccin
Una vez asumidas las limitaciones que impone el uso de un sistema como Minix, y haber
descartado otros sistemas de uso didctico por razones ms o menos similares, se consider interesante y deseable, desarrollar como proyecto fin de carrera un Sistema Operativo propio. Se
podra hacer a la medida de las necesidades especficas para la enseanza que se haban encontrado, y podra darle un uso prctico real. No debera sufrir de los inconvenientes ya mencionados y podra servir de plataforma para una futura ampliacin. Adems al aportar suficiente informacin tcnica, sera posible su utilizacin por parte de otros educadores, sin que ello tuviera
que requerir un gran esfuerzo en su aprendizaje.
1.2 Objetivos.
El objetivo fundamental de este Proyecto Fin de Carrera es conseguir que el estudiante disponga
de una herramienta til y adecuada, la cual le facilite el aprendizaje de los fundamentos del diseo y construccin de los sistemas operativos.
Para llevar a cabo este objetivo, se disear e implementar un sistema operativo sencillo que
pueda correr mediante software de virtualizacin tipo DOSBox, Qemu, etc. y que est desarrollado en un entorno que permita efectuar las operaciones de compilacin, edicin y depuracin
de forma integrada, todo ello en aras a facilitar al estudiante las tareas de modificacin y ampliacin de las funcionalidades del sistema, facilitando as una comprensin ms amplia del mismo.
Dicho sistema se completar, en la medida de lo posible, con la elaboracin de unos trabajos
prcticos basados en dicho sistema, con objeto de que los realice el estudiante, para que de este
modo se faciliten, mejoren y amplen los conocimientos del estudiante sobre los conceptos fundamentales en los que se basa el diseo y la construccin de los sistemas operativos.
Pg. 12
Introduccin
1.3.1
Definiciones.
1.3.2
Acrnimos.
Acrnimo
Significado
MS-DOS
UNIX
POSIX
FAT
Pg. 13
Introduccin
SCO
BSD
RAE
GNU
RAM
ROM
EEPROM
BIOS
CMOS
MMU
DMA
GUI
IDE
USB
DLL
COCOMO
PYMES
RTI
En este punto se presenta de forma resumida el contenido de cada uno de los captulos que forman este trabajo.
En el Captulo 1, Introduccin se presenta este trabajo mostrando de forma resumida en qu
consiste y cules son sus objetivos.
En el Captulo 2, Estado de la cuestin se hace una rpida revisin de los fundamentos de los
sistemas operativos, y se comentan las caractersticas ms relevantes de algunos sistemas operaDiseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 14
Introduccin
tivos prximos o relacionados con los objetivos de este proyecto. Especialmente de aquellos que
se han desarrollado principalmente como herramienta de apoyo a la enseanza de los principios
bsicos de diseo de estos sistemas.
En el Captulo 3, "Gestin de proyecto" se comenta la metodologa seguida para la realizacin
del proyecto, as como el software utilizado para el control de versiones.
En el Captulo 4, "Objetivos" se sealan los objetivos bsicos del proyecto as como tambin los
objetivos secundarios.
El Captulo 5, "Memoria del trabajo" se realiza una exposicin relativamente detallada del diseo e implementacin del sistema operativo, as como detalles de funcionamiento, instalacin,
requisitos, etc.
El Captulo 6, "Resultados", Se muestran algunas de las mltiples pruebas realizadas y resultados
obtenidos por los comandos internos y externos del sistema, as como tambin algunos programas de usuario y trabajos prcticos a realizar por el alumno.
El Captulo 7, "Conclusiones", se exponen las conclusiones obtenidas al final del desarrollo del
proyecto, as como futuras lneas de desarrollo y ampliacin y una valoracin o presupuesto del
proyecto.
El Captulo 8, "Referencias", relaciona la bibliografa de consulta y enlaces URL a pginas web
de inters para el proyecto.
El Captulo 9: "Anexos", detalla el contenido del CD que se adjunta con esta memoria.
Pg. 15
Estado de la cuestin
2. ESTADO DE LA CUESTIN
En este apartado se muestra en principio una visin general de los fundamentos de los sistemas
operativos, y seguidamente se detallan las caractersticas ms relevantes de algunos de ellos.
Especialmente se profundizar en aquellos que se han utilizado principalmente como herramienta de apoyo a la enseanza de los principios bsicos de diseo de estos sistemas.
2.1.1
Qu es un Sistema Operativo
Segn el diccionario de la RAE, el Sistema Operativo podra definirse as: Programa o conjunto de programas que efectan la gestin de los procesos bsicos de un sistema informtico, y
permite la normal ejecucin del resto de las operaciones. No obstante an dada esta breve y
concisa definicin, resulta difcil saber exactamente qu es lo que consideramos un Sistema Operativo. Ello es as porque entre otras cosas el Sistema Operativo realiza bsicamente dos funciones no relacionadas. Por una parte extiende la mquina real, ampliando sus posibilidades y ocultando su complejidad, y por otra, realiza una gestin de los recursos de la misma [TAWO98].
Pg. 16
Estado de la cuestin
Para entender mejor cmo se extiende una mquina real pensemos por ejemplo cmo hay que
programar una operacin de entrada y salida sobre un disquete en una mquina desnuda. Si estudiamos la documentacin de la controladora de disco de un ordenador tpico de sobremesa, veremos que sta encierra una gran complejidad. Para llevar a cabo una simple operacin elemental
de lectura o escritura, hay que programar bastantes instrucciones en cdigo mquina, y con todo
ello slo habramos resuelto una pequea fraccin del problema. La visin fsica del disco la
componen pistas, cabezas y sectores, y tanto escribir informacin, como su recuperacin, acarreara llevar el control de dnde se encuentra sta. Un concepto tan familiar hoy da, como lo es
la abstraccin fichero, es algo que no se encuentra a nivel mquina, y por tanto no es posible
realizar una operacin tan bsica como es leer o escribir una determinada informacin en el fichero, sin tener que ser consciente de los innumerables detalles que exige el nivel de programacin de lenguaje mquina. Esta abstraccin y otras muchas, es algo que nos ofrece el Sistema
Operativo. Nos presenta una mquina extendida o virtual que dispone de unas caractersticas
mejoradas, con las que podemos trabajar de una forma mucho ms cmoda y sencilla. En resumen, el sistema operativo ofrece al programador una gran variedad de servicios, que se pueden
obtener mediante el uso de lo que denominamos comnmente, llamadas al sistema.
Por otro lado, tambin es posible contemplar al sistema operativo, fundamentalmente como un
administrador de un sistema con un cierto grado de complejidad, lo que corrientemente conocemos como ordenador, el cual generalmente est compuesto de procesadores, memorias, discos, temporizadores, ratones, conexiones de red, impresoras, y una gran variedad de otros dispositivos. La administracin consistira en asegurar el reparto equilibrado de estos recursos entre los
diferentes procesos que se ejecutan en el sistema. Se hace necesario proteger la memoria, los
dispositivos de entrada/salida, y muchos otros recursos de un acceso indiscriminado, sobre todo
para evitar conflictos entre procesos (y usuarios, si el sistema operativo fuera multiusuario).
Otra posible forma de administracin puede consistir en la multiplexacin de un recurso en el
tiempo o en el espacio, por ejemplo el procesador puede ser asignado alternativamente a diferentes procesos en intervalos de tiempo de una determinada duracin, para as conseguir que los
procesos se vayan ejecutando de un modo pseudo-concurrente. Otro ejemplo de multiplexacin
en tiempo puede ser el uso de una impresora compartida. Cuando se encolan varios trabajos en
una impresora han de tomarse decisiones de qu trabajo se imprime primero y cul despus.
Pg. 17
Estado de la cuestin
2.1.2
El sistema operativo debe conocer a fondo los componentes fsicos del ordenador donde se va a
ejecutar (hardware), para as poder presentar al programador una mquina extendida.
De forma general y algo simple podemos describir un ordenador personal como una serie de dispositivos conectados todos ellos entre s mediante un bus de comunicacin. En realidad la estructura es bastante ms compleja, ya que hay mltiples buses, pero de momento como modelo
es suficiente. A continuacin se va a detallar brevemente los componentes ms relevantes de cara
al desarrollo de un sistema operativo [TANE09]:
El Procesador: Es el ncleo principal del ordenador. Se encarga de extraer instrucciones de la
memoria y ejecutarlas. El ciclo bsico de ejecucin de un programa consiste en extraer una instruccin, decodificarla para determinar su tipo y operandos, ejecutarla, y seguidamente obtener la
siguiente instruccin para repetir el ciclo.
El procesador tiene un conjunto de registros generales donde guardar resultados intermedios y
variables. Dentro del conjunto de instrucciones del procesador, las hay que cargan el contenido
de una palabra de memoria en un registro y viceversa, otras realizan operaciones aritmticas con
dichos registros o posiciones de memoria, evalan condiciones, bifurcan el flujo de ejecucin,
etc.
Adems de esos registros, el procesador dispone de otros registros especiales. Uno de ellos es el
contador de programa, que contiene la direccin de la siguiente instruccin del programa, otro
registro es el puntero de pila, el cual seala la cima de la pila actual en memoria. En la cima se
encuentra un conjunto dado de variables locales y parmetros asociados a un procedimiento. Por
ltimo otro registro especial muy importante es el registro de estado del programa, el cual
Pg. 18
Estado de la cuestin
guarda un conjunto bits que reflejan el estado y condicin de ciertas operaciones efectuadas, como por ejemplo las de comparacin. Tambin pueden guardar el estado del permiso de interrupciones, etc.
Para realizar una llamada al sistema, el programa de usuario ejecuta una instruccin especial que
irrumpe en el ncleo del sistema operativo invocando el servici asociado a la llamada. Esta instruccin especial conmuta el nivel de privilegio del modo usuario al modo supervisor, siempre y
cuando el procesador dispusiera de esta caracterstica, ya que por ejemplo el procesador Intel
8086 no la tiene.
La memoria: Es el segundo componente ms importante en un ordenador. Como cualidades
deseables de la memoria tenemos que; debera ser lo ms rpida posible, existir en abundancia y
de un coste lo ms bajo posible. No hay tecnologa que satisfaga todos estos requisitos simultneamente y se ha de optar por una solucin de compromiso, estableciendo jerarquas de memorias, atendiendo a su velocidad, tamao y coste. La memoria ms rpida la forman los registros
del procesador, y suele ser de un tamao inferior a 1KB. Seguidamente tenemos la memoria
cach, controlada por el hardware. Se usa como almacenamiento intermedio entre el procesador y la memoria de siguiente nivel, o memoria principal. Cuando se necesita un dato se busca
primero en esta memoria y si se encuentra el dato no se requiere entonces el acceso al siguiente
nivel de memoria, que es ms lento. La cach puede tener ms de un nivel, siendo sucesivamente
los niveles superiores ms rpidos que los inferiores. En el siguiente nivel tenemos la memoria
principal o RAM (Random Access Memory), es ms lenta y ms barata y lo suele ser persistente.
Los siguientes niveles lo componen dispositivos tales como los discos en primer lugar y cintas a
continuacin. Son memorias de gran tamao, ms econmicas y de velocidad considerablemente
menor. En estas memorias se suele soportar el almacenamiento del sistema de ficheros.
En el ordenador tambin suele haber un tipo de memorias conocidas como EEPROM y flash
RAM, que son persistentes, aunque en contraste con las memorias ROM (Read Only Memory)
pueden ser escritas ms de una vez, y almacenan cdigo y datos de rutinas bsicas de manejo del
hardware, como por ejemplo en el ordenador personal, la BIOS (Basic Input Output System).
Otro tipo es la memoria CMOS, que es voltil, pero de muy bajo consumo. El ordenador suele
mantener en ella la fecha y hora y la configuracin del equipo, alimentada por una pequea batera.
Pg. 19
Estado de la cuestin
Con relacin a los procesos y el uso de la memoria principal hay que precisar que normalmente
es deseable guardar ms de un programa en memoria, porque si un proceso se bloquea en espera
de E/S, el procesador puede proseguir con la ejecucin de otro proceso, aumentando con ello el
rendimiento del procesador. No obstante en este caso, habra que resolver dos problemas:
1. Proteger la memoria del acceso indiscriminado de los procesos.
2. Manejar la reubicacin del cdigo y datos de los procesos.
Existen muchas posibles soluciones, pero todas ellas requieren que el procesador posea determinadas caractersticas hardware.
El primer problema es obvio, pero el segundo no lo es tanto. Cuando se compila y enlaza un programa, el compilador normalmente no sabe en qu direccin se cargar el programa, por ello,
normalmente se asume como direccin de comienzo la 0. Supongamos que la primera instruccin carga una palabra de la direccin 1000, y que el programa se encuentra cargado en la direccin 2000. Sin reubicacin, no se ejecutara correctamente esta instruccin, porque cargara la
palabra de la direccin 1000, en vez de en la direccin 3000, que es de donde debera cargarla.
Es necesario efectuar un proceso de reubicacin del programa. La reubicacin se puede hacer
durante la carga, modificando todas las direcciones, lo que es costoso, o bien al vuelo, en
tiempo de ejecucin, mediante el empleo de registros base en el procesador para formar las direcciones (tambin puede haber registros lmite para proteccin). El proceso de mapeo resultante
de convertir una direccin generada por el programa, llamada direccin virtual, en una direccin de memoria llamada fsica, lo realiza una parte del procesador llamada MMU (Memory
Management Unit).
Los dispositivos de entrada/salida (E/S): Normalmente constan de dos partes, la controladora y
el propio dispositivo. En la mayora de los casos el control del dispositivo es bastante complejo,
y el trabajo de la controladora es presentar una interfaz ms sencilla al sistema operativo.
Cada tipo de controladora requiere un software diferente. El software que maneja la controladora
se le conoce comnmente en ingls como device driver, trmino bastante conocido, aunque
normalmente se le llama simplemente driver, como por ejemplo: driver de la tarjeta de video, o driver de la tarjeta de red, etc.
Pg. 20
Estado de la cuestin
El driver normalmente corre dentro del kernel en modo privilegiado, aunque tericamente
puede correr fuera del kernel, son pocos los sistemas que optan por ello porque se requiere la
habilidad de acceder al dispositivo de un modo controlado desde el espacio de usuario del driver.
Normalmente hay tres formas de aadir un driver al sistema operativo. La primera consiste en
compilar y enlazar el software del driver junto con el sistema operativo, tal que al arrancar
el sistema, el driver forma parte del mismo. Muchos sistemas tipo UNIX funcionan as. La
segunda forma consiste en crear una entrada en un fichero del sistema indicando que se requiere
un driver y reiniciar. Durante el arranque el sistema operativo examina dicho fichero y cargue
en ese momento el driver. El sistema MS-DOS por ejemplo funciona as. La tercera y ltima
es ms sofisticada y menos corriente. Consiste en que el sistema operativo es capaz de instalar en
cualquier momento un driver sin necesidad de re arrancar el sistema. Esta ltima forma cada
vez es ms corriente, y las ltimas versiones de los sistemas operativos ms populares ya la
adoptan.
Una controladora suele disponer de un pequeo conjunto de registros que se usan para comunicarse con ella, por ejemplo para especificar direcciones, bien de memoria, de disco, etc. El driver recibe un comando del sistema operativo y lo traduce en los valores adecuados para escribir
en estos registros. En algunos sistemas estos registros forman parte del espacio de direcciones de
memoria, usando por tanto instrucciones de acceso a memoria tambin para este propsito. Otros
en cambio, tienen un espacio de entrada/salida aparte, con instrucciones especficas como IN y
OUT que requieren el modo de ejecucin privilegiado. Ambos sistemas son ampliamente usados.
La entrada/salida se puede hacer de tres formas distintas. En el mtodo ms simple el proceso
realiza una llamada al sistema, el cual a su vez invoca al driver. El driver inicia la entrada/salida y permanece en un bucle de examen continuo del estado de finalizacin del dispositivo.
Cuando el dispositivo cambia su estado indicando que ha acabado, el driver sale del bucle y
retorna el resultado al sistema operativo el cual a su vez lo devuelve al proceso. Este mtodo se
llama espera activa y tiene la desventaja de que tiene ocupado al procesador en el bucle durante el tiempo que dure la operacin de entrada/salida.
En el segundo mtodo, el driver inicia el dispositivo pidindole que emita una interrupcin
cuando finalice. En este punto el driver retorna al sistema operativo, el cual bloquea al proceso
Pg. 21
Estado de la cuestin
que hizo la peticin y se pone a hacer cualquier otro trabajo pendiente. Cuando la controladora
detecta el fin de la operacin, emite una interrupcin que hace que el sistema operativo recupere
el resultado de la misma, y lo entregue al proceso que la solicit, desbloqueando.
1
Procesador
Controladora
de interrupciones
Controladora
de disco
2
Pg. 22
Estado de la cuestin
2.1.3
En prcticamente todos los sistemas operativos podemos encontrar ciertos conceptos o abstracciones tales como: Procesos, memoria, entrada/salida, ficheros, etc. [TANE09], [CARR01].
Procesos: Un proceso es bsicamente un programa en ejecucin. Los procesos tienen asociados
un espacio de direcciones, formado por un conjunto de posiciones de memoria donde el proceso
puede leer o escribir. En dicho espacio se encuentra el programa ejecutable, los datos y la pila.
Tambin hay asociados al proceso un conjunto de registros, tales como el contador de programa,
el de pila, etc., as como el resto de informacin necesaria para ejecutar el programa.
Cuando se suspende temporalmente la ejecucin de un proceso, para posteriormente reanudarse
en el mismo punto donde se suspendi, el S.O. tiene que guardar toda la informacin necesaria
en algn sitio. En muchos sistemas operativos dicha informacin se guarda, salvo el contenido
de su espacio de direcciones, en la tabla de procesos. Los elementos de dicha tabla (que podra
ser una lista u otro tipo de estructura) se conocen generalmente como descriptores de proceso.
Las llamadas al sistema relativas a procesos primordiales son las que se encargan de la creacin
y terminacin de procesos. Cuando un proceso crea otro proceso, generalmente se le conoce como proceso hijo. Si a su vez ste crea otro proceso y as sucesivamente se obtiene una jerarqua
de procesos en rbol. A menudo varios procesos relacionados pueden trabajar cooperativamente
en algn trabajo y necesitan comunicarse para sincronizarse. El S.O. dispone para ello de un servicio de comunicacin de procesos. Otras llamadas al sistema solicitan o liberan memoria, esperan por la terminacin de un proceso, cambian la imagen de memoria del proceso, etc.
Una herramienta muy til la constituye las seales. Por ejemplo el S.O. puede enviar una seal
de alarma a un proceso cuando ha transcurrido un cierto lapso de tiempo. La seal hace que el
proceso suspenda temporalmente lo que est haciendo y se ejecute un procedimiento especfico
de tratamiento de seal. Cuando este tratamiento finaliza, el proceso reanuda lo que estaba
haciendo, justo a continuacin del momento en que fue suspendido. Las seales son el anlogo a
las interrupciones hardware. stas pueden ser generadas por mltiples causas. Por ejemplo, si
Pg. 23
Estado de la cuestin
se produce una excepcin por una direccin invlida, el S.O. podra enviar una seal al proceso
que la provoc.
A los usuarios de un sistema normalmente se les suele asignar un identificador (UID), igualmente sucede con los procesos (PID). El sistema sabe que usuario ejecut cada proceso gracias a
estos identificadores. Con ello se puede evitar que un proceso lleve a cabo funciones que no le
son permitidas.
Cuando dos o ms procesos interactan se puede dar el caso que se produzcan interbloqueos.
Una situacin en la cual ningn proceso puede avanzar por estar bloqueado por el otro. Esta
situacin es similar al caso de la vida real en la que varios vehculos se aproximan a un cruce y
se detienen justo en la interseccin para verificar si pueden cruzar, bloqueando el paso a otros
vehculos. No cruzan porque otro vehculo les bloquea el paso y esto mismo sucede con todos los
dems, quedando todos ellos bloqueados. Para evitar estas situaciones existen herramientas ofrecidas por el S.O., tales como los semforos, buzones, etc.
Gestin de la memoria: Un S.O. que se precie puede alojar en memoria ms de un proceso.
Esto significa que debe haber algn mecanismo de proteccin para evitar que los procesos interfieran entre ellos. Este mecanismo lo aporta el hardware pero lo controla el S.O. Por otro lado,
otro concepto no menos importante relacionado con la gestin de memoria consiste en el manejo
del espacio de direcciones de los procesos. Normalmente un proceso tiene un conjunto de direcciones mximo. En el caso ms simple, todo el espacio de direcciones de un proceso cabe en la
memoria principal disponible. Sin embargo, Qu pasara si un proceso tuviera un espacio de
direcciones mayor que la memoria disponible y quisiera escribir en todo el espacio? En los sistemas antiguos eso no era posible, pero en la actualidad, una tcnica llamada memoria virtual,
nos lo permite. Con esta tcnica el S.O. gestiona el espacio, manteniendo parte del mismo en
memoria principal y parte en disco, trayendo y llevando bloques de memoria desde un soporte
fsico al otro segn sea necesario.
Entrada/Salida: En todos los ordenadores existen dispositivos fsicos para poder realizar operaciones de entrada o salida de datos. Despus de todo, De qu servira tener la posibilidad de
hacer un buen trabajo si el usuario no pudiera indicar qu hacer, o recibir el resultado? Hay muchos tipos de dispositivos: teclado, pantalla, impresora, ratn, etc., y es responsabilidad del S.O.
su gestin. Una parte del software encargado del mismo puede ser independiente del dispositi-
Pg. 24
Estado de la cuestin
vo, y por tanto ser el mismo para todos ellos y otra parte puede ser especfica para cada dispositivo concreto.
Ficheros: Otro concepto clave es el sistema de ficheros. Tal y como ya se ha dicho, una parte
importante del S.O. es ocultar las particularidades de los discos y otros dispositivos, presentado
al programador un modelo abstracto limpio y agradable de ficheros independientes del dispositivo. Se necesitan servicios para crear, borrar, leer y escribir ficheros.
La mayora de los sistemas operativos utilizan un concepto llamado directorio, como una forma alojar ficheros y mantenerlos agrupados. Los directorios pueden formar una jerarqua en
rbol, y aunque esto sucede tambin con los procesos, la similitud acaba aqu. La profundidad o
niveles en los procesos suele ser corta, al contrario que con los ficheros donde son comunes niveles de profundidad de 4, 5 o ms. La jerarqua en los procesos es de vida corta, al contrario que
en ficheros. Tambin es bastante diferente la gestin de la propiedad y la proteccin.
Todo fichero dentro de la jerarqua puede ser especificado por su ruta (pathname). sta puede
ser especificada desde la raz, por ejemplo: \usrs\libros\curso\milibro.doc, o bien especificarse
desde el directorio de trabajo. Por ejemplo: curso\milibro.doc, donde el directorio de trabajo
sera \usrs\libros. Todo proceso tiene asociado por tanto un directorio de trabajo y es posible
mediante una llamada al sistema cambiarlo.
Un concepto muy importante en sistemas operativos tipo UNIX es el de fichero especial. Este
concepto sirve para poder trabajar con dispositivos como si fueran ficheros, de este modo, stos
pueden ser ledos y escritos usando los mismos servicios que el resto de ficheros. Existen dos
tipos de ficheros: orientados a bloque, y orientados a carcter. Los orientados a bloque se usan
para modelizar dispositivos consistentes en un conjunto de bloques con forma de acceso directo,
tales como los discos. Los orientados a caracteres se usan para modelizar impresoras, mdems, y
otros dispositivos que aceptan una corriente de caracteres como entrada o salida.
Pg. 25
Estado de la cuestin
En este apartado se mostrarn las caractersticas ms importantes de algunos de los sistemas operativos existentes en la actualidad, que por su relativa sencillez se han venido utilizando como
modelo y herramienta para prcticas en la enseanza de sistemas operativos.
2.2.1
Minix
Minix es un sistema operativo de cdigo abierto, diseado para ser muy fiable, flexible y seguro.
Este sistema operativo tipo UNIX fue desarrollado por Andrew S. Tanenbaum en 1987, el cual
tambin distribuy su cdigo fuente. Fue creado principalmente para la enseanza del diseo de
sistemas operativos, ya que el sistema Unix estaba bajo restricciones de licencia de AT&T y era
demasiado complejo. Gracias a su pequeo tamao, su diseo basado en el concepto de micro
ncleo y la amplia documentacin disponible, resulta bastante adecuado para todo aquel que
desee un sistema Unix para su PC, as como tambin para aprender sobre su funcionamiento interno.
La versin inicial, Minix 1, era compatible a nivel de llamadas con la 7 edicin de Unix. Fue
desarrollada para correr sobre un PC con procesador 8088/86, aunque versiones posteriores fueron ampliando sus posibilidades al correr sobre procesadores ms potentes como el 80286,
80386, etc. Junto con el sistema operativo, Tanenbaum & Woodhill publicaron tambin el libro
Sistemas Operativos: Diseo e Implementacin [TAWO98], en el que se recoga una parte del
cdigo en C del ncleo, el gestor de memoria y el gestor de ficheros (pg. web no oficial:
http://minix1.woodhull.com/index1.html).
En 1991 se lanz la versin Minix 1.5, con soporte para micro-channel y arquitecturas Motorola
68000 y SPARC, aunque tambin hubo versiones no oficiales para el i386 (en modo protegido),
NS32532 de National Semiconductor, ARM e INMOS transputers. Meiko Scientific us una versin previa de Minix en su sistema operativo MeikOS, para sus ordenadores paralelos de
cmputo superficial basados en transputers. Tambin se cre un simulador llamado SMX que
corra como un proceso de usuarios sobre los sistemas operativos SunOS y Solaris.
Posteriormente, en 1997 se lanz Minix 2 que era solo compatible con arquitecturas x86 y
SPARC, dada la cada de las arquitecturas 68000. Con esta versin se lanz la segunda edicin
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 26
Estado de la cuestin
del libro de Tanenbaum. En esta versin se aadi compatibilidad con POSIX1, soporte 32 bits
(I386 y superiores) y protocolo TCP/IP que reemplazo al de Amoeba de Minix 1.5. Ms tarde
dos investigadores de la universidad Vrije de msterdam realizaron la versin Minix-vmd para
procesadores compatibles IA-32, a la que dotaron de memoria virtual y soporte para XWindows. En general, Minix 2 puede ser una buena opcin para alguien que desee entender casi
todos los elementos del sistema operativo con slo algunos meses de estudio. (pg. web no oficial: http://minix1.woodhull.com).
Finalmente, en Octubre de 2005, en la conferencia del ACM Symposium Operating Systems
Principles, Andrew Tanenbaum anunci Minix 3, que aunque sigue mantenindose como
ejemplo en su libro, se redise para ser utilizado como un sistema serio para PCs con recursos
limitados y aplicaciones que requieran una gran fiabilidad. Esta versin est disponible en forma
de LiveCD, lo que permite ser utilizado sin necesidad de instalacin previa. Tambin puede
ser usado en sistemas de virtualizacin como BOCHS, Qemu, VirtualBox. Etc. La ltima versin
en la actualidad es la 3.1.8, la cual se encuentra disponible en la pgina oficial www.minix3.org.
Por ser muy extensa la documentacin en la red sobre este sistema, slo se muestran slo algunos de los enlaces donde se puede obtener ms informacin.
http://minix1.woodhull.com. (soporte para Minix 2). Desde esta pgina se pueden obtener
otros muchos enlaces tales como:
http://minix1.woodhull.com/hints.html. Informacin variada sobre Minix.
http://minix1.woodhull.com/mxdownld.html Descarga de Minix 2.
http://minix1.woodhull.com/contrib.html Paquetes de software (utils..).
http://minix1.woodhull.com/mxinet.html. Minix como servidor ftp, http, etc.
http://minix1.woodhull.com/docs.html. Documentacin sobre Minix 2.
http://minix1.woodhull.com/teaching. Recursos para la enseanza de Minix
http://minix1.woodhull.com/others.html. Otras versiones de Minix (Minix-vmd.)
http://minix1.woodhull.com/links.html. Otras fuentes de informacin.
http://www.dmoz.org/Computers/Software/Operating_Systems/Unix/MINIX.
http://sopa.dis.ulpgc.es/ii-dso/lecminix/lecminix.htm.
Las caractersticas ms relevantes de Minix 2 son:
Pg. 27
Estado de la cuestin
Pg. 28
Estado de la cuestin
Nivel 0: Atender Interrupciones, planificar tareas y procesos y manejo y validacin del paso de
mensajes.
Nivel 1: En el corren las tareas de entrada salida (manejadores de dispositivo) y la tarea del sistema (tiene funciones especiales no asociadas a ningn dispositivo). Tanto las tareas de
nivel 0, como las del nivel 1, forman un nico ejecutable (kernel), el cual corre en modo
supervisor.
Nivel 2: En el corren los procesos servidores (gestores) de Minix. El Gestor de memoria, el Gestor de ficheros y el Gestor de red. Tiene un nivel de privilegio menor que las tareas pero
mayor que los procesos de usuario. El Gestor de Memoria atiende todas las llamadas relacionadas con procesos y memoria: fork, exec, brk, etc. El Gestor de ficheros, atiende
las de ficheros: open, read, etc.
Nivel 3: En este nivel corren los procesos init e interprete de comandos (Shell), as como el
resto de aplicaciones de usuario con el nivel de privilegio ms bajo.
2.2.2
Linux
Estado de la cuestin
operativos, no obstante, y debido a su complejidad, podra no serlo tanto en algunas otras, sobre
todo en aquellas cuyos conceptos esenciales requieren ser contados con la mxima sencillez.
Linux aparece al principio de los 90. Un estudiante de informtica llamado Linux Torvalds comenz, como una aficin, a programar las primeras lneas de cdigo de lo que llegara a ser este
sistema. Se inspir bsicamente en el proyecto MINIX de Andrew S. Tanenbaum y al principio
el foro de discusin principal era para usuarios que deseaban algo ms de lo ofrecido por
MINIX.
La primera versin oficial fue la 0.02. En ella se poda ejecutar Bash (interprete de comandos
Bourne) y gcc (compilador GNU de C), pero no mucho ms. Despus de la versin 0.03 se
salt a la 0.10 y empezaron a trabajar ms y ms personas en el proyecto. En marzo del 92 la
versin era la 0.95 y desde entonces no se ha parado de desarrollar este proyecto
A continuacin se hace un resumen algo ms extenso de sus caractersticas:
Multitarea: La palabra multitarea describe la habilidad de ejecutar varios programas al mismo tiempo. LINUX utiliza la llamada multitarea expulsora, la cual asegura que todos los
programas que se estn utilizando en un momento dado sern ejecutados, siendo el sistema
operativo el encargado de ceder tiempo de microprocesador a cada programa.
Multiplataforma: Las plataformas en las que en un principio se puede utilizar Linux son i386,
i486. Pentium (toda la familia y clones), Amiga y Atari, tambin existen versiones para su
utilizacin en otras plataformas, como Alpha, ARM, MIPS, PowerPC y SPARC.
Carga de ejecutables por demanda: Linux slo lee del disco aquellas partes de un programa
que estn siendo usadas actualmente.
Poltica de copia en escritura para la comparticin de pginas entre ejecutables: esto significa
que varios procesos pueden usar la misma zona de memoria para ejecutarse. Cuando alguno
intenta escribir en esa memoria, la pgina (4Kb de memoria) se copia a otro lugar. Esta poltica de copia en escritura tiene dos beneficios: aumenta la velocidad, y reduce el uso de memoria.
Pg. 30
Estado de la cuestin
Memoria virtual usando paginacin (sin intercambio de procesos completos) a disco o a una
particin y/o archivo; con la posibilidad de aadir ms reas de intercambio sobre la marcha.
Se pueden usar hasta un total de 16 zonas de intercambio de 128Mb de tamao mximo en
un momento dado con un lmite terico de 2Gb para intercambio. Este lmite se puede aumentar fcilmente con el cambio de unas cuantas lneas en el cdigo fuente.
La memoria se gestiona como un recurso unificado para los programas de usuario y para el
cach de disco, de tal forma que toda la memoria libre puede ser usada para cach y sta
puede a su vez ser reducida cuando se ejecuten grandes programas.
Se realizan volcados de estado (core dumps) para posibilitar los anlisis post-mortem, permitiendo el uso de depuradores sobre los programas no slo en ejecucin sino tambin tras
abortar stos por cualquier motivo.
Emulacin de iBCS2, casi completamente compatible con SCO, SVR3 y SVR4 a nivel binario.
Todo el cdigo fuente est disponible, incluyendo el ncleo completo y los drivers, las
herramientas de desarrollo y programas de usuario; adems todo ello se puede distribuir libremente, aunque existen algunos programas comerciales de los que no se ofrece libremente
el cdigo fuente.
Pseudo-terminales (pty's).
Emulacin de 387 en el ncleo, de tal forma que los programas no tengan que hacer su propia emulacin matemtica. Cualquier mquina que ejecute Linux parecer dotada de coprocesador matemtico. Por supuesto, si el ordenador ya tiene una FPU (unidad de coma flotante), esta ser usada en lugar de la emulacin, pudiendo incluso compilar tu propio kernel sin
la emulacin matemtica y conseguir un pequeo ahorro de memoria.
Soporte para muchos teclados nacionales o adaptados y es bastante fcil aadir nuevos dinmicamente.
Consolas virtuales mltiples, con varias sesiones login a travs de la consola, intercambiables mediante las combinaciones de teclas adecuadas (independiente del hardware de video).
Se pueden crear dinmicamente hasta un mximo de 64.
Soporte para varios sistemas de archivo comunes, incluyendo Minix-1, Xenix y el sistema de
archivo tpico de System V. Dispone de un avanzado sistema de archivos propio con una capacidad de hasta 4 TB y nombres de archivos de hasta 255 caracteres de longitud.
Pg. 31
Estado de la cuestin
Un sistema de archivos especial llamado UMSDOS que permite que Linux sea instalado en
un sistema de archivos DOS.
Sistema de archivos de CD-ROM que lee todos los formatos estndar de CD-ROM.
Appletalk.
Diversos protocolos de red incluidos en el kernel: TCP, IPv4, IPv6, AX.25, X.25, IPX, DDP,
Netrom, etc.
En la actualidad existen muchos sitios en Internet donde se puede acceder a una gran diversidad
de distribuciones Linux, muchas de ellas gratuitas y otras de propsito comercial de coste variable en funcin de mltiples factores. Si lo que se desea es obtener algo ms de informacin sobre
este sistema se pueden consultar en otras muchas pginas web, las siguientes:
http://www.linux-es.org/sobre_linux
http://es.wikipedia.org/wiki/Historia_de_Linux
http://www.monografias.com/trabajos14/linux/linux.shtml - historia
2.2.3
MikeOS: Este sistema operativo est diseado para PCs de la familia i386 y est escrito completamente en ensamblador. Es una buena herramienta para demostrar lo simple que es el funcionamiento de un sistema operativo, con un cdigo bien comentado y una amplia documentacin. Sus caractersticas bsicas son:
Pg. 32
Estado de la cuestin
Pg. 33
Estado de la cuestin
Pg. 34
Estado de la cuestin
1.
equipo desnudo x86. Hacer que un programa haga boot, incluso uno tan sencillo como el tpico que muestra la frase: Hola mundo, es un primer paso para construir el kernel de un sistema operativo. Poner en un ejemplo, simple y bien documentado, cmo
hace boot el kernel, podra ser una buena idea para animar a otros a escribir su propio
kernel. Debido a la simplicidad de GeekOS, ste podra ser til para desarrolladores de
sistemas empotrados.
El deseo de actualizar los proyectos del curso (under graduate) de sistemas operativos de
2.
la universidad de Maryland. Los proyectos fueron desarrollados originalmente para MSDOS, usando un compilador y ensamblador para el modo real de 16 bits. Mientras que usar
MS-DOS como anfitrin para los proyectos permita el acceso directo a los recursos, algo
deseable, tambin dejaba al S.O. vulnerable a los cuelgues de los proyectos. Adems los
estudiantes acostumbrados a la programacin en sistemas como Unix o Windows no encontraban muy agradable la experiencia del modo de direccionamiento artificioso del modo real. GeekOs es un repuesto lgico para los proyectos originales de 16 bits, porque sigue manteniendo el espritu de la programacin prxima al hardware. Porque los PCs
basados en la familia x86 son baratos y ampliamente disponibles, y porque existen herramientas libres de mucha calidad disponibles para ellos. Adems existen excelentes emula-
Pg. 35
Estado de la cuestin
dores para el hardware del PC, como Bochs, Qemu, etc. que se encuentran implementados
para varias plataformas.
Hacer que GeekOS se mantenga simple es de vital importancia, y para ello se han limitado sus
caractersticas a aquellas consideradas fundamentales. A continuacin se enumeran algunas de
ellas:
Escrito bsicamente en C.
Manejo de interrupciones.
Gestin de memoria dinmica (heap memory allocator).
Hilos (threads) de kernel por rodajas de tiempo con planificacin de prioridad esttica.
Mutexes y variables de condicin para la sincronizacin de hilos del kernel.
Modo usuario con proteccin de memoria basada en segmentacin y un interfaz simple de
llamadas al sistema.
Controladores de dispositivo para teclado y pantalla VGA en modo texto.
En esta lista no se han incluido memoria virtual paginada, controladores de dispositivo de almacenamiento ni sistema de ficheros. Se ha usado el mecanismo de segmentacin del x86 para
implementar la proteccin de memoria para las tareas de modo usuario. Hay que sealar que los
segmentos de modo usuario usan un modelo de direccionamiento de 32 bits, evitndose con ello
los artificios requeridos en el direccionamiento de segmentos del modo real. GeekOS incluye un
mecanismo para compilar un programa de usuario como un objeto de datos enlazados directamente dentro del kernel, para sortear as la ausencia de almacenamiento de disco y sistema de
ficheros. Esta tcnica puede usarse tambin para implementar un sistema de ficheros sobre la
RAM.
Pgina oficial: http://geekos.sourceforge.net
GNUFiwix:
Fiwix es un ncleo de sistema operativo basado en la arquitectura UNIX y enfocado totalmente
para ser compatible con el ncleo de LINUX. Este sistema est siendo desarrollado fundamentalmente para uso educacional, y entre sus objetivos est el intentar mantenerse tan simple como
sea posible, en aras de facilitar al mximo al estudiante la comprensin del mismo. Linux es de-
Pg. 36
Estado de la cuestin
masiado grande y complejo para que un estudiante medio pueda entender fcilmente su estructura interna, y es por ello que este sistema, debido a su mnima estructura, puede ser ms adecuado
para la educacin. Por razones obvias, este sistema est enfocado principalmente a estudiantes y
entusiastas en sistemas operativos que deseen ampliar sus conocimientos en la materia.
La plataforma en la que corre es la de la familia de Intel "x86" de 32 bits, y es compatible con un
buen nmero de aplicaciones GNU ya existentes. No hay que creer que Fiwix es una variante
ms de Unix con sus propias libreras, utilidades, aplicaciones, etc., sino ms bien que es un nuevo ncleo de estilo Linux pero bajo la filosofa GNU/Linux y que se aprovecha de las aplicaciones GNU ya existentes. Fiwix es altamente compatible con la base del ncleo Linux (con sus
limitaciones), permitiendo que cualquier programa ELF-i386 compilado en un sistema
GNU/Linux puede ser ejecutado nativamente sin ningn tipo de emulacin. El diseo de su
ncleo es monoltico, y el lenguaje de desarrollo principal es 'C', con algunas partes crticas en
ensamblador.
Algunas de sus caractersticas a destacar son:
Especificacin compatible "GRUB Multiboot".
Modo protegido de 32bit. Ncleo no expulsor.
Multitarea real (tareas de ncleo a nivel 0).
Entorno de tareas protegido (direc. memoria independientes por proceso).
Manejo de interrupciones y excepciones.
Seales POSIX.
Comunicacin entre procesos con "pipes".
Manejo de memoria virtual hasta 4GB (todava sin intercambio).
Paginacin por demanda con "Copy-On-Write".
Compatibilidad de llamadas al sistema Linux.
Soporte formato ejecutable Linux ELF-386 (enlazado esttica y dinmicamente).
Algoritmo de planificacin Round Robin (todava sin prioridades).
Capa de abstraccin VFS.
Soporte sistema ficheros Ext2" (solo lectura) con bloques de 1, 2 y 4 KB.
Soporte sistema ficheros "Linux PROCfs".
Soporte pseudo sistema ficheros "PIPEfs".
Soporte sistema ficheros "ISO9660" con extensiones "Rock Ridge".
Pg. 37
Estado de la cuestin
Pg. 38
Estado de la cuestin
que el sistema se adapte a cualquier propsito; por ejemplo podra ser adaptado a sistemas de
tiempo real con muy poco cdigo adicional, ya que las operaciones para manejar las restricciones
de tiempo ya existen. La ligereza y modularidad del ncleo tambin lo hacen til para el desarrollo de sistemas dedicados a dispositivos y/o tecnologas particulares que no requieran interfaces
amigables pesadas y/o grficos, as como tambin lo hace muy fcilmente adaptable a las nuevas
tecnologas.
Se puede encontrar informacin adicional en: http://minirighi.sourceforge.net/
Pg. 39
3.1 Introduccin.
Podramos decir que un proceso es el conjunto ordenado de pasos que hay que seguir para obtener un producto o alcanzar la solucin de un problema, y en el caso concreto de ser un producto
software, que ste resuelva un problema. El proceso de creacin puede llegar a ser bastante complejo, por ejemplo, la creacin de un sistema operativo suele requerir un proyecto y, la gestin de
muchos recursos y en general todo un equipo de trabajo. Por otro lado, si se tratara de un programa sencillo, ste podra ser realizado por un solo programador fcilmente. Es por ello que el
proyecto suele clasificarse segn su tamao en tres categoras: pequeo, mediano y de gran porte. Existen varias metodologas para estimarlo, siendo una de ellas el sistema COCOMO, el cual
aporta mtodos y un programa que calcula y realiza una estimacin de los costes de produccin
[BARR94], pg. web en espaol: http://www.sc.ehu.es/jiwdocoj/mmis/cocomo.htm.
En los proyectos de gran porte es necesario realizar trabajos bastante complejos, tanto en el aspecto tcnico como en el administrativo, por lo que se ha llegado a desarrollar una ingeniera
para su estudio conocida como "Ingeniera de Software" [SOMM05]; mientras que en los de mediano porte basta un pequeo grupo de trabajo e incluso un nico analista-programador eficiente
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 40
puede realizar el trabajo. En estos proyectos (incluso a veces en los de pequeo porte) es siempre
necesario seguir ciertas etapas en la creacin del software, si bien stas, pueden ser flexibles de
acuerdo a la metodologa utilizada.
Los "procesos de desarrollo software" [CUEV02], tienen reglas establecidas que deber ser aplicadas en la creacin del software de mediano y gran porte, ya que de lo contrario es muy posible
que el proyecto no concluya satisfactoriamente. Entre tales "procesos" los hay livianos y pesados, con sus variantes intermedias. Por citar algunos, "Programacin extrema", "Proceso unificado de Rational", "Feature Driven Development (FDD)", etc. Pero cualquiera que sea el proceso
utilizado en el desarrollo del software, siempre se aplica un "modelo de ciclo de vida", siendo
corriente en desarrollos de mediano porte el aplicar una metodologa propia, normalmente un
hbrido de los procesos anteriores (para ms informacin se puede consultar la siguiente pgina
web: http://es.wikipedia.org/wiki/Proceso_para_el_desarrollo_de_software).
Las etapas mnimas a cumplir durante el proceso de desarrollo son:
Captura, anlisis y especificacin de requerimientos.
Diseo
Codificacin
Pruebas (unitarias y de integracin)
Instalacin y paso a produccin
Mantenimiento
Aunque el nombre de stas puede variar ligeramente, o pueden ser ms generales o detalladas;
por ejemplo, agrupando o diferenciando "Anlisis y Diseo".
Estas etapas se subdividen a su vez en sub-etapas, siendo el "Modelo del proceso", o "Modelo de
ciclo de vida" el que define el orden de las mismas, as como la coordinacin, enlace y realimentacin entre ellas. Algunos de los modelos ms conocidos son: "Modelo en cascada o secuencial", "modelo espiral" y "modelo iterativo incremental", con sus variantes y alternativas ms o
menos atractivas en funcin la aplicacin y sus requisitos. El "modelo en cascada" (tambin conocido como modelo clsico, tradicional, y lineal secuencial), difcilmente se utiliza tal cual,
pues requiere un conocimiento previo y absoluto de los requisitos, que stos no cambien y que
las etapas posteriores estn libres de errores, lo cual hace que slo sea aplicable a desarrollos
Pg. 41
pequeos. En este modelo no hay vuelta atrs en el paso de una etapa a otra, por ejemplo, pasar
del diseo a la codificacin implica un diseo "perfecto", lo cual es bastante utpico. Cualquier
cambio durante la ejecucin de una etapa implica reiniciar el ciclo completo. Seguidamente se
muestra un posible esquema de este modelo:
Pg. 42
senta algunos inconvenientes: los cambios en un etapa madura puede ser muy graves en un gran
proyecto; no es frecuente que el cliente explicite los requisitos de una forma clara, completa y
precisa; el producto final no estar disponible hasta muy avanzado el proyecto; etc.
Los modelos vistos no tienen en cuenta la evolucin temporal del software. Por ello se requieren
modelos diseados para acomodarse a la evolucin temporal, donde los requisitos centrales son
conocidos de antemano aunque no as tanto sus detalles. Estos modelos conocidos como "evolutivos" permiten desarrollar versiones cada vez ms completas y complejas hasta llegar al objetivo
deseado. Los ms conocidos son el "modelo iterativo incremental" y el "modelo espiral".
Pg. 43
Pg. 44
En este apartado se va a exponer cmo se ha realizado el proyecto, incluyendo las etapas y/o
fases que seguidas para la realizacin del mismo.
En primer lugar, y a riesgo de ser reiterativo, diremos que este proyecto naci principalmente por
la necesidad de contar con una herramienta muy personal de trabajo, para ayudar a la imparticin
de prcticas de sistemas operativos en un laboratorio de trabajo. Aunque las necesidades y requisitos mnimos eran en principio bien conocidos por el autor, y stos se iban satisfaciendo a medida que transcurra el tiempo, globalmente se incrementaban, debido principalmente al deseo de
introducir continuamente nuevas mejoras en el sistema.
En el proceso de creacin del sistema se ha seguido un modelo del tipo "evolutivo incremental",
aunque con muchos de sus aspectos muy particularizados y de un modo no muy ortodoxo. Todo
ello debido fundamentalmente a las peculiares caractersticas del proyecto en cuanto al tiempo
empleado en su desarrollo, su complejidad, los objetivos perseguidos y los recursos humanos
disponibles (nicamente el autor).
En la primera etapa del proyecto, los objetivos no eran muy ambiciosos. En primer lugar se pretenda implementar, en primer lugar, el cdigo de arranque de un sistema (boot), escrito en lenguaje "C", de un tamao de cdigo ejecutable no mayor de un sector (512 bytes), para que cupiera en el primer sector de un disquete, "el sector de arranque". Este "boot" se limitara a cargar en
un segmento de memoria dado, una cantidad dada de sectores consecutivos partiendo del primer
sector de datos. Estos sectores, supuestamente, deberan contener el cdigo del sistema operativo, para tras finalizar su carga, continuar la ejecucin con la primera instruccin de la zona de
memoria de carga. Por otro lado, y en segundo lugar, haba que realizar un pequeo esqueleto de
sistema operativo con funciones muy bsicas, para ser cargado por dicho cdigo 'boot', siendo
realmente esta segunda parte, el ncleo principal de este proyecto.
En esta segunda parte, inicialmente se pretenda tener un pequeo interprete de comandos y una
gestin de ventanas con capacidad para visualizar datos de forma independiente, tal que se pudiese, en un futuro, asignar una ventana (y una entrada de teclas) independiente para cada uno de
los procesos que se pudieran crear. En una segunda fase se implementara la posibilidad de crear
ms procesos, con su ventana y teclado independientes, y con una planificacin expulsora tipo
Pg. 45
turno circular o "round-robin". En fases posteriores se iran creando las sucesivas llamadas al
sistema y la especificacin de los procesos de usuario, para poder ser cargados desde el pequeo
intrprete o consola de comandos interna a 'SO'. A medida que el proyecto iba creciendo se
pens en la conveniencia de usar una herramienta de control de versiones, concretamente: 'git'.
Este producto est muy difundido, tiene un gran prestigio y es abierto. Es utilizado entre otros
muchos proyectos para la propia gestin del kernel de "Linux". Este software y toda su documentacin se puede encontrar en: http://git-scm.com, y se incluye como anexo junto con los
fuentes del proyecto. Seguidamente, a ttulo ilustrativo se muestran algunas de las pantallas que
pueden obtenerse con 'git' sobre este proyecto:
Pg. 46
Desde esta ventana se pueden hacer nuevos "commits" (fijar cambios) al repositorio, o enmendar
los ya realizados, crear nuevas ramas, fusionarlas, as como extraer o actualizar repositorios
remotos. En la imagen anterior no se muestra la historia del proyecto. Para verla se puede
arrancar la ventana del comando 'gitk' la cual muestra la siguiente pantalla:
Pg. 47
En la imagen superior se muestran los cambios de un repositorio. Esto incluye mostrar el grafo
de "commits", mostrar informacin sobre cada cambio fijado ("commit"), los arboles de ficheros
de cada revisin, etc. La siguiente imagen muestra la ejecucin del comando "git log".
Pg. 48
Anlisis de requisitos
4. ANLISIS DE REQUISITOS
El principal objetivo del proyecto, como ya se ha dicho, es que el estudiante disponga de una
herramienta til y adecuada para la comprensin de los principios y/o fundamentos de diseo y
construccin de los sistemas operativos para ordenadores.
La herramienta fundamental para logar dicho objetivo es el propio sistema operativo a desarrollar, siendo una condicin muy deseable el de poder utilizar dicho sistema con un software de
emulacin de maquina virtual (DOSBox, Qemu, etc.). Otra caracterstica tambin muy deseable,
es la de poder utilizar un entorno de desarrollo de aplicaciones para el desarrollo del mismo, tal
que permita efectuar las operaciones de compilacin, edicin y depuracin de forma integrada
(IDE turbo C). Adems, en aras a facilitar al mximo la sencillez del sistema, y por ltimo, hay
que citar tambin como un requerimiento fundamental el que el S.O. est diseado para funcionar en el modo "real" de la familia de procesadores i386.
A continuacin, se van a relacionar los requisitos bsicos que debe cumplir el S.O. para satisfacer las necesidades y requerimientos de este proyecto fin de carrera.
Pg. 49
Anlisis de requisitos
Identificacin
ID-01
Tipo
Funcional
Nivel
Imprescindible
Descripcin
Comprobacin
del cumplimiento
Tener la posibilidad de elaborar y modificar las polticas de planificacin expulsoras, por prioridad, etc.
Crear y destruir procesos varios procesos, ponindolos en ejecucin
concurrente, comprobando el buen funcionamiento.
Identificacin
ID-02
Descripcin
Motivacin
Tener la posibilidad de sincronizar procesos as como de transferir informacin entre ellos mediante el uso de buzones. Esta caracterstica
permite el estudio de diferentes tcnicas de implementacin.
Comprobacin
del cumplimiento
Identificacin
ID-03
Descripcin
Motivacin
Comprobacin
del cumplimiento
Uso de programas de usuario con alguna regin crticas para implementar con algn semforo.
Identificacin
ID-04
Descripcin
Motivacin
Motivacin
Comprobacin
del cumplimiento
Tipo
Tipo
Tipo
Funcional
Funcional
Funcional
Nivel
Nivel
Nivel
Para prcticas
Para prcticas
Imprescindible
La creacin y destruccin de procesos requiere que se les asigne memoria a la medida de su tamao y posteriormente se libere.
Tambin se requiere para que el estudiante pueda ampliar y modificar
las polticas de asignacin de la memoria.
Monitorizar la lista de huecos de memoria tras la creacin y destruccin
de procesos. Realizacin de programas que pidan y devuelvan memoria
siguiendo diferentes patrones con comprobacin de la consistencia de la
memoria gestionada.
Pg. 50
Anlisis de requisitos
Identificacin
ID-05
Descripcin
Motivacin
Comprobacin
del cumplimiento
Identificacin
Descripcin
Motivacin
Comprobacin
del cumplimiento
Identificacin
Descripcin
Motivacin
Comprobacin
del cumplimiento
Identificacin
Descripcin
Motivacin
Comprobacin
del cumplimiento
Pg. 51
Tipo
Funcional
Nivel
No Imprescindible
ID-07
Tipo
Funcional
Nivel
Imprescindible
Tipo
Funcional
Nivel
Imprescindible
Tipo
Funcional
Nivel
Imprescindible
Gestin completa de ficheros tipo FAT12 y FAT16, incluyendo al menos llamadas al sistema bsicas: crear, abrir, posicionar, leer, escribir,
cerrar y borrar ficheros, y operaciones bsicas sobre directorios tales
como: crear y borrar. Soportar tambin el concepto de rutas relativas y
absolutas de tanto uso en la actualidad en otros sistemas.
Permitir el acceso y escritura de informacin en disquete y en disco duro, con formato creado por sistemas tipo MS-DOS.
Escritura de aplicaciones y/o comandos con uso de llamadas al sistema
relacionadas con ficheros, como: leer, escribir, crear directorio, etc.
Anlisis de requisitos
Identificacin
ID-10
Tipo
Funcional
Nivel
Imprescindible
Comprobacin
del cumplimiento
Gestin completa de ficheros tipo FAT12 y FAT16, incluyendo al menos llamadas al sistema bsicas: crear, abrir, posicionar, leer, escribir,
cerrar y borrar ficheros, y operaciones bsicas sobre directorios tales
como: crear y borrar. Soportar tambin el concepto de rutas relativas y
absolutas de tanto uso en la actualidad en otros sistemas.
Permitir el acceso y escritura de informacin en disquete y en disco duro, con formato creado por sistemas tipo MS-DOS.
Escritura de aplicaciones y/o comandos con uso de llamadas al sistema
relacionadas con ficheros, como: leer, escribir, crear directorio, etc.
Identificacin
ID-11
Descripcin
Motivacin
Facilitar el desarrollo del sistema, ya que ste se realiza bajo DOS y con
el entorno integrado de desarrollo de Turbo-C, el cual permite ejecutar
y depurar programas, y no sera deseable tener que renunciar a esta caracterstica porque el sistema no lo soportase (aunque existe un depurador de Borland: "Turbo Debugger", que es independiente del entorno
integrado y permite depurar aplicaciones DOS, sera deseable que el
sistema no lo requiriese; y pudiese ser ejecutado y depurado, desde el
propio entorno integrado que acompaa al compilador de Turbo C).
Comprobacin
del cumplimiento
Identificacin
ID-12
Descripcin
Manejo sencillo de tratamiento de interrupciones, tanto para implementar la multitarea, como para el teclado, ofreciendo posibilidades de ampliacin a otros dispositivos como el puerto serie y el ratn.
Motivacin
Comprobacin
del cumplimiento
Descripcin
Motivacin
Tipo
Tipo
No Funcional
No Funcional
Nivel
Nivel
Muy deseable
Muy deseable
Aunque los requisitos del sistema operativo que se pretende implementar rene caractersticas
bastante interesantes, ste no pretende ser usado para otro propsito diferente al de la enseanza
de los "Sistemas Operativos". En el anlisis de requisitos se pueden encontrar muchas carencias ,
por ejemplo, no se considera en principio necesario que soporte muchas de las caractersticas que
en otros sistemas son bsicas, tales como: la "redireccin" de entrada/salida, la incorporacin de
nuevos "drivers", ni en tiempo de carga ni con posterioridad; la equiparacin de dispositivo fsico al concepto lgico de fichero; un diseo por niveles o capas; diferenciacin entre niveles de
privilegio de usuario y supervisor; lneas de comunicacin serie, Ethernet; etc.
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 52
Diseo e implementacin
5. DISEO E IMPLEMENTACIN
En primer lugar hay que decir que ha sido conveniente seleccionar un sistema de emulacin de
"mquina virtual" de entre las diversas opciones disponibles, ya que es ms sencillo adaptar el
proyecto a un sistema especfico que dejarlo abierto y operativo a cualquier otra posibilidad, no
obstante, aun cindose a uno concreto, el proyecto no debe quedar relegado nicamente a dicho
sistema, y ha de poder ser adaptado fcilmente a cualquier otro. De los sistemas de virtualiza-
Pg. 53
Diseo e implementacin
cin estudiados, el que se ha juzgado ms apropiado para el presente proyecto, ha sido "DOSBox" junto con el entorno de interfaz grfico "D-Fend", aunque no ha sido fcil dicha eleccin
dada las buenas cualidades que ofrecen las otras opciones disponibles (Qemu, Bosch, Virtual PC,
Virtual Box, etc.). En principio cualquiera de ellas podra ser apta para el proyecto, sin embargo,
se escogi finamente "DOSBox-DFend", porque la conjuncin de ambos ofreca determinadas
caractersticas deseables que no llegaban a aportar los otros productos por una u otra razn. De
entre stas se van a citar a continuacin las ms relevantes:
El sistema est disponible para varias plataformas: Windows, Linux, Apple iMac y es
software abierto.
Una vez resuelta la decisin de qu software a utilizar como mquina virtual, como segundo paso, haba que decidir tambin qu leguaje y compilador utilizar. La eleccin del leguaje fue C,
por ser la opcin tradicional para el desarrollo de sistemas operativos. En cuanto al compilador,
"Borland Turbo C" era un compilador que el autor conoca bastante bien y en varias versiones, y
adems ste reuna las cualidades necesarias para el desarrollo del proyecto, por lo que no se
consider necesario explorar ms posibilidades. Esta herramienta aporta un entorno integrado de
desarrollo (IDE) que permite compilar, ejecutar y depurar de forma muy cmoda, lo que hace
que sea una herramienta muy buena para el desarrollo del sistema y para ser utilizada por los
estudiantes. Sin embargo, ha sido necesario retocar el mdulo de inicializacin C0t.obj, para
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 54
Diseo e implementacin
poder compilar programas que no fuesen a ejecutarse necesariamente bajo MS-DOS. En este
mdulo se inclua el cdigo de inicializacin del programa bajo MS-DOS y ha sido eliminado.
En su lugar se ha dejado nicamente una inicializacin muy bsica para el nuevo sistema, consistente en guardar en la variable DGROUP@ (que usa Turbo C) el valor del registro DS, para que
posteriormente, cuando se invoquen las rutinas de tratamiento de interrupcin (tipo void interrupt), se restaure dicho registro DS al valor del segmento de datos de SO (este cdigo lo genera automticamente el compilador en el modelo small para las funciones del tipo "void interrupt()". Una vez hecho esto, se invoca la funcin main() y finalmente la instruccin INT 20,
para contemplar el caso de ejecucin como invitado de MS-DOS (ver fichero MI_C0t.asm en
el directorio MI_C0).
Otra particularidad a tener en cuenta respecto al uso del compilador "Turbo C++ v3.0", la emulacin de mquina virtual "DOSBox" y el sistema 'SO' es la siguiente: cuando se ejecuta 'SO' como
invitado de MS-DOS y desde el entorno integrado de Turbo C, el depurador no funciona adecuadamente, porque el vector de teclado que captura (y al salir restituye) SO, no es el original del
MS-DOS (o BIOS), sino el que previamente ha establecido Turbo C, lo cual interfiere con el
depurador provocando cuelgues, para evitarlo, el sistema intenta discernir si el sistema se ejecuta
sobre en el entorno de Turbo C. Si es as, slo se guarda el vector de teclado de DOSBox v.074,
que es F000:E987, en caso contrario, se limita a leer y guardar el vector de teclado anterior. Por
todo ello, si se cambiara de versin de DOSBox o se usara otro emulador, habra que cambiar el
valor mencionado o implementar otra solucin (ver mdulo teclaint.c). Estos detalles, sin ser
esenciales para el proyecto, s son importantes, porque la posibilidad de usar el depurador de
Turbo C para detectar y corregir errores de programacin del sistema es una cualidad muy deseable, tanto para ser usada por el estudiante como por cualquier programador que deseara trabajar en el proyecto.
Este pequeo sistema operativo se ha diseado bsicamente como un sistema monoltico, ya que
en principio es ms fcil de implementar, al estar todo el sistema en un nico programa ejecutable, el cual rene toda la funcionalidad. El programa en el que se ha compilado todo el sistema se
denomina "SO.EXE" para el entorno de ejecucin MS-DOS (aunque ste no se usa) o bien
"SO.BIN" (se convierte a ".bin" mediante la conocida utilidad "exe2bin.exe", la cual bsicamente
Pg. 55
Diseo e implementacin
elimina la cabecera del fichero ejecutable requerida por MS-DOS), si se va a ejecutar directamente sobre el hardware tanto fsico como virtual. El esquema de la estructura de este diseo es
el siguiente:
Pg. 56
Diseo e implementacin
Las llamadas al sistema se reciben mediante la invocacin de una rutina de tratamiento de interrupcin (RTI) software dedicada a ello y en ella se determina de qu llamada se trata, redirigiendo la peticin a la rutina de servicio apropiada, la cual a su vez se apoya en las rutinas de
utilidad y funciones de la BIOS. En el caso particular de las llamadas relacionadas con ficheros,
la peticin es encolada, para ser posteriormente extrada y servida por el Servidor de Ficheros ya
mencionado.
Por otro lado los dispositivos (hardware) producen interrupciones que procesan las rutinas de
tratamiento de interrupcin hardware, las cuales entre otras cosas, se encargan del teclado (interpretando y encolando teclas en cada uno de los buffers de teclado de las ventanas terminal), de
reloj (controlando los "quantum" de tiempo, alarmas, etc.), y en general de cualquier dispositivo
que haya que controlar (ratn, rs232, etc.).
En este sistema se han implementado los tpicos componentes de un sistema operativo: Gestin
de Procesos (con multitarea expulsora), Memoria (particiones variables), Entrada/Salida y Ficheros (FAT-12 y 16) y una consola interna como intrprete de comandos (stos y otros conceptos generales sobre sistemas operativos que se ven a lo largo de este captulo se pueden consultar
en la amplia bibliografa existente en la actualidad al respecto, como muestra pueden consultarse: [TANE09, CAND07, CARR01], etc.; en la seccin final de bibliografa se citan algunas referencias ms). Otros componentes como gestin de redes, proteccin, etc. se han dejado para futuras ampliaciones. Todos estos componentes se hayan integrados en el ncleo, ya que este sistema, como ya se ha dicho, tiene un diseo monoltico. No obstante, el cdigo est estructurado en
ficheros independientes (para compilacin separada) que forman parte de un proyecto de Turbo
C. Cada uno de estos ficheros agrupa las diferentes funciones de cada uno de estos componentes.
En los sub-apartados siguientes se muestran las peculiaridades de diseo e implementacin de
cada uno de estos componentes.
Pg. 57
Diseo e implementacin
5.3.1
Gestin de Procesos:
Pg. 58
Diseo e implementacin
TICS_POR_RODAJA (tamao del quantum), origen o base de la pila para el proceso servidor de
ficheros , y nmeros de proceso de los procesos: ocioso (IDLE=-1), servidor de ficheros
(SERV=0), y la consola interna (CNSL=1).
Las funciones exportadas son:
inicProcesos(). Inicializacin del mdulo.
nuevoPid(). Devuelve un nuevo identificador de proceso "pid".
nproc(pid). Devuelve el nmero de proceso (ndice de la tabla de procesos) a partir de un
identificador de proceso.
encolar(cola, npr). Encola un proceso en una cola.
desencolar(cola). Devuelve el primer proceso de la cola y lo quita de ella.
quitarDeCola(cola, npr, nprAnt). Suprime el proceso 'npr' de la 'cola' en cualquier posicin. No lo busca, se ha de pasar el proceso anterior 'nprAnt'.
buscarEnCola(cola, npr, *pErr). Busca el proceso 'npr' y devuelve el anterior si lo encuentra, sino pone *pErr=TRUE.
activarProceso(npr). Pone en ejecucin al proceso 'npr'.
activarPreparado(). Pone en ejecucin el primero proceso "preparado".
bloquearProceso(por, cola). Pone al proceso que est ejecutndose en estado
BLOQUEADO con la razn especificada en 'por'; encola el proceso al recuso si se especific 'cola' y por ltimo pone en ejecucin al primer proceso preparado.
crearProceso(part, size, *name). Crea un nuevo proceso en el bloque de memoria especificado en 'part', de tamao 'size', con el nombre del fichero ejecutable 'name'. El proceso
recibe una nueva recin creada ventana terminal.
killProcess(npr). Mata el proceso 'npr liberando todos sus recursos.
reboot(). Reinicia la maquina llamando a la BIOS - INT 19
finProgDos(). Finaliza 'SO' cuando corre sobre MS-DOS (aunque no lo usa)
getDate(). Devuelve la fecha.
listarProcesos(). Para uso interno de la consola para listar los procesos vivos.
De algunas de estas funciones conviene aclarar algo ms su funcionamiento dada su importancia
para el sistema:
Pg. 59
Diseo e implementacin
La funcin "inicProcesos()" se encarga de preparar los datos relacionados con procesos. Inicializa la tabla de procesos a 0's, para despus inicializar las entradas correspondientes a los procesos
servidor (SERV) y consola (CNSL). Establece el nmero de procesos vivos a 2, deja como proceso en ejecucin (nprEjec) la consola y pone en la cola de preparados como nico proceso al
proceso servidor de ficheros (SERV). Hay que sealar que estos dos procesos al ser internos a
SO tienen algunos campos con valores atpicos, por ejemplo el valor del segmento de memoria
donde estn cargados (dirMem) es el mismo en ambos, que es el valor del registro CS. El campo
tamao de la consola tiene el valor de todo el sistema y el del servidor vale 0. Estos valores se
usan en procesos normales para liberar la memoria ocupada al morir el proceso, pero estos procesos siempre estn vivos y adems no reciben la memoria como el resto de procesos, mediante
asignacin dinmica, por lo que el valor es puramente informativo. Por otro lado tambin hay
que sealar que la ventana terminal es la misma para ambos y por ltimo decir que tanto la consola como el servidor tienen su propia pila dentro del espacio de SO indicada por las constantes
SRV_PILA y BASE_PILA.
La funcin "activarProceso(npr)" es bastante pequea, sin embargo su contenido no es trivial.
Se encarga de poner en ejecucin el proceso "npr", del que se supone que debe tener en su pila el
valor de los registros del procesador, tal y como estaban cuando sucedi la ltima prdida del
procesador por parte de dicho proceso, bien por cesin voluntaria, o bien por expulsin (fin rodaja o cualquier otra razn). Esta funcin pone el estado del proceso a "EJECUCIN", recupera la
pila del proceso de su descriptor y tras una serie de instrucciones POP, acaba con la instruccin
IRET, la cual sirve para retornar de una interrupcin (hardware o software => INT). Conviene
decir que en Turbo C existe el tipo de funcin "void interrupt()" que se utiliza para implementar
las rutinas de tratamiento de interrupcin. En este tipo de funcin, el compilador incluye implcitamente al comienzo de la funcin, una serie de instrucciones PUSH que se encargan de guardar
en pila el valor de los registros del procesador, y anlogamente, al final de la funcin, tambin
incluye una serie de instrucciones POP para recuperar dichos valores. Son estas POP's la que se
incluyen explcitamente en la funcin "activarProceso()" al no ser esta funcin del tipo "void
interrupt()" y quererse obtener el mismo efecto.
La funcin "activarPreparado()" se utiliza cuando hay que poner en ejecucin otro proceso, bien
porque muere, se bloquea o vence el quantum de tiempo del proceso en ejecucin (en este ltimo
caso slo si hay algn proceso preparado). En todos estos casos hay que seleccionar otro proceso
para ponerlo en ejecucin. Si al tratar de seleccionar otro proceso la cola estuviese vaca, enton-
Pg. 60
Diseo e implementacin
ces se para el procesador (instruccin HLT) y se pone "IDLE (ocioso)" como proceso en ejecucin. Cualquier interrupcin reactivara la CPU (sacndola de "halt" y reanudando el bucle). Si
durante la ejecucin de la rutina de tratamiento de interrupcin (RTI) algn proceso pasase a
preparado, entonces se abandonara el bucle, seleccionando al primer proceso preparado y retornando. Seguidamente se activara dicho proceso y continuara su ejecucin.
La funcin "bloquearProceso(por, cola)" pone el estado del proceso que est ejecutndose a
"BLOQUEADO" especificando la razn del bloqueo. Si se especifica "cola", pone al proceso a
bloquear en dicha cola (algunos recursos se pueden implementar sin uso de cola), y por ltimo,
tiene que poner a otro proceso en ejecucin por lo que llama a activarPreparado(). Hay que sealar que la funcin "bloquearProceso()" no guarda el estado de los registros del procesador en
pila. Estos registros se supone que ya estn salvados en la pila del proceso, porque cuando se
invoca esta funcin se hace durante la ejecucin de una RTI (bien sea esta una interrupcin
hardware como el reloj, teclado, etc., o una llamada al sistema).
La funcin "crearProceso(dirM, size, name)" se encarga de crear un nuevo proceso que ubica en
el segmento de memoria 'dirM', de tamao 'size', (previamente solicitado al mdulo de memoria), y le asigna el nombre dado en 'name'. Esta funcin debe encontrar un descriptor libre para el
proceso y completar todos sus campos. Hay que destacar el campo 'pWin' que contiene la direccin de la ventana del proceso, la cual se crea solicitando memoria e inicializndola despus mediante "initWin" del mdulo de ventanas. El campo "sp" guarda la direccin de la pila del proceso. Su valor es la direccin ms alta del segmento ubicado para el proceso menos el tamao de la
trama de registros del procesador que ha de introducirse en el momento de la creacin del proceso. De los valores de estos registros slo son relevantes los registros flags, CS e IP, sobre todo
CS:IP ya que deben contener la direccin de comienzo del programa, la cual deber ser: 0 para IP
y dirMem para CS. Hecho esto el proceso se deja en "preparados", para que cuando se active, se
restauren los registros del procesador (inicialmente si importancia), y al ejecutar la instruccin
IRET la CPU salte a la direccin del comienzo del proceso => dirMem : 0.
La funcin "killProcess(npr)" mata al proceso 'npr' teniendo en cuenta su estado. En primer lugar
comprueba que el proceso sea uno vlido, si es as, cierra cualquier fichero que pudiera estar
abierto por l. Seguidamente comprueba que la ventana del proceso solo la usa l, para liberarla
en ese caso o dejarla como est en caso contrario. A continuacin, si el proceso estaba preparado
o estaba bloqueado y encolado en alguna cola de recurso, lo quita de dicha cola. Para finalizar, y
Pg. 61
Diseo e implementacin
entre otras cosas, si el proceso padre estaba en 'PAUSE' se pone en preparados, y si el proceso
que muere estaba en ejecucin se selecciona otro proceso a ejecutar. Nota: Se despierta al padre
si ste est en estado PAUSE porque cuando al crear un hijo mediante "fork()", el padre puede
quedarse esperando finalizacin del hijo mediante "pause", por ello al morir el hijo debe despertar al padre si estuviera en dicho estado (Este comportamiento sera mejorable mediante el
empleo de seales [todava no implementadas]).
Pg. 62
Diseo e implementacin
Pg. 63
Diseo e implementacin
Pg. 64
Diseo e implementacin
da al sistema es posible que el proceso se deba quedar bloqueado, por ejemplo si la llamada es
"sleep()", o 'leerTecla()", o tambin podra ser un fin de proceso, teniendo en todos estos casos la
necesidad de activar otro proceso. Igualmente, si se hubiera tratado de una interrupcin hardware, durante el tratamiento de la misma podra haber cambiado el estado de un proceso, pasando
de bloqueado a preparado. Adems ste proceso podra ser prioritario, por ejemplo, si se pulsa
un tecla por la que espera un proceso, podra interesar activar dicho proceso sin dilacin para
mejorar el tiempo de respuesta, algo deseable en sistemas interactivos. Otra posible situacin
podra ser una interrupcin de reloj que hiciese que el "quantum" o rodaja, finalizase, en cuyo
caso tambin habra que activar otro proceso. Cuando cualquiera de estas cosas sucede, para
activar otro proceso, hay que recuperar el puntero de pila de su descriptor y seguidamente recuperar los registros del procesador que deben estar guardados en su pila y retornar al punto donde
se produjo la ltima interrupcin (o inicio de programa si es la primera vez).
5.3.2
Gestin de Memoria:
Diseo e implementacin
visibles a otros mdulos se encuentran en el fichero "memoria.h", en el cual nicamente se define la constante "CHUNK" para establecer la cantidad de memoria que se le da a todo proceso
para espacio ('gap') entre el rea de datos y la pila. Tambin se declaran las variables 'iniHeap' y
'finHeap', que contienen las direcciones (en "paragraphs") de comienzo y fin del "hueco" que
inicialmente constituye toda la memoria disponible. Por ltimo se declaran los prototipos de las
funciones que exporta el mdulo, la cuales son:
memBios(). Memoria de la que dispone el sistema segn la BIOS.
inicMemoria (). Inicializa los datos del mdulo.
TomaMem (tam). Asigna memoria.
SueltaMem (dir, tam). Devuelve memoria.
incPtr (ptr, v). Suma el valor 'v' al puntero 'ptr' evitando desbordamiento.
esNULL (ptr). Comprueba exhaustivamente si 'ptr' es NULL
volcar (ptr, nBytes). Muestra en pantalla 'nBytes' a partir de la direccin 'ptr'.
mostrarMemoria (). Muestra en pantalla la lista de huecos de memoria.
compactaMem (). Compacta la memoria.
De las funciones arriba sealadas las ms interesantes a comentar son: TomaMem(), SueltaMem() y compactaMem().
En primer lugar hay que decir que las unidades de memoria que se asignan y liberan son los "paragraphs" ("clics" en Minix), de tamao igual a 16 bytes, pudindose entender por ello, que tanto los parmetros de entrada como los de salida sean del tipo "word_t" (el cual ocupa 2 bytes).
Otro trmino que conviene aclarar es el de "hueco", usado aqu ampliamente para designar un
bloque de memoria libre (o disponible).
En el fichero "memoria.c" se declara una estructura de datos para implementar los nodos de a
lista de "huecos" llamada "foo" (ya que hay que poner algo, y este nombre no se usa nunca),
compuesta por dos campos , "tam" y "uSig" que contendrn el tamao y direccin del siguiente
hueco de la lista. Sobre este tipo se declara el tipo "pHue_t" que es un puntero a dato del tipo
anterior, que es el que realmente se usa. Tambin existe declarada un tipo "unin" para facilitar
la visin del dato "direccin de hueco" que a veces interesa tratarlo como un entero "word_t" y
otras como un puntero especial de Turbo C (_seg *), el cual se caracteriza por ocupar slo dos
bytes, pero se considera prcticamente a todos los efectos un puntero largo (el cual ocupara 4
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 66
Diseo e implementacin
bytes), en el que la parte "offset" de la direccin siempre es vale 0. Por ltimo hay que decir que
existen dos variables de mbito privado a este mdulo que son, "priHue" y "memDisponible", las
cuales guardan respectivamente la direccin del primer hueco y la cantidad de memoria disponible inicialmente.
La funcin TomaMem() recorre la lista de huecos buscando uno de tamao mayor o igual al solicitado. Si lo encontrase devolvera su direccin base, dejando la memoria sobrante, si la hubiere, en el propio hueco. Si el tamao del hueco encontrado fuese exactamente el solicitado, el
hueco se eliminara de la lista. Si por otro lado tras el recorrido, no se hubiese encontrado un
hueco de suficiente tamao se devolvera 0.
A continuacin se muestra un ejemplo de mapa de memoria en SO. En l se han mostrado unos
bloques de memoria asignados para tablas FAT del sistema de ficheros de las unidades 0, 1 y 2,
tres procesos y dos ventanas terminal. El resto de la memoria disponible est fragmentada en tres
bloques o huecos.
Pg. 67
Diseo e implementacin
Pg. 68
Diseo e implementacin
fcilmente los procesos, estos se han diseado para que no contengan direcciones estticas, por
tanto, todas las direcciones son reubicables, es decir no estn ligadas a la direccin de carga del
proceso. Cuando un proceso cambia de sitio slo se requiere modificar el descriptor del mismo,
algunos registros de segmento, los cuales especifican segmentos de memoria donde se encuentra
el proceso, y por ltimo la trama de registros guardada en pila que forma parte del contexto del
proceso.
A continuacin se va describir a grandes rasgos el mtodo seguido durante el proceso de compactacin:
En primer lugar hay que recorrer la tabla que el SO mantiene con las unidades de disco para ir
aadiendo en una tabla temporal de objetos a reubicar, inicialmente vaca, cada una de las tablas
FAT de dichos discos. Slo nos interesa la direccin y tamao del objeto. La insercin en la tabla
temporal de objetos se hace en orden ascendente de direcciones y se guarda tambin qu tipo de
objeto es (FAT, ventana, o proceso) y un identificador del mismo (para poder identificar la unidad, y el proceso), aunque en este primer caso, obviamente los objetos incorporados sern todos
del tipo FAT. A continuacin se recorre la tabla de procesos, incorporando igualmente a la tabla
temporal de objetos, los objetos que constituyen los bloques de memoria donde se encuentran
ubicados los procesos. Tambin se aprovecha cada iteracin en la que se accede al descriptor de
un proceso, para incorporar a esta tabla el objeto ventana terminal de dicho proceso, salvo que
ste hubiese sido incorporado con anterioridad a la tabla temporal, por ser una ventana terminal
compartida con otro proceso.
Una vez finalizado el relleno de la tabla temporal, y con sta perfectamente ordenada por direcciones crecientes de memoria, hay que recorrer esta tabla desde el primer elemento al ltimo
para ir reubicando cada uno de los objetos, a la vez que se modifican las estructuras de datos que
el sistema mantiene sobre ellos. Por cada objeto de la tabla se hace lo siguiente:
Se compara la direccin de memoria donde se encuentra el objeto con una direccin "base"
establecida al principio, con la direccin del primer hueco de la lista. Si la direccin del objeto
es menor que dicha "base" se pasa al siguiente objeto, si es mayor, se copia (reubica) el objeto
a la direccin "base", se modifican todas las estructuras de datos que el sistema tiene sobre el
objeto y se incrementa la direccin "base", tantos paragraphs como tenga el objeto reubicado.
Si el objeto es una FAT, la actualizacin de las estructuras de datos slo requiere modificar la
tabla informativa de discos (infDrv[d].pFat). Si el objeto es un proceso hay que actualizar vaPg. 69
Diseo e implementacin
rias cosas: el campo del descriptor del proceso que seala dnde se encuentra el proceso
(tblProc[n].dirMem); el puntero de pila guardado en el descriptor del proceso (tblProc[n].sp);
y finalmente los registros de segmento incluidos en la trama apilada en la misma. Si por
ltimo el objeto es una ventana terminal, hay que modificar el campo del descriptor de proceso que contiene la direccin de la ventana terminal (tblProc [n]. pWin), de todos los procesos que usen dicha ventana terminal y por otro lado hay que retocar los punteros (pWUp y
pWdw) que forman parte de la lista de ventanas que mantiene el SO sobre el orden "3D" que
ocupan stas en pantalla. Esta es quiz la parte ms compleja de la implementacin.
Una vez finalizado el recorrido de la tabla temporal de objetos, reubicados todos y ellos y con
todas las estructuras de datos del sistema correctamente actualizadas slo restara actualizar la
variable del sistema que seala la direccin y tamao del nico y primer hueco de memoria disponible (priHue).
Para acabar, tambin hay que sealar que durante todo el proceso de compactacin las interrupciones deben estar inhibidas para evitar cambios de contexto o intentos de acceso a objetos que
puedan estar en trnsito. Esto no supone un grave problema, ya que este proceso no suele durar
mucho y de momento es una funcin reservada que slo se puede invocar desde la consola.
5.3.3
primero de ellos implementa los servicios de obtencin de tecla y el segundo gestiona las interrupciones del teclado.
En "teclado.h" tenemos los prototipos de las funciones:
char leerTecla (void);
char LeerTeclaLista (void);
void LeerLinea (char far * lin, word_t size, bool mayus);
int mibioskey (byte_t cmd);
Pg. 70
Diseo e implementacin
"leerTecla()" es una funcin especial en el sentido de que no est implementada en este mdulo
sino en "llamadas.c", en el cual se encuentran implementados los servicios o llamadas al sistema
y que veremos en otra seccin. Esta funcin utiliza auxiliarmente LeerTeclaLista(). Si no hubiera
ninguna tecla en el buffer de teclado del proceso, leerTecla() dejara bloqueado al proceso. Esta
es la principal razn por la que esta funcin, que usa la consola de SO, se encuentra implementada como una llamada al sistema.
"LeerTeclaLista()" comprueba si hay alguna tecla disponible en el buffer de teclado del proceso,
el cual se encuentra en la ventana terminal del mismo. Si hay una tecla la extrae y la devuelve y
si no la hay devuelve 0. El cdigo de las teclas es el cdigo ASCII que maneja la BIOS, y el 0 no
se usa.
"LeerLinea()" obtiene caracteres del teclado hasta la pulsacin de la tecla "Entrar" (CR, ASCII
13). Se admite la pulsacin de la tecla "Retroceso" (BS, ASCII 8) para borrar el ltimo carcter
introducido. Tiene como lmite "size"-1 (parmetro de entrada) y opcionalmente puede devolver
los caracteres forzados a maysculas.
"mibioskey()" es una funcin clnica de la funcin de librera de turbo C "bioskey()". La razn
de su existencia es porque la original de turbo C usa la interrupcin INT 16 de la BIOS, la cual
deja las interrupciones permitidas cuando se la llama, lo que no es deseable, ya que es preciso
usar esta funcin desde el sistema con las interrupciones inhibidas. Esta funcin, no obstante, se
beneficia del uso de la rutina de tratamiento de interrupcin (RTI) de teclado que implementa la
BIOS, as como del buffer de teclas que sta mantiene.
En el mdulo "teclaint.h" se encuentran los prototipos de establecer y restaurar la RTI de teclado
que suplantar a la incluida en la BIOS: redirigirIntTeclado() y restablecerIntTeclado(). Se
requieren estos prototipos en este fichero porque estas funciones de usan en el cdigo del mdulo
principal "SO.c".
En el modulo "teclaint.c" se encuentra el cdigo de las funciones vistas arriba. Su implementacin es similar al otras funciones cuyo cometido es el mismo, el de instalar y restablecer un vector de interrupcin y que tambin existen para el reloj, las llamadas al sistema y tratamiento de
excepciones; estas funciones se invocan justo en los momentos de arranque y finalizacin del
sistema operativo. No obstante, debe hacerse una aclaracin sobre el cdigo de "redirigirIntTeclado()", ya que incorpora algo especfico debido a una peculiaridad del entorno DOSBox y turPg. 71
Diseo e implementacin
Pg. 72
Diseo e implementacin
alguna tecla disponible, se extrae mediante mibioskey(0). En este punto se analiza si la tecla es
especial, es decir, si se va a usar para controlar el movimiento o tamao de una ventana. Si fuese
una de esas teclas ("tab", "flechas" con o sin "mayus"), se procedera a mover / redimensionar
la ventana focal segn la tecla de control pulsada y a continuacin se abandonara la RTI. Otra
posible combinacin de teclas de uso particular es Ctrl-C, la cual se usa para abortar programas.
En este punto se detecta dicha combinacin de teclas y se invoca la interrupcin "1B", que tcnicamente est reservada por la BIOS, para el evento de pulsacin de la combinacin de teclas
Ctrl-Break. Este detalle conviene aclararlo un poco ms: normalmente, la BIOS de un equipo o
emulador, genera la interrupcin "1B" cuando se pulsa la combinacin "Ctrl-Break"; sin embargo, el emulador "DOSBox" no lo hace as. Para solventarlo, el sistema ha decidi reservar la
combinacin "Ctrl-C" para producir el mismo efecto, y es en este punto donde esto se lleva a
cabo.
Si tras extraer una tecla del buffer mediante "bioskey(0)" la tecla no es especial, entonces se incorpora al buffer de la ventana focal (la que tiene el foco del teclado), a no ser que dicho buffer
est lleno, en cuyo caso se desecha dicha tecla. Tras realizar esto, y dado que en este momento se
sabe que la ventana focal tiene en su buffer teclas disponibles, se comprueba si hay algn proceso bloqueado en espera de dicho evento, si es as se desbloquea dicho proceso quitndolo de la
cola de procesos de la ventana terminal y aqu se ofrecen varias posibilidades: a) Poner el proceso recin despertado en al final de la cola de preparados, b) Ponerlo al principio de la cola de
preparados y c) ponerlo en ejecucin directamente expulsando al proceso actual en ejecucin que
se ira a preparados, bien a la primera posicin, o a la ltima. La eleccin de alguna de estas opciones se har en funcin del grado de interactividad que se desee tener con las aplicaciones que
esperan por el teclado. En la versin actual se ha escogido la opcin "c".
Acceso a disco:
fundamentalmente debido a su relativa sencillez. La nica funcin a tener en cuenta cuyo prototipo se encuentra en "ficheros.h" es:
int leeSector (word_t sect, drv_t drv, void far *pBuf);
El cdigo de la misma se encuentra en "ficheros.c". Esta funcin lee un sector de 512 bytes
(sect) de una unidad de disco (drv), y deja la informacin en la direccin (pBuf) que se le indica.
La funcin se limita a llamar a otra ms general llamada "leeEscr()", que es la que lleva a cabo el
Pg. 73
Diseo e implementacin
trabajo. Esta segunda funcin admite un parmetro ms (cmd) que indica si se desea leer o escribir el sector. La implementacin no es complicada pues se apoya en la funcin de servicio de la
BIOS INT 16, la cual es en realidad quien realiza todo trabajo de programacin de la controladora de disquete y/o de disco duro. Bsicamente lo que esta funcin hace es traducir el nmero de
sector lgico a coordenadas fsicas del disco: cabeza; pista y sector, que a la postre, son los
parmetros con los trabaja la INT 16. Hay que sealar un detalle que debe tener en cuenta esta
funcin; y es: si la unidad de disco es un disquete o es un disco duro. Si es un disco duro, se realiza un desplazamiento (suma) en el nmero de sector pedido para saltarse los 63 primeros sectores, los cuales suelen estar libres, ya que forman un hueco entre el sector maestro de arranque
(master boot record) y el sector de arranque de la particin (boot sector). Otro detalle a tener en
cuenta resulta del hecho de que la funcin INT 16 de la BIOS activa las interrupciones, y stas
quedan permitidas al salir de ella. Si se desea evitar esto, se pueden enmascarar las interrupciones de reloj y teclado, para evitar posibles interrupciones no deseables durante el servicio. Esta
solucin se adopt inicialmente, sin embargo, en la versin actual se han vuelto a dejar permitidas, pues se ha controlado de otro modo esta posibilidad; concretamente, cuando se aadi al
sistema el proceso "Servidor", el cual se encarga en exclusiva de usar esta funcin. Gracias a este
proceso se evita la concurrencia y las posibles condiciones de carrera.
Por ltimo, hay que aadir un ltimo detalle sobre el funcionamiento de la BIOS y la unidad de
disquetes. En un principio, el hardware (controladora DMA) de los equipos sufra la restriccin
de no poder efectuar lecturas de sector, sobre posiciones de memoria que traspasasen una frontera de 64KB, y era responsabilidad del programador el evitar llamar a la INT 16, con una de esas
direcciones de memoria. En esta funcin este problema todava no est resuelto, ya que al estar
destinado principalmente a ejecutarse bajo el emulador "DOSBox", y carecer ste, de dicho problema, se ha considerado innecesario introducir esta complicacin en el cdigo; no obstante, si
se deseara portar el sistema a otros emuladores o equipos reales, habra que efectuar un parche en
esta funcin; algo que en principio sera bastante sencillo; bastara con efectuar siempre todas las
lecturas de sector en un buffer del sistema ubicado en una direccin correcta y luego copiar los
datos desde dicho buffer a la direccin pasada en la llamada.
Tratamiento del reloj:
Pg. 74
Diseo e implementacin
prototipos de las funciones que se encargan de instalar y restaurar los vectores de interrupcin
del reloj (INT 8). En el fichero ".c" est le cdigo de estas funciones junto con el cdigo de la
rutina de tratamiento de interrupcin del reloj. Esta RTI es invocada por el hardware de reloj
cada 55 mseg. aproximadamente y se llevan a cabo las siguientes acciones:
Se Invoca a la antigua RTI (la de la BIOS/DOS), para que pueda seguir llevando a cabo el
recuento de "tics" en las variables de la BIOS (controlar el tiempo).
Se muestra en la esquina superior derecha un smbolo que denota el progreso del sistema,
as como tambin el proceso en ejecucin y el contador de rodajas. Todo esto es opcional, slo tiene una finalidad ilustrativa y/o en ocasiones, tras retocar el cdigo, funciones
de ayuda en la depuracin de cdigo.
Se desactiva el altavoz si transcurri el tiempo de duracin del ltimo pitido, si ste estaba
activo. Esta accin es necesaria porque el tpico pitido que se emite en determinadas ocasiones para indicar algn suceso, como por ejemplo una pulsacin indebida de teclado,
debe dejar de sonar, ya que en el momento en el que el sistema activa el altavoz, este empieza a sonar y se deja en ese estado, ya que no es conveniente efectuar una espera activa
hasta la finalizacin del pitido. En su lugar lo que se hace es inicializar un contador con
la duracin del tiempo de pitido y proseguir con el cdigo, dejando a la RTI de reloj encargada de la tarea de decrementar este contador con cada "tic" y desactivar el altavoz
cuando finalmente llegue a 0.
Se decrementa el lapso (tiempo restante para despertar) de los procesos dormidos, si los
hay, y se comprueba si dicho lapso vale 0, en cuyo caso se despierta al proceso, quitndolo de la cola de dormidos y ponindolo en la cola de preparados.
Por ltimo, se comprueba si en contador de "tics" por rodaja ha llegado al mximo, lo que
significara que habra que expulsar al proceso en ejecucin. Esto se hace ponindolo en
la cola de preparados, y llamando la funcin activarPreparado(). Esta ltima funcin no
retorna, ya que en ella se selecciona el primer proceso de la cola de preparados, se restaura su pila y se retorna a la siguiente instruccin donde se produjo el ltimo cambio de
contexto de dicho proceso. Si por otra parte no se hubiera alcanzado el lmite de "tics" de
la rodaja de tiempo, simplemente la RTI restaurara la pila y finalizara.
Manejo de ventanas:
virtuales" en la forma de "ventanas", las cuales sirven para que los procesos puedan mostrar
informacin a la vez que permiten focalizar el teclado, de tal modo que cuando una ventana se
Pg. 75
Diseo e implementacin
encuentra en primer plano, las pulsaciones de teclado en ese momento se dirijan a ella y quedan
almacenadas en su "buffer", del cual extraern teclas las aplicaciones cuando las soliciten al sistema.
La implementacin se encuentra en los ficheros "windows.c/h". Encontrndose en el fichero ".h"
los prototipos de las funciones que se usan en otros mdulos, as como tambin la declaracin de
algunos tipos y constantes que tambin se requieren. Entre las constantes se encuentra el tamao
del buffer de teclas de las ventanas, y en forma de tipo enumerado, los atributos de color de ventanas y caracteres. El tipo ms relevante es el puntero a ventana "pWint_t", que se apoya la estructura "win_t", la cual consta de los campos necesarios para definir las propiedades de una ventana. Por citar algunos de ellos: rea (plano) para guardar la informacin visual, posicin de la
ventana relativa a la pantalla (eSI. eID), cursor, buffer de teclas (keyBuf), cola de procesos en
espera de tecla, punteros a ventanas por encima y debajo, etc. Entre las variables a exportar a
otros mdulos tenemos: bellTime, que indica los tics de reloj que restan para apagar un posible
pitido que est sonando. videoDirecto, es una variable booleana que sirve para conmutar la salida
de la funcin printCar(), de la ventana del proceso en ejecucin a la pantalla (fondo de escritorio)
y viceversa. Esto se usa cuando el sistema quiere emitir un mensaje sin usar la ventana de ningn
proceso, usando entonces la pantalla en forma directa. curY, curX, indican la posicin de cursor
de pantalla donde se realizar la salida cuando se usa videoDirecto. Por ltimo, pWinTop indica
las ventana que se encuentra en primer plano y pWinFocal la que tiene el foco del teclado.
Las funciones que se usan en otros mdulos son:
PrintXXX(). Sirven para visualizar informacin con diferentes formatos por la ventana del
proceso en ejecucin.
inicWindows(). Inicializa el mdulo. Determina el modo, color o monocromo y limpia la
pantalla.
initWin(). Inicializa una ventana situndola en unas coordenadas de pantalla.
destroyWin(). Elimina una ventana liberando sus recursos.
moverWin(). Mueve una ventana que se le indica con arreglo a un incremento/decremento
(variacin) de coordenadas que se le pasan.
MoverWindow(). Mueve la ventana del proceso en ejecucin a las coordenadas que se le
pasan. (la usa una llamada al sistema)
ColorWindow(). Cambia el atributo de color de una ventana.
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 76
Diseo e implementacin
Diseo e implementacin
das las ventanas tienen dos punteros que, en general, sealan a la ventana de arriba y abajo, sin
embargo en el caso de la ventana del fondo, el puntero que seala abajo en realidad apunta a la
ventana de primer plano, y similarmente, en el caso de la ventana de primer plano, el puntero que
seala a la de arriba, en realidad apunta a la del fondo, con ello se consigue la circularidad de la
lista. Existen varias funciones que sirven para actuar sobre la lista de ventanas, entre estas se
encuentran: insTopLis() y insBottomLis(), que inserta una ventana por la cima y fondo respectivamente, delLis(), que elimina una ventana de la lista y pWinBottom(), que retorna la ventana
que se encuentra en el fondo.
Las ventanas tienen una propiedad llamada "plano" que se usa para guardar la informacin a
visualizar. Este "plano" consta de una matriz de 25x80 elementos (celdas o posiciones de pantalla), y cada uno de estos elementos a su vez de un byte para el color y otro para el carcter.
Cuando se hace "print" en una ventana, lo que se hace es guardar el(los) carcter(es) con su color
en esa matriz, justo en la posicin que indica el cursor de la ventana; adems si la(s) posicin(es)
de la celda de esa ventana no est(n) oculta(s) por ninguna ventana que se encuentre encima,
el(los) carcter(es) se mostrar(n) en pantalla. A continuacin se muestra una figura con varias
ventanas y la lista "3D" que las representa:
Pg. 78
Diseo e implementacin
Un aspecto bastante relevante sobre las ventanas es el cmo se ha resuelto precisamente el que
una posicin concreta de una ventana deba mostrarse o no. Tambin cuando se ha de repintar una
ventana surge dicho problema. La solucin adoptada pasa por utilizar un concepto que se ha denominado "proyeccin de segmentos". Este consiste bsicamente en utilizar "segmentos", los
cuales tienen un atributo de posicin, dado por las coordenadas "x, y", de su punto situado ms a
la izquierda, y de la longitud del segmento. Una ventana siempre puede descomponerse en varios
segmentos, y estos segmentos se pueden proyectar en "3D" hacia el fondo de la pantalla o hacia
arriba.
Durante la proyeccin de un segmento, que es un proceso iterativo, el segmento interseca con las
ventanas que se va encontrando y se van produciendo como resultado de la interseccin nuevos
segmentos, que pueden ser: segmentos a la izquierda de la ventana (s1), a la derecha (s2), o interiores (si). Dependiendo de si el sentido de la proyeccin es hacia arriba o hacia abajo, los segmentos resultantes continan o no su proyeccin de una forma recursiva, para finalmente determinar si el segmento final se ha de mostrar en pantalla o no. La siguiente figura muestra varios
ejemplos de interseccin de un segmento con una ventana:
Pg. 79
Diseo e implementacin
En la figura de arriba se muestra cmo un segmento al intersecar con una ventana puede producir
hasta tres subsegmentos como ya se indic antes, aunque el resultado tambin podra ser slo
uno; "s1" si no hubiera interseccin, o bien "si", si la interseccin fuera totalmente interior, o
tambin podran ser dos; "s1" y "si", o "si" y "s2". La funcin que realiza esto es obtenSegmentos(), la cual es invocada repetidamente durante el proceso de proyeccin realizado por la funcin proyectaSegmento(), el cual a su vez invoca a proyectaUp() o proyectaDown(), dependiendo del sentido de la proyeccin. Para mejorar la comprensin de cmo funciona este sistema se
seala la conveniencia de consultar los amplios comentarios existentes en los fuentes de este
mdulo.
Veamos qu se lleva a cabo cuando, por ejemplo, una ventana oculta se va a mostrar en una posicin de profundidad intermedia. Lo que se hace es recorrer todas las filas de la ventana (abscisa
y) y por cada una de ellas se toma el segmento que la conforma y se inicia la proyeccin del
mismo hacia la cima. Con cada interseccin de ventana se obtienen uno o ms subsegmentos. Si
un subsegmento es del tipo interior (si), significa que est oculto y por tanto no se muestra, acabando el proceso recursivo para l, si por otro lado, los subsegmentos son "s1" o "s2", entonces
proseguirn su proyeccin hacia arriba, efectundose todo esto mediante recursin. Si al final del
proceso recursivo un subsegmento alcanza la cima, entonces se muestra en pantalla. Veamos
ahora el caso en el que lo que se quiere hacer es ocultar una ventana que se encuentra en cualquier posicin de profundidad. Este caso es algo ms complejo pero se resuelve de un modo bastante similar. En este caso los segmentos en los que se descompone la ventana inicialmente inician su proyeccin hacia el fondo. Los subsegmentos interiores "si" son candidatos a ser mostrados, pero no el formado con la ventana a ocultar, si no uno nuevo que se forma con la ventana de
la interseccin. Para comprobar si se debe mostrar dicho segmento, ahora habra que comenzar
un proceso anlogo al visto anteriormente, es decir habra que proyectarlo hacia la cima para
determinar que partes se deben mostrar o no. Volviendo a los posibles segmentos "s1" y "s2",
estos deben proseguir su proyeccin hacia abajo para seguir comprobando si hay alguna ventana
debajo de ellos. La proyeccin finaliza bien cuando alcanzan una ventana y se transforman en un
subsegmento "si" (candidato a ser mostrado) e inician el proceso inverso ya visto, o bien cuando
alcanzan el fondo de la pantalla, en cuyo caso tambin se inicia dicho proceso de proyeccin
hacia arriba, pero el segmento a mostrar en caso de que se alcance la cima sera uno formado por
los caracteres de relleno de fondo de escritorio. Todo lo visto constituye a grandes rasgos el
mtodo usado para la gestin de ventanas en "3D". Este mtodo podra ser mejorado de varias
formas, una de ellas podra consistir en usar tambin segmentos verticales, realizando una transDiseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 80
Diseo e implementacin
5.3.4
Gestin de ficheros:
La gestin de ficheros se encuentra implementada ntegramente en "ficheros.c y .h", y nicamente contempla el sistema de ficheros FAT (variantes 12 y 16 bits) usado principalmente por sistema operativo MS-DOS. La razn por la que se ha usado este sistema es su sencillez y amplia
difusin. En el fichero de cabeceras ".h" estn definidas algunas de las estructuras de datos que
se requieren para implementar la gestin de ficheros. La primera de ellas tiene que ver con la
Pg. 81
Diseo e implementacin
informacin que el sistema debe guardar sobre una unidad de disco. En la estructura "infDrv[]"
se recogen datos como el nmero de cabezas, sectores y pistas que tiene el disco, nmero de sectores por cluster, el primer sector de datos, primer sector del directorio raz, tamao de la FAT y
puntero a ella, etc.
Otra estructura importante, "infoFich_t", recoge la informacin que el sistema debe tener sobre
los ficheros que mantiene abiertos. Entre sus campos se encuentran:
Copia de la entrada del directorio donde se encuentra el fichero (entr).
Nmero de sector lgico donde se encuentra sta (nSect) y
Posicin relativa dentro de dicho sector (nEntr).
Unidad de disco donde se ubica el fichero (drv).
Contador de referencias (nRefs) (indica cuantas veces est siendo referenciada esta estructura por otros objetos tales como los descriptores de fichero) y por ltimo,
"flag" (modif) para indicar si se ha modificado esta estructura y que por tanto ha de ser volcada a disco cuando el fichero se cierre.
Los procesos tienen que mantener cada uno de ellos una tabla con los ficheros que tienen abiertos. Cada uno de los elementos de esa tabla es un manejador de fichero (file handle), y generalmente suele ser un puntero o ndice a una tabla de ficheros abiertos por el sistema cuyos elementos son conocidos comnmente como descriptores de fichero, y el tipo o estructura que lo define
es "descrFich_t", la cual tiene los siguientes campos:
puntero a estructura informativa de fichero, vista en el prrafo anterior (pInf);
posicin del ltimo byte ledo o escrito (fPos), corrientemente conocido como "file
pointer", y que se utiliza en la conocida llamada a sistema de ficheros "lseek"; y por
ltimo,
nmero de veces referenciado (nRefs), el cual es un contador que especifica cuantas veces est siendo utilizado este descriptor por los procesos.
En la siguiente figura se muestra las diferentes tablas de datos y su relacin entre ellas:
Pg. 82
Diseo e implementacin
Diseo e implementacin
riormente desde la perspectiva de su propsito o funcionalidad en el captulo de llamadas al sistema. Seguidamente se hace una descripcin de las funciones usadas por la consola.
inicFicheros(): Inicializa el sistema de ficheros. Se limita a poner las tablas a 0. Esta funcin se usa durante la inicializacin del sistema, en la funcin main() del fichero "so.c"
listarTFAbiertos(): Esta funcin muestra en pantalla los ficheros que el sistema mantiene
abiertos. Es puramente informativa y es la encargada de llevar a cabo el comando de consola "tfa" (Tabla de Ficheros Abiertos).
Respecto a las funciones de soporte de las llamadas al sistema relacionadas con el sistema de ficheros, se evitar entrar en una descripcin pormenorizada del cdigo de cada una de ellas, en
su lugar, se har una descripcin general de las partes de cdigo ms influyentes en la implementacin del sistema de ficheros, el cual se encuentra como es fcil suponer en "ficheros.c".
En primer lugar, puesto que el sistema de ficheros est basado en el de MS-DOS, y ste se basa
en el uso de clusters, es conveniente comentar un poco la funcin "sigCluster()", la cual, con la
FAT cargada en memoria, devuelve el siguiente cluster a uno dado. Si la FAT fuese del tipo 16
bits, el clculo sera muy sencillo, bastara con acceder al elemento de la tabla cuyo ndice fuese
el parmetro de entrada, sin embargo, con una FAT del tipo 12 bits, el clculo es ms complicado. En el manual de referencia tcnico de MS-DOS [MIC91], se detalla el clculo, y esta funcin simplemente lo lleva a cabo. Existe tambin una funcin reciproca llamada "setSigCluster()", la cual se encarga de escribir en la FAT el valor del cluster siguiente a uno dado. Existen
otras funciones de manejo de clusters como son "clus2sect(), getClusLibre(), getUltCluster() y
liberaCadenaClusters()" cuyo propsito y modo de operacin se puede consultar ampliamente en
los comentarios incluidos en el cdigo de dichas funciones.
Otras funciones interesantes son las de montar y desmontar una unidad de disco, "MontaDrv() y
DesmontaDrv()". El montaje de una unidad consiste bsicamente en cargar la informacin del
sistema de ficheros de la unidad, en la tabla informativa de unidades: infDrv[]. Para ello, lee el
sector 0, y si es vlido, establece en dicha tabla valores como: la geometra del disco, el primer
sector de datos, etc. Tambin se encarga de solicitar memoria dinmica para leer la FAT del disco sobre ella, y si la unidad hubiera estado previamente montada la desmontara antes. Desmontar la unidad consiste en liberar los recursos de la unidad, en especial el de la memoria dinmica
Pg. 84
Diseo e implementacin
de la FAT, aunque previamente, comprobara el estado del flag "sucio" (modificada), para en su
caso, escribir los cambios en el disco.
Antes de proseguir con otras funciones hay que sealar la existencia de dos tipos de datos declarados en "fich-ifz.h" (incluido a su vez en "ficheros.h"). Estos tipos son: "entrada_t", que define
la estructura de una entrada de directorio y que se utiliza ampliamente en todo el cdigo; y el
otro tipo de datos, "resulBus_t", cuya necesidad o naturaleza no es tan obvia. Este ltimo tipo lo
utiliza el usuario para poder realizar las llamadas al sistema "buscaPriEntDir()" y "buscaSigEntDir()", que se usan para iniciar, y posteriormente continuar, con la bsqueda de uno o ms ficheros / directorios a partir de una ruta, la cual puede incluir comodines, y atributos de fichero. Los
campos que incluye "resulBus_t" son los necesarios para poder iniciar una bsqueda de entrada
de directorio, y tambin para poder retomar posteriormente dicha bsqueda partiendo del punto
donde se qued la ltima vez. Para esto ltimo se requiere que la informacin persista, lo que
hace gracias a esta estructura de datos. Este tipo de datos tambin se usa ampliamente en todo el
cdigo, porque la necesidad de buscar entradas de fichero en los directorios y proseguir la
bsqueda es muy corriente, pues lo necesitan muchas funciones. Seguidamente se comentan los
campos de esta estructura de datos tan importante.
entrsDir[]. Tabla (buffer) para un sector de disco completo (512 bytes) con capacidad de
hasta 32 entradas de directorio.
nombre[8], ext[3]. Nombre y extensin del fichero a buscar.
atr. Atributo de fichero (oculto, sistema, slo lectura, archivo, directorio, etc.) a usar como
filtro en la bsqueda.
drv, nClus, nSect, nEnt. Son los valores que permiten localizar la entrada objeto de la
bsqueda. "nClus" toma en la primera bsqueda el valor del primer cluster del directorio,
pero a medida que avanza la bsqueda, este campo se actualiza con el valor de los siguientes clusters que forman la cadena del directorio de bsqueda. "nSect" guarda el
nmero de sector relativo al cluster (empezando por 0), pues como sabemos un cluster
puede agrupar 2, 4, 8, etc. sectores. Por ltimo, "nEnt" guarda el nmero de entrada relativa al sector, cuyo valor va de 0 a 31. Todos estos valores, que vienen a representar la direccin de una entrada de directorio, van actualizndose como ya se dijo a medida que la
bsqueda progresa al efectuar sucesivas llamadas a la funcin busSigEntDir().
secCLoDR. Este campo tiene un doble significado. A veces guarda el nmero de sectores
por cluster y otras veces el nmero de sectores que ocupa el directorio raz. La razn es
Pg. 85
Diseo e implementacin
porque el directorio raz no se trata igual que cualquier otro subdirectorio. Para empezar,
el nmero de cluster de comienzo del directorio raz es 0 por convenio, ya que este valor
(junto con el 1) est reservado y no puede ser usado. Para recorrer al directorio raz, al
comprobar que es el cluster 0, se toma como primer sector el indicado en la informacin
sobre la unidad de disco y se va incrementado ste hasta alcanzar el valor del campo
"secCLoDR", que en este caso ser el total de sectores que ocupa el directorio raz. Los
subdirectorios en cambio, se han de recorrer empezando por su primer cluster para seguir
con la cadena de clusters guardada en la FAT. Se alcanza el final cuando la cadena as lo
indica. El algoritmos que determina todo esto se encuentra en las funciones: "incEntDir()" y "setResulBus()", las cuales estn bastante documentadas en el propio cdigo.
Vistas las estructuras de datos ms importantes, vamos a comentar a continuacin algunas de
las funciones ms importantes en el cdigo:
clus2ruta(): En el sistema de ficheros FAT, toda entrada de directorio contiene adems del
nombre del fichero/directorio, el nmero del primer cluster de la cadena de clusters que
conforman el fichero/directorio. Esta funcin construye la ruta de un directorio a partir de
este cluster de inicio. Este proceso es algo tedioso ya que debe ascender en la jerarqua de
directorios buscando las entradas "..", que sealan al directorio padre, hasta que se alcanza finalmente el directorio raz. Esta funcin es utilizada por "GetWorkDir()", que utiliza
la unidad y cluster de trabajo que se guardan en el descriptor del proceso para a partir de
esto y la ayuda de esta funcin devolver la ruta del directorio de trabajo. La funcin "parseRuta()" tambin requiere de esta funcin y se explica a continuacin.
parseRuta(): Esta funcin "analiza" la ruta que se pasa como parmetro de entrada, que
puede ser absoluta o relativa y construye una ruta absoluta sobre una variable local esttica, de la cual devuelve su direccin. Otro efecto importante es que rellena la variable
"*pResul" que se le pasa con la informacin requerida para poder realizar posteriores
bsquedas o cualquier otro propsito. Esta funcin es bastante importante porque es la
que realiza el trabajo principal de la llamada al sistema "buscaPriEntDir()", a la cual solo
le resta aadir un filtro de seleccin de bsqueda y retornar el cluster inicial de la entrada
buscada.
expandeEntrada(): Cuando un fichero crece hay que ampliar su cadena de clusters. Esto
sucede cuando se escribe al final de un fichero o tambin cuando se aaden ficheros a un
directorio. Esta funcin facilita esta tarea permitiendo aadir 'n' clusters a una entrada de
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 86
Diseo e implementacin
directorio existente. Las funciones que llaman a esta funcin son "write()" y "creaEntrada()".
liberaCadenaClusters(): Recorre la cadena de clusters a partir del cluster que se le pasa
como parmetro, el cual normalmente es el primero, y va poniendo ceros en la FAT con
lo que los deja marcados como libres. Esta funcin es usada por "truncaFichero()", "delEntrada()" y "rmDirEntradas()", que sirven para truncar el tamao del un fichero, borrar
una entrada de directorio y borrar todas las entradas de un directorio recursivamente.
abrirFichero(): El S.O. mantiene una tabla en memoria con todos los ficheros con los que
est trabajando en un momento dado llamada "tblFicAbiertos[]". Esta tabla guarda toda la
informacin que necesita el sistema para poder acceder rpidamente a los datos del fichero. Esta funcin se encarga de rellenar esta informacin en esta tabla, admitiendo como
parmetro de entrada la ruta con el nombre del fichero y devolviendo un nmero entero
llamado descriptor de fichero que ser utilizado en otras funciones para poder operar con
el fichero. Esta funcin es usada por "RunFichero()", "Execv()" y "Open()" que se utilizan respectivamente, para ejecutar un programa (crear un nuevo proceso a partir de un fichero ejecutable), cambiar la imagen de memoria de un proceso por la que se encuentra
en un fichero ejecutable y para dar soporte a la llamada al sistema "open()". Conviene sealar que el modulo de ficheros utiliza dos tipos de descriptor de fichero diferentes aunque estn declarados del mismo tipo (df_t). Los descriptores (df) usados como ndice en
la tabla de ficheros abiertos (tblFicAbiertos[df]), del cuyo tipo es el descriptor devuelto
por esta funcin, y los descriptores (fd) usados como ndice para la tabla de ficheros
abiertos del proceso (tblProc[npr].tdf[fd]), de cuyo tipo es el valor devuelto por la llamada al sistema open().
RunFichero(): Pone en ejecucin un fichero ejecutable. Para ello en primer lugar abre el fichero, obtiene su tamao y solicita memoria mediante "TomaMem()". La cantidad pedida
es la suma del tamao del fichero ejecutable, el cual incluye el rea de cdigo y datos inicializados, y una cantidad fija (CHUNK), que est prefijada para dar cabida al rea de datos no inicializados y la pila. Si tiene xito en la obtencin de la memoria, entonces carga
el cdigo ejecutable (imagen binaria del contenido del fichero) mediante la funcin "cargaFichero()", en la direccin de memoria previamente obtenida, y a continuacin crea el
proceso mediante "creaProceso()". Si se produce algn error en la carga del fichero, entonces se libera la memoria y retorna con un cdigo de error. Esta funcin es utilizada por
la consola cuando el operador teclea un comando y ste resulta ser el nombre de un fichePg. 87
Diseo e implementacin
ro ejecutable. Existe otro modo de ejecutar programas que se corresponde mejor con el
sistema usado en el mundo "Unix", el cual consiste en crear un proceso hijo mediante
"fork()" y cambiar su imagen de memoria por la de un fichero ejecutable, lo cual se hace
mediante la llamada al sistema "execv()", que se detalla a continuacin.
Execv(): Cambia la imagen de memoria del proceso en ejecucin por la de un fichero ejecutable. Los pasos que sigue son:
o Abrir el fichero ejecutable (si no existe retorna error),
o Pedir memoria (si no hay suficiente memoria retorna error),
o Cargar el fichero en la memoria solicitada (si no se consigue se retorna error y se libera la memoria que se solicit), y por ltimo,
o Actualizar los campos del descriptor del proceso para que reflejen la nueva situacin.
Hay que cambiar: la direccin de memoria donde se encuentra el proceso, el tamao
del mismo y su nombre. Tambin hay que cambiar el campo 'sp' (cima de la pila), y
en la trama de pila: las posiciones de los registros CS:IP (contador de programa), el
valor de la direccin de comienzo del cdigo, y los "flags" de estado con sus valores
iniciales por defecto.
Los pasos que modifican estructuras de datos como la tabla de procesos o las variables de
gestin de memoria deben hacerse con las interrupciones inhibidas, pues constituyen regiones crticas. Otros pasos que afectan a la gestin de ficheros no requieren de dicha
medida, porque las operaciones con ficheros las lleva a cabo el proceso "servidor" y est
garantizado que mientras se lleva a cabo un servicio por dicho servidor, no se lleva a cabo
otro, por lo que no hay concurrencia, ni por tanto condiciones de carrera.
Read(): Da soporte a la llamada al sistema read(). Esta funcin se comporta del mismo
modo que viene asiendo habitual en otros sistemas. Lee de un fichero con descriptor 'fd',
una cantidad de bytes pedidos sobre un buffer en memoria, a partir de la posicin actual
del apuntador del fichero, devolviendo como resultado el nmero de bytes ledos. Esta
funcin se apoya en la funcin "pos2sec()" que devuelve el sector de disco donde se encuentra una posicin de fichero dada. La dificultad principal de esta funcin se da por el
hecho de poder solicitar una cantidad cualquiera de bytes a partir de cualquier posicin
del fichero, lo que obliga a tener que leer uno o ms sectores dependiendo de donde est
posicionado el apuntador de fichero y la cantidad solicitada de bytes. Estos sectores a leer
a su vez de deben ir obteniendo a partir de los clusters que forman la cadena del fichero,
Pg. 88
Diseo e implementacin
Diseo e implementacin
do utilizada por algn otro proceso. Una vez hecha la comprobacin la unidad se monta
para que pueda trabajar sobre ella el procedimiento "chkDsk". Con la unidad ya montada,
se tiene la primera FAT cargada en memoria y ningn fichero abierto, y con estas condiciones lo primero que se hace es comprobar que las dos FAT son iguales, si no lo fueran
se emite un mensaje en pantalla y en cualquier caso se usa la FAT con la que se invoc el
comando. Seguidamente se pide memoria para ubicar una tabla de bits que servir para
reflejar el estado de cada cluster durante el proceso de chequeo. Dicha tabla tendr al
menos tantos bits como clusters tenga la FAT. Para el clculo del tamao de la tabla se
toma el numero de clusters de la FAT redondeado al siguiente CLIC (16 bytes), ya que
las peticiones de memoria dinmica as lo requieren. En esta tabla cada bit se corresponde
con un cluster, el primer bit de la tabla con el cluster nmero 1, el segundo con el cluster
2, etc. El significado de cada bit es el de informar de si su cluster correspondiente pertenece a una cadena o no. Para examinar si un bit de esta tabla vale 1 0 se dispone de la
funcin "getBit(n)" y para modificar su valor "setBit(n)". Esta tabla es usada por la funcin "checkDir()", empleada en este proceso y comentada a continuacin.
El siguiente paso importante consiste en la comprobacin de directorios y ficheros. Este
trabajo lo lleva a cabo la funcin "checkDir()", la cual acta recursivamente y se vale de
la tabla de bits comentada anteriormente. Esta funcin realiza un bucle donde recorre todas las entradas de un directorio. Si la entrada est libre se la salta y en caso contrario la
trata. El tratamiento consiste en primer lugar en iniciar un recorrido por la cadena de
clusters de la entrada. Durante este recorrido se va marcando a '1' el bit de la tabla correspondiente al cluster de la cadena analizado, comprobando previamente que no lo estuviera ya, pues en ese caso sera un error y se reportara como "cluster cruzado o en bucle" y se reparara truncando en ese punto la cadena. Una vez finalizado el recorrido, hay
que preguntar si la entrada es un directorio, en cuyo caso la funcin se llama a s misma
inicindose una recursin; en caso contrario la funcin retorna al punto de llamada. Si dicho punto fue una llamada recursiva la funcin continuara con otras entradas del directorio superior. Este proceso continuara hasta finalizar el recorrido de todos los directorios
de la unidad y devolviendo el control a la funcin principal "chkDsk()".
Una vez ejecutada la funcin "checkDir()" desde el directorio raz, en la tabla de bits estarn marcados todos los bits de aquellos clusters que pertenezcan a un fichero/directorio,
sin embargo podra haber cadenas "hurfanas", es decir, que no se puede acceder a ellas a
Pg. 90
Diseo e implementacin
partir de una entrada de directorio, que no tendrn su bits homlogos de la tabla marcados, pues no se habra accedido a ellas durante el recorrido de la estructura en rbol de directorios. Por tanto, se ha comenzar otra fase en la que hay que buscar dichas cadenas
perdidas y a ser posible, si as se indicara en el comando, recuperarlas. A continuacin se
describe qu es lo que se hace para conseguir este objetivo:
Se inicia un recorrido de la FAT secuencial y progresivo partiendo del cluster nmero 2. Durante el recorrido se omiten los clusters libres (cuyo valor es cero), y aquellos cuyo bit en la tabla est marcado (lo que significa que pertenece a un fichero/directorio). Si el cluster no cumple las condiciones mencionadas, significa que es
un cluster perdido (hurfano) y llamemos 'C' a dicho cluster, entonces iniciamos un
proceso de bsqueda del primer cluster de la cadena a la que pertenece el cluster
'C', aunque bien podra ser el nico. Para realizar esta bsqueda recorro la FAT
desde el punto donde se encontr el cluster 'C' y se avanza secuencialmente buscando un cluster, llammosle 'B', que sea justo su inmediato anterior, es decir cuyo siguiente sea precisamente el cluster 'C'. Si no se encuentra dicho cluster 'B' significa
que el cluster 'C' es el primero de la cadena, si por el contrario se encuentra, entonces se marca el bit correspondiente a dicho cluster y se repite el proceso buscando
ahora justo el inmediato anterior a 'B', y as sucesivamente hasta encontrar el principio de la cadena. Al final, adems de haber encontrado el principio de la cadena,
se habrn marcado todos los bits homlogos de todos los clusters previos al cluster
que llamamos 'C'. Una vez realizado este paso, nos resta continuar con un recorrido
de la cadena de clusters partiendo del cluster 'C', durante el cual se van marcando
los bits homlogos de los clusters de la cadena. Concluida esta fase, tendremos una
cadena perfectamente localizada y slo quedar crear una entrada de directorio a la
que adjudicarle la misma, quedando con ello recuperada la misma. Con todo esto se
habra efectuado el tratamiento de recuperacin de la cadena a la que pertenece el
cluster 'C', pero evidentemente habra que continuar el proceso inicial, lo cual se
hace partiendo del siguiente cluster a 'C' en orden secuencial en la tabla FAT y repitiendo todo lo ya contado hasta finalizar completamente el recorrido secuencial de
toda la FAT. Hay que aadir un pequeo pero importante detalle, si durante el proceso se produce algn cruce de cadenas, entonces se trunca la cadena que se estuviera recomponiendo.
Pg. 91
Diseo e implementacin
Las llamadas al sistema se encuentran implementadas principalmente en el mdulo "llamadas.c/h", no obstante, algunas de ellas utilizan funciones auxiliares que se encuentran en otros
mdulos y que en muchos casos se encargan prcticamente de todo. En este mdulo se encuentra
la funcin primordial de atencin y despacho de las llamadas soportadas por el sistema. Esta
funcin est implementada como tipo "void interrupt()". Este tipo caracterstico de Turbo-C se
usa cuando se desea instalar una funcin en un vector de interrupcin, y es el mtodo comnmente empleado para instalar las RTI's tpicas, como la de teclado o reloj. En este caso lo que se
ha hecho es utilizar un vector (60H) de los disponibles en el sistema para instalar en l la rutina
de servicio del sistema operativo, "void interrupt rti_SO()". En el fichero llamadas.h se encuentran los prototipos de las funciones que se usan para instalar y desinstalar este vector de interrupcin. Estas funciones, inicLlamadasSO() y restablecerLlamadas(), se utilizan durante
el arranque del sistema, para la instalacin del vector, y al finalizar el sistema, para su desinstalacin. Al igual que sucede con los otros vectores como los del teclado o reloj.
El trabajo de la funcin de reparto de llamadas al sistema, rti_SO(), es bastante sencillo: En
primer lugar establece una nueva pila dentro de SO, antes de hacer cualquier otra cosa, y restaurarla justo antes de finalizar. Hecho esto esta funcin bsicamente lo que hace es determinar el
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 92
Diseo e implementacin
cdigo de operacin, que recibe en el registro AH pasado en la pila del proceso invocante, y en
funcin de ste, llama a la rutina encargada de llevar a cabo el servicio. Todas las rutinas de servicio estn definidas del mismo modo, y existe una tabla que contiene todas las direcciones de
estas funciones. El nmero de ndice que ocupa en la tabla cada funcin se corresponde exactamente con el cdigo de operacin. De este modo para invocar la rutina apropiada basta con utilizar la sentencia "(*dirLlamada [cod_op])()", donde "dirLlamada" es el nombre de la tabla que
contiene los punteros a funcin de las funciones de servicio y "cod_op" es el cdigo de operacin
recibido en el registro AH. En el caso especial de que el cdigo de operacin sea superior a 80h,
el servicio lo atender el proceso servidor de ficheros y no se bifurca del modo habitual si no con
una sentencia "if" previa. La funcin "so_serv()" es la encargada de atender estas peticiones y lo
que hace bsicamente es meter en la cola del proceso servidor la peticin y dejar al proceso de
usuario que la solicit bloqueado en espera "BLK_SERV". Si el proceso servidor hubiera estado
bloqueado en el momento del encolado entonces se le desbloqueara dejndolo preparado, para
que pueda ser planificado y con ello atender la peticin.
Las funciones de servicio cuya direccin se encuentra en la tabla reciben todas ellas los parmetros de entrada en los registros copiados en la pila del proceso de usuario. Para facilitar el acceso
a los mismos se ha declarado la macro "RGP(N,R)", cuya declaracin es:
#define RGP (N, R) (tblProc[N].sp->R)
Esta macro nos permite acceder a cualquier registro guardado en la pila usando para 'N' el nmero de proceso y para 'R' el nombre de un campo de una 'union' de 'C' declarada en "tipos.h". Por
ejemplo para acceder al registro AX se pondra A.X y para acceder al registro AH se pondra
A._.H. Ej. RGP (npr, A.X). A continuacin se detallan las funciones de servicio que dan soporte
a las llamadas al sistema actuales:
(AH=0). so_finProceso(): Llama a killProcess.
(AH=1). so_matarProceso():
(AH=2). so_leerTecla(): Llama a "LeerTeclaLista()". Si esta funcin no retornara ninguna
tecla y se opt por llamada bloqueante, se deja al proceso bloqueado.
(AH=3). so_printCar(): Muestra un carcter en la posicin del cursor de la ventana del
proceso en ejecucin.
Pg. 93
Diseo e implementacin
Pg. 94
Diseo e implementacin
(AH=0Eh). so_enviaMsjbuzon: Este servicio implementa el envo de un mensaje de longitud fija a un buzn de capacidad fija. Estos tamaos estn fijados previamente por el sistema en tiempo de compilacin, as como tambin el nmero mximo de buzones que se
implementa mediante una tabla. El comportamiento es sencillo. Si el mensaje enviado entra en el buzn por haber sitio en ste, el proceso deposita el mensaje en el buzn y contina su ejecucin, si por el contrario el buzn est lleno, entonces el proceso quedar
bloqueado por "BLK_BUZON" y aadido a la cola del buzn. No saldr de este estado
hasta que le toque el turno de ser despertado, cuando algn otro proceso al recibir un
mensaje de este buzn, habilite sitio en el mismo y haga que se despierte un proceso de la
cola del buzn. Los detalles de implementacin se pueden ver en el cdigo pues est
ampliamente comentado.
(AH=0Fh). so_recibeMsjBuzon: Este servicio es recproco al anterior. Si al intentar recibir
un mensaje de un buzn, el buzn no est vaco, el mensaje es extrado del buzn y entregado al proceso receptor, continuando ste su ejecucin. Si por el contrario estuviese
vaci, entonces el proceso quedar bloqueado por "BLK_BUZON" y puesto al final de la
cola del buzn. De este estado saldr cuando otro proceso haga un envo a este buzn y le
toque el turno de ser despertado, en cuyo caso se le entregar el mensaje y se le pondr en
la cola de preparados.
(AH=10h). so_fork: Esta llamada al sistema es ampliamente conocida en el entorno de
UNIX. Sirve para clonar un proceso. Normalmente un proceso conocido por "padre" invoca esta llamada y a resultas de la misma, se crea un nuevo proceso nuevo llamado
"hijo", el cual tiene el mismo cdigo, datos y pila que su predecesor y su ejecucin contina, al igual que su "padre", justo despus de esta llamada, con la diferencia de que al
proceso padre se le devuelve el identificador del proceso hijo (pid) y al proceso hijo se le
devuelve el valor 0. Los pasos groso modo seguidos por esta funcin son:
o Buscar un descriptor libre para el hijo en la tabla de procesos.
o Copiar el descriptor del proceso padre en el del hijo.
o Pedir la cantidad de memoria que ocupe el padre para el hijo.
o Copiar la imagen de memoria del padre en la del hijo.
o Actualizar contador de referencias de ficheros abiertos.
Pg. 95
Diseo e implementacin
o Modificar los valores que cambian en el descriptor del hijo. El pid, el pPid,
el puntero a trama de pila, sp (solo la parte segmento), y las copias de los registros CS, DS y ES guardadas en dicha trama de pila.
o Incrementar el nmero de procesos y dejar en preparados el proceso hijo.
o Retornar al padre el pid del hijo y poner en la trama de pila del proceso
hijo, en la posicin del registro AX, el valor 0, para que en su momento se le devuelva este valor al proceso hijo.
El proceso hijo hereda los ficheros abiertos y tambin la ventana terminal. El cdigo
se copia en una nueva rea de memoria pero una posible mejora podra consistir en no
duplicar dicho cdigo sino compartirlo.
(AH=11h). so_tomaMem: Solicita memoria al sistema. La funcin homloga TomaMem(),
que se encuentra implementada en el mdulo memoria.c/h recorre la lista de huecos
buscando uno de tamao igual o superior al solicitado y si lo encuentra devuelve su direccin base (en paragraphs). Si sobra memoria en el hueco hallado, la deja en el mismo,
modificando la lista de huecos para reflejar la nueva direccin del hueco resultante. Si el
tamao del hueco hallado fuera exactamente igual al solicitado, se elimina el hueco. Si no
se encuentra ningn hueco de tamao suficiente, la funcin retornara 0.
(AH=12h). so_sueltaMem: Devuelve memoria al sistema. Es una funcin reciproca a la anterior. Al devolver memoria al sistema puede suceder que el bloque devuelto tenga uno,
dos o ningn hueco adyacente a l, si no tiene ninguno, entonces se genera un nuevo hueco, si tiene uno, tanto si su direccin es anterior o posterior a la direccin de la memoria
devuelta, sta se fusiona con el hueco adyacente, y si por ltimo tiene dos huecos adyacentes, entonces ambos huecos se fusionan con la memoria devuelta y como resultado se
produce un solo hueco donde antes haba dos, reducindose en este caso el nmero de
huecos.
(AH=13h). so_test: Esta llamada tiene nicamente el propsito de ser usada para realizar
pruebas durante la fase del desarrollo del sistema.
(AH=14h). so_printEntDir: Muestra el contenido de una entrada de directorio por la ventana terminal del proceso actual.
Existe adems otra RTI en el sistema 'SO', la funcin void interrupt rti_AuxSO(). Esta funcin presta un servicio de apoyo a los procesos de usuario aportando funciones "auxiliares" y se
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 96
Diseo e implementacin
instala en el vector siguiente al de llamadas al sistema. Actualmente slo incluye la funcin auxiliar so_leerLinea(), la cual se trata de forma anloga a cualquier otra llamada, aunque no igual,
si bien para los procesos de usuario no hay prcticamente ninguna diferencia. La diferencia fundamental para el sistema 'SO' es que en la RTI no se establece una nueva la pila dentro del sistema, sino que se deja la de usuario, lo que permite invocar a la RTI de llamadas al sistema desde
dentro del propio sistema, evitndose un problema de reentrancia. Por ejemplo, la funcin auxiliar so_leerLinea(), puede llamar al servicio de 'SO' leerTecla(), el cual puede dejar bloqueado al proceso. Esto puede hacerse, an a pesar de que al invocar a leerTecla() se haga desde
dentro del sistema, porque la pila contina siendo la del proceso de usuario, y esto permite que el
mecanismo usado para bloquear un proceso siga siendo vlido.
Otro aspecto importante a sealar en este mdulo es el referente a los servicios relacionados con
ficheros que atiende el proceso servidor. La funcin "srv_main()" constituye el cuerpo principal
del proceso servidor. Esta funcin contiene un bucle sin fin en el cual se est continuamente
atendiendo peticiones de servicio de ficheros. En el bucle se pregunta si hay alguna peticin en la
cola de asociada al servidor y de ser as se extrae la peticin de la cola y sta se despacha, invocando la rutina de servicio asociada a la misma. El nmero de proceso que ha efectuado la peticin y que permanece bloqueado se guarda en la variable del sistema "nprAtnd", la cual se usa en
algunas partes del cdigo de ficheros para conocer qu proceso realiz la peticin, ya que la variable "nprEjec" no puede usarse para dicho fin al ser siempre en este caso su valor la del proceso
servidor. Una vez atendida la peticin se elimina sta de la cola del servidor y se desbloquea al
proceso de usuario ponindolo en la cola de preparados para que pueda continuar con su ejecucin. Si por otra parte en el bucle, al comprobar si hay alguna peticin en la cola, se detecta que
la cola est vaca, entonces el proceso servidor se duerme indefinidamente con razn de bloqueo
"BLK_PAUSE". De este estado saldr cuando un proceso de usuario al hacer una peticin al
sistema, provoque que la funcin de servicio "so_serv()" despierte al servidor, tal y como ya se
ha comentado anteriormente.
El mecanismo de despacho de servicios es distinto al empleado en la RTI de llamadas al sistema.
En este caso, y como modo alternativo, se utiliza una sentencia "switch" en vez de una tabla de
punteros, y los cdigos de operacin de estos servicios empiezan en 80h. Las llamadas al sistema
relacionadas con ficheros que atiende el servidor son:
Pg. 97
Diseo e implementacin
Las rutinas de interfaz de llamadas se encargan bsicamente de trasladar los parmetros de entrada que en "C" se reciben en la pila, a los registros pertinentes del procesador donde los espera
la RTI de servicio del S.O., y por supuesto de invocar a dicha RTI, para tras retornar sta, devolver el resultado que se encuentra en el registro AX en la pila, aunque el empleo de la pila es
implcito por el uso del lenguaje "C". Cada rutina de interfaz es responsable de introducir el
cdigo de operacin correcto asociado a la llamada en el registro AH, as como tambin utilizar
los registros convenidos para paso de informacin a la RTI de servicio del sistema. Existe lgicamente una rutina de interfaz por cada llamada al sistema.
Estas rutinas forman parte del proceso de usuario, y al compilar un programa de usuario se incluyen en el mismo. Por tanto no tendran por qu formar parte del sistema operativo, sin embargo, puesto que se ha incluido un intrprete (consola) dentro del sistema, y ste hace uso de estas
funciones, como cualquier otro proceso de usuario, al final el cdigo de estas funciones tambin
forma parte de 'SO'.
El cdigo de las rutinas de interfaz se haya distribuido en varios ficheros que se encuentran ubicados en un subdirectorio interno llamado "LL_S_SO". En este subdirectorio se encuentran los
siguientes ficheros: "bsic-ifz.c/h", "memo-ifz.c/h", "proc-ifz.c/h", "fich-ifz.c/h" y "test-ifz.c/h". A
continuacin se detallan las rutinas de interfaz que contiene cada uno de estos ficheros:
Pg. 98
Diseo e implementacin
Resultados
6. RESULTADOS
Pg. 100
Resultados
USRS_PRG: Contiene los ficheros fuente de las aplicaciones de usuario. Estas aplicaciones se vern ms adelante:
Otro resultado de la ejecucin de este proyecto ha consistido en poder llevar a cabo el desarrollo
de unas prcticas de laboratorio para su realizacin por parte del estudiante, lo que era adems
uno de sus principales objetivos. Estas prcticas han consistido por lo general, en solicitar al estudiante que lleve a cabo una modificacin o mejora en el sistema. Ms adelante, en uno de los
apartados de este captulo, haremos una breve descripcin de algunas de ellas.
Por otro lado; con el nimo de probar, depurar y mostrar la funcionalidad del sistema operativo,
adems de llevar a cabo un cometido; se han programado una serie de comandos que interpreta y
ejecuta el proceso "consola", el cual se encuentra integrado en el sistema operativo; y tambin
una serie de aplicaciones de usuario, que corren en el espacio de usuario y hacen uso de las llamadas al sistema. Estos comandos y aplicaciones se detallan en los siguientes apartados de este
captulo.
La "consola" es un proceso del sistema que realiza las funciones de intrprete de comandos, con
funciones de administracin del sistema. Al contrario que los intrpretes de comandos en el espacio de usuario, la consola incluye algunos comandos que son exclusivos, debido principalmente a que stos utilizan funciones internas; no disponibles como llamadas al sistema. Cuando el
cdigo del sector de arranque (boot ) cede el control de ejecucin al sistema operativo, lo primero que se hace es llevar a cabo la inicializacin del Sistema. Entre otras cosas, se establecen determinados vectores de interrupcin, se inicializan determinadas estructuras de datos del Sistema
y eventualmente se prepara el proceso "consola" (CNSL), para que tome el control de la ejecucin y comience a realizar su tarea bsica, la cual consiste, tal y como ya se ha indicado, en interpretar y ejecutar comandos y en su momento, tras el comando pertinente, finalizar el sistema.
El fichero del sistema donde se encuentra este proceso (CNSL) es 'so.c', el cual es adems, el que
contiene la funcin "main()" con la que se inicia sistema operativo. Esta funcin comienza invocando las funciones de inicializacin de las ventanas (inicWindows), memoria, (inicMemoria),
ficheros (inicFicheros), procesos (inicProcesos) y llamadas al sistema (inicLlamadasSO). Estas
Pg. 101
Resultados
Pg. 102
Resultados
nes inhibidas. 2) Recorrido de la tabla de ficheros de la consola cerrando cada uno de ellos.
3) Se desmontan las unidades montadas. 4) se Elimina la ventana terminal de la consola. 5)
Se restablecen los vectores de interrupcin establecidos durante la inicializacin del sistema.
Este paso slo es necesario si el sistema corre como invitado de MS-DOS (aunque no lo usa),
sin embargo se realiza siempre pues en cualquier caso no es malo, por ltimo, 6) se retorna al
sistema anfitrin o se reinicia el sistema dependiendo del caso.
o CLS: Borra la pantalla. Lo hace mediante printCar(FF), el carcter "Form Feed".
o DIR: Muestra el contenido del directorio que se le especifica o del directorio actual de trabajo
si no le especifica ninguno. La funcin que lo implementa 'Dir()" est declarada e implementada en este mismo fichero. Esta funcin utiliza las llamadas al sistema "buscaPriEntDir" y
"buscaSigEntDir", y constituye un buen ejemplo del uso de estas funciones.
o MEM: Muestra la direccin de comienzo de la memoria de asignacin dinmica y la lista de
huecos disponibles. La funcin que implementa el comando es "mostrarMemoria() y se encuentra implementada en el fichero "memoria.c". Los detalles de cada elemento de la lista
mostrado son: direccin de comienzo de cada hueco, tamao en paragraphs y direccin del
siguiente hueco.
o LSV: Muestra la lista de ventanas. La funcin que lo implementa es "listaWin()" y se encuentra en el fichero "windows.c". Los detalles de cada elemento de la lista mostrado son: Direccin del objeto ventana, direccin de la ventana que se encuentra justo arriba, direccin de la
ventana que se encuentra justo debajo y el nombre con el que se cre la ventana.
o TFA: .Este comando muestra los ficheros abiertos de la tabla informativa de de ficheros. La
funcin que lo implementa, "listarTFAbiertos()", se encuentra en "ficheros.c". la informacin
que se muestra es: descriptor de fichero, nombre, extensin, atributos, primer cluster, tamao,
sector y nmero de entrada dentro del sector donde se encuentra el fichero.
o PS: Muestra los procesos vivos del sistema, El comando los implementa la funcin "listarProcesos" que se encuentra en "procesos.c". La informacin mostrada es: el nmero de proceso
(ndice dentro de la tabla de procesos), el identificador de proceso (pid), el estado (preparado,
ejecucin, etc.), el siguiente proceso de la posible cola en la que se encuentre el proceso, la
direccin de memoria donde se ha cargado el proceso, la cantidad de "paragraphs" que ocupa el proceso, los registros CS, IP, DS, SS y SP y flags del procesador, nmero de caracteres
del buffer de teclado de su ventana terminal y por ltimo nombre del proceso. La funcin obPg. 103
Resultados
Pg. 104
Resultados
o COMPAC: Realiza una compactacin o desfragmentacin de la memoria. Este proceso consiste en mover los bloques de memoria asignados, como sea preciso, para conseguir que todos los huecos se unan formando uno slo. Los detalles se han explicado con anterioridad en
el captulo de gestin de memoria (ver #Compacta).
o DESMON: Desmonta una unidad de disco liberando la memoria ocupada para la FAT de la
misma. Esta implementada mediante la llamada al sistema desmontaDrv. La operacin no
puede llevarse a cabo si el sistema mantiene algn fichero abierto en la unidad
o TYPE: Muestra el contenido ASCII de un fichero. Esta implementado mediante la funcin
"type()" que se encuentra en el propio fichero "so.c". Esta funcin se limita a abrir el fichero,
para luego ir leyndolo y mostrndolo en pantalla, cerrndolo finalmente.
o TST: Este comando se utiliza exclusivamente para realizar pruebas.
o AYUDA: Muestra la lista de comandos disponibles y los parmetros admitidos en cada uno
de ellos.
Resultados
en el captulo de gestin de ficheros (ver #chkDsk). Para que se pueda hacer la comprobacin
la unidad tiene que ser desmontada. Si no se logra, no se realiza el chequeo. El comando admite que se le especifique en los parmetros: la unidad a comprobar, si se desea reparar (/F) o
slo comprobar, y cul de las dos FAT se desea considerar como buena (1 2). De forma
implcita se consideran: la unidad de trabajo, la FAT 1 y el modo "slo comprobacin.
o Adems de los comandos vistos hasta ahora, el interprete tambin admite el cambio de la unidad de trabajo introduciendo la letra de la unidad seguida de ":", ej. A: . Cualquier otro comando se interpretar como la ejecucin de un fichero. El fichero puede tener la extensin
"bin" que no es necesario teclear. Ej.: A:>errante <.
Dentro del directorio "MiSO", que contiene las fuentes del proyecto, se encuentra el subdirectorio "USRS_PRG", el cual contiene el cdigo de las aplicaciones de usuario que se han desarrollado para demostrar la funcionalidad del Sistema Operativo, y a la vez de servir de ejemplo para
la realizacin de otros programas.
Antes de ver uno a uno los procesos de usuario, se va a explicar el proceso seguido para su compilacin. En el directorio "USRS_PRG" se encuentra el comando batch "compila.bat" que contiene las rdenes necesarias para compilar un programa de usuario. Una de las ordenes invoca al
compilador: "tcc B C -1 k- -c mt w+pro g20 j10 %1.c". La informacin sobre las opciones de compilacin empleadas se puede obtener de la documentacin interna del compilador
"Turbo-C". Despus de compilar, el proceso por lotes hace una pausa que permite examinar los
errores de compilacin. El proceso por lotes se reanuda al pulsar la tecla "INTRO", y entonces se
ejecuta la lnea "tlink /c/s %1.obj,,,\tc30\lib\cs.lib", lo que lleva a cabo la fase de montaje. Una
vez generado el ejecutable tras el proceso de montaje (link), se ejecuta la lnea "exe2bin %1.exe
%1.bin", lo que convierte el fichero ".exe" en un fichero ".bin". Este ltimo paso elimina la cabecera del fichero ".exe". Las dems lneas son opcionales. Seguidamente vemos un ejemplo de
compilacin del programa de usuario "hola.c". En l se presupone que el fichero "hola.c" se encuentra en el directorio "HOLA".
Pg. 106
Resultados
Pg. 107
Resultados
o Errante: Es un programa que se limita a solicitar un valor numrico entre 0 y 9, el cual indica
la velocidad a la que se tiene que desplazar la ventana del proceso por la pantalla describiendo una espiral cuadrada. Entre un movimiento y otro el programa realiza una espera activa.
Es decir consume CPU. Al final de una serie de vueltas el programa finaliza.
o Erraslp: Es un programa similar a "errante", pero en este caso la espera no es activa. sta se
hace mediante una llamada a "sleep", lo que libera la CPU. Este efecto puede constatarse
fcilmente si se ejecutan varios programas "Errante/Erraslp" y se ejecuta el comando "type"
para mostrar el contenido de un fichero de texto. En el caso de varios errantes el contenido
del fichero se muestra a saltos, pues cuando le toca el turno a "type", ste muestra el contenido y cuando le toca el turno a los otros procesos, como consumen CPU en su turno y hay varios, el tiempo empleado por ellos es significativo y se percibe la pausa forzosa del comando
"type". En el caso de "Erraslp", al ser una espera con bloqueo, la CPU se libera y puede asignarse al comando "type", que prcticamente no nota las interrupciones de los otros procesos,
y por tanto lo que se muestra en pantalla aparece sin saltos.
o Menuexc: Este programa nos permite probar las excepciones de overflow y divisin por cero.
o Menusem: Este programa nos permite probar los semforos. Nos solicita un nmero de semforo, un valor para el mismo y una operacin de subir o bajar. Con todo esto podemos comprobar cmo un proceso que quiere bajar un semforo que no puede, se queda bloqueado, y
tambin, a la contra, si sube un semforo el cual tiene bloqueado a algn proceso, podemos
ver cmo dicho proceso se desbloquea y contina su ejecucin.
o Cross: programa similar al errante pero con uso de semforos. Este programa utiliza el valor
6 para inicializar dos semforos que consultarn las instancias de este programa para determinar si las vueltas cuatro y ocho del circuito estn ocupadas por algn proceso. Si se ejecutan varias instancias de este programa y una de ellas pone como velocidad '6' y las dems
cualquier otro valor, entonces todas las ventanas empezaran a moverse realizando la espiral
del circuito. Cuando cualquiera de ellas intenta entrar en la vuelta cuatro, realiza previamente
la operacin "bajar semforo", si lo consigue, realiza la vuelta y al finalizarla, efecta la operacin "subir semforo". Si durante el trayecto de dicha vuelta hubiera intentado entrar cualquier otro proceso, se habra quedado bloqueado en la operacin bajar, y al salir el proceso
que entr en la vuelta y suba el semforo, provocar que el proceso bloqueado despierte y entre en la vuelta. Lo mismo sucede con la vuelta ocho usando otro semforo.
Pg. 108
Resultados
o Menumsj: Programa demostracin del paso de mensajes. El programa nos permite seleccionar un buzn (B), por defecto 0. Nos permite escribir un texto para mensaje (M), realizar una
operacin de envo (E) o recepcin (R) de un mensaje, y/o terminar (T). Todo ellos nos permite probar el envo y recepcin de mensajes a/de un buzn y tambin comprobar cmo se
bloquea/desbloquea un proceso cuando se realizan dichas operaciones.
o Tsfork: Programa demostracin de la llamada al sistema fork.
o Sh y Sh-fork: Estos programas son una Shell o intrprete de comandos en el espacio de usuario. Implementa muchos de los comandos de la consola de SO, aunque no todos. La diferencia entre ellos es que "Sh" ejecuta los programas utilizando la llamada "runFichero" y "Shfork" utiliza "fork". El comportamiento difiere en que "runFichero" ejecuta el proceso en una
ventana nueva y propia y "fork" hereda la ventana del proceso padre y por tanto la comparte
con l.
En la figura siguiente se muestran algunas de estas aplicaciones de usuario ejecutndose bajo el
sistema 'SO'.
Pg. 109
Resultados
En este apartado vamos a comentar brevemente algunas de las prcticas que se han desarrollado
especficamente para este sistema operativo.
Prctica sobre manejo de excepciones. En esta prctica el estudiante aprender a tratar o manejar las excepciones. Concretamente se estudiarn las excepciones "divisin por 0" y "desbordamiento (overflow)". Esta prctica est muy relacionada con la anterior y es bastante similar salvo
por los vectores de interrupcin y el evento que los causa.
Prctica sobre la llamada "sleep". En esta prctica el alumno aprende a incorporar al sistema
una nueva llamada al sistema, y en particular la conocida llamada "sleep" de Unix. Para llevar a
cabo el trabajo el estudiante debe aprender a bloquear un proceso y despertarlo transcurrido un
tiempo, para lo cual debe modificar las RTI's de servicio del sistema y del reloj. Tambin aprende acerca de las libreras de interfaz de usuario de llamadas al sistema.
Prctica sobre semforos. En esta prctica, ya con el conocimiento adquirido acerca de la incorporacin de una nueva llamada al sistema, el estudiante aprende de forma prctica la implementacin de las operaciones bsicas sobre semforos: "iniciar, subir y bajar".
Pg. 110
Resultados
Prctica sobre el proceso de arranque del sistema (boot). Esta prctica no trata sobre la mejora o
modificacin de ningn aspecto del sistema operativo, sin embargo, es fundamental entender el
proceso de arranque en cualquier sistema operativo, y para ello, en este trabajo, el estudiante
adquiere los conocimientos necesarios para realizar un arranque "en fro" del sistema, lo que
adems es vlido para muchos otros sistemas.
Pg. 111
Conclusiones
7. CONCLUSIONES
En este captulo se muestran: primero las conclusiones que se han obtenido como resultado de la
realizacin de este proyecto y a continuacin, las lneas de trabajo futuro a seguir para su ampliacin y mejora.
Como primera conclusin podemos decir que se ha conseguido cumplir el objetivo principal del
proyecto, consistente en la realizacin de un pequeo sistema operativo orientado a fines didcticos. El sistema conseguido es pequeo, manejable y de una relativa sencillez. El sistema tiene
adems entre sus cualidades, el haber demostrado que se pueden realizar unas "prcticas" para
alumno, acerca de conceptos bsicos e importantes en la comprensin de los "sistemas operativos". Por otro lado el sistema es suficientemente complejo y completo como para permitir la
realizacin de nuevos trabajos prcticos dirigidos a la enseanza, que abarquen muchos otros
aspectos de los sistemas operativos de igual o incluso mayor relevancia.
Pg. 112
Conclusiones
Otra conclusin es que este tipo de proyectos, que en principio pueden ser abordables por una
sola persona si se les limita suficientemente su funcionalidad, cuando se llevan a la prctica, es
fcil que se tornen ms complejos de lo previsto y se produzcan frecuentemente errores de anlisis y de programacin difciles de depurar, con lo que los tiempos de desarrollo se incrementan
en gran medida y de forma poco previsible. En estos sistemas es corriente que a medida que crece su tamao y complejidad la introduccin de nuevas caractersticas, o simplemente la correccin de los errores detectados, introduzca nuevos errores, aunque este hecho no ha llegado a ser
muy preocupante en este sistema, dado que la complejidad del mismo se ha intentando mantener
lo ms baja posible y ste era uno de los objetivos principales del proyecto; no obstante; este
indeseable efecto se ha hecho notar.
Tambin podemos extraer otra conclusin relativa a las decisiones tomadas sobre el diseo del
sistema operativo. Esta consiste en sealar la razn por la que no se ha buscado un diseo moderno o elegante, sino ms bien uno anticuado como el "monoltico". Este ltimo ha resultado ser
el ms sencillo de cara a su implementacin y facilidad de depuracin en un entorno cmodo
como lo es el ofrecido por las herramientas de desarrollo escogidas. Es por ello por lo que se han
sacrificado diseos ms actuales, que aunque sin duda hubiesen sido mejores candidatos para
realizar un sistema de uso ms general, no lo han sido en este caso, pues como ya se ha dicho
repetidas veces, el objetivo principal siempre fue el uso del sistema como herramienta de apoyo
para la enseanza de los fundamentos de los "sistemas operativos".
Por ltimo, y tambin como conclusin, cabe sealar lo prctico que ha resultado ser el sistema
una vez aplicado a la enseanza gracias a las herramientas de trabajo requeridas, que son simples, de sencillo manejo, ampliamente disponibles y de coste muy econmico o nulo en algunos
casos, como son el emulador: "DOSBox" con entorno "D-Fend", el compilador "Turbo C", y
otras varias de cdigo abierto (open source). Todo ello ha permitido el montar unas prcticas en
las que el esfuerzo de aprendizaje del alumno se ha focalizado principalmente en el conocimiento de los entresijos del sistema operativo y no de las herramientas empleadas. Por otro lado, hay
que sealar que el desarrollo de las "prcticas" ha servido en gran medida para probar el sistema,
ya que stas han sido realizadas en la actualidad, no slo por el autor sino tambin por muchos
otros estudiantes, que en algunas ocasiones han aportado ideas, o simplemente han cometido
errores que el sistema no tena contemplados, y por ello han servido para que el autor efectuase
cambios en forma de revisiones y/o mejoras del sistema.
Pg. 113
Conclusiones
En principio habra que decir que en un proyecto como este, la lista de trabajos de ampliacin y
mejora a realizar en un futuro se podra hacer interminable, sin embargo, dado que el objetivo es
mantener el sistema lo ms simple posible, slo se van a comentar algunas de las caractersticas
que se podran incorporar en un futuro al sistema sin que ello lo volviera demasiado complejo.
Incorporar nuevas llamadas al sistema similares a las que ya ofrecen los sistemas UNIX
como: wait, signal, sigaction, etc.
Aumentar la lista de comandos internos.
Gestin de ficheros especiales de dispositivo.
Ofrecer posibilidad de redireccionar la entrada y la salida.
Ofrecer comunicaciones al menos por el puerto serie y paralelo y deseablemente por red
Ethernet.
Incorporar el dispositivo ratn, bien mediante el puerto RS232 o PS/2.
Gestin de ficheros especiales (dispositivos consola, impresora, rs232, etc.).
Diseo del sistema para permitir que los drivers se instalen dinmicamente.
Etc.
Otras ideas para la mejora del proyecto podran incluir la realizacin de programas o herramientas de ayuda para la enseanza que se apoyen en el sistema operativo, como por ejemplo un programa para la monitorizacin de procesos en ejecucin. Este programa podra correr en una maquina fsica o virtual distinta y sera capaz de recibir informacin procedente del sistema 'SO''
acerca de los procesos actualmente en ejecucin a travs de la lnea de comunicaciones serie,
paralelo o Ethernet; e ira mostrando un grfico con un cronograma de los eventos que se producen en el sistema. De este modo el estudiante podra observar el comportamiento del sistema,
viendo cmo evoluciona el estado de los procesos, los eventos que se producen, etc. De hecho
este proyecto ya se encuentra implementado (no por el autor) y en fase de pruebas.
Por ltimo y como una tarea quiz algo ms compleja pero tambin muy interesante, sera la
adaptacin del sistema para corriese en el modo protegido del procesador 80386 y modelos superiores, para de este modo, crear as un sistema operativo que se pudiese beneficiar de las caractersticas avanzadas que ofrece dicho modo protegido.
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 114
Conclusiones
7.3 Presupuesto
En este apartado se hace un pequeo anlisis sobre los costes tericos que ha requerido este proyecto fin de carrera.
En general, es corriente que previo a la realizacin de cualquier proyecto, se haya hecho antes un
presupuesto para valorar su coste, sin embargo, para el caso particular de este proyecto fin de
carrera, por diversas razones, no se ha hecho as, procediendo, a posteriori y en primer lugar, a
efectuar una valoracin que pretende ser ms o menos objetiva, utilizando algunas de las herramientas disponibles en la actualidad para su medicin y valoracin; y en segundo lugar, se efectuara una valoracin algo ms subjetiva, y desde el punto de vista del autor.
7.3.1
Conclusiones
subjetiva del producto, hardware, personal, etc. Por ltimo el modelo "detallado" tiene en cuenta
la valoracin de los atributos en cada una de las fases de desarrollo del proyecto.
Computing results.
SLOC Directory SLOC-by-Language (Sorted)
3108
top_dir
cpp=3108
632
USRS_PRG
cpp=632
333
LL_S_SO
cpp=333
0
MI_C0
(none)
Totals grouped by language (dominant language first):
cpp:
4073 (100.00%)
Total Physical Source Lines of Code (SLOC)
=
Development Effort Estimate, Person-Years (Person-Months) =
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
Schedule Estimate, Years (Months)
=
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
Estimated Average Number of Developers (Effort/Schedule) =
Total Estimated Cost to Develop
=
4,073
0.87 (10.49)
0.51 (6.11)
1.72
$ 118,046
El
para su desarrollo son: 10,49 / 6,11 = 1,72, y el coste total del proyecto tomando como salario
mensual el que utiliza el programa que es de: 50.286 /12 = 4.690,5 $, ser el resultado de la ope-
Pg. 116
Conclusiones
El programa "sloccount" tiene ciertas limitaciones al contabilizar las lneas de cdigo, por ejemplo slo contabiliza lneas de cdigo fsicas, no lgicas. Otros programas en cambio si lo hacen.
Entre ellos se ha utilizado el programa "ucc release 2011.03", (Unified CodeCount)" para al menos disponer de una alternativa a la medicin.
A continuacin se muestra slo un resumen del resultado obtenido mediante dicho programa. En
l se observa que el nmero de lneas fsicas es diferente del obtenido con "sloccount", aunque la
diferencia no es demasiado grande. Tras comprobar manualmente algunas de las diferencias entre ellos en el resultado detallado por ficheros, parece ser que este ltimo es ms preciso, por lo
que se reajusta el clculo mediante las mismas frmulas y utilizando las lneas lgicas en vez de
las fisicas, se obtendra un esfuerzo hombre/mes de
total sera de 132.722 $ . Por otro lado, si tomamos como salario anual para un analista programador con experiencia, en la actualidad y en Espaa, el valor de 50.400 , obtenido tras haber
consultado diversas fuentes, el coste total del proyecto sera: 4.200 x 11,79 x 2.4 = 118.843
File
Type
CODE
CODE
SLOC
Definition
Physical
Logical
Pg. 117
Conclusiones
http://sunset.usc.edu/csse/research/COCOMOII/cocomo_main.html y
http:// www.sc.ehu.es/jiwdocoj/mmis/cocomo.htm.
Al coste del proyecto visto hasta ahora habra que aadirle los gastos en material tanto software
como hardware que se han requerido para su realizacin. En lo referente al hardware, lo nico
requerido ha sido un ordenador personal del tipo medio que se podra valorar en torno a los
750, y en lo referente al software, no hara falta aadir ningn coste, pues todo el software empleado es del tipo "freeware" o similar, es decir de coste 0.
7.3.2
En el punto anterior hemos visto una estimacin ms o menos objetiva y simplificada utilizando
herramientas de medicin de lneas de cdigo, sin embargo dado que la valoracin del proyecto
se ha efectuado una vez finalizado ste, en principio sera posible saber con bastante precisin la
cantidad de tiempo empleado por el autor en el desarrollo del mismo, no obstante, dada la forma
en la que se ha llevado a cabo el proyecto, que ha sido poco uniforme en el tiempo, y tambin
debido a la falta de contabilidad del tiempo empleado en el desarrollo del mismo, no se dispone
del tiempo real empleado, aunque siempre es posible considerar de forma algo subjetiva el tiempo que el autor estima que ha empleado en el proyecto. La primera versin registrada en la gestin del proyecto gestionada mediante el software "git" es de febrero de 2010, pero con seguridad
dicha versin ya haba requerido bastante trabajo antes de disponer de ella. Al menos el trabajo
se inici seis meses antes. Posteriormente el sistema se fue ampliando y mejorando, invirtiendo
aproximadamente hasta el da de hoy, unos dos aos, aunque hay que decir que se estuvo trabajando todo el tiempo en el proyecto, ya que como ya se ha dicho, el desarrollo ha sido bastante
irregular, con perodos intensos de trabajo y otros de total inactividad, no obstante, la apreciacin
subjetiva del autor sobre el tiempo empleado por da laborable es de unas 3 horas de media, lo
que supondran un porcentaje de un 37% sobre una jornada aproximada de 8 horas al da. Si consideramos como tiempo total de desarrollo del proyecto dos aos y medio y aplicamos el porcentaje arriba calculado y aplicando el salario anual ya visto de 50.400 , obtendremos un coste de:
50.400 x 2,5 x 0,37 = 46.620 , antes de aplicar la sobrecarga de 2.4 puntos debidos al mantenimiento, tal y como ya se aplic en el punto anterior, en la valoracin objetiva. Una vez aadida dicha sobrecarga para una mejor comparacin entre ambas valoraciones tendremos un costeo
de trabajo de: 2.4 x 46.620 = 111.888 , lo cual no est muy alejado de la valoracin anterior.
Pg. 118
Conclusiones
A continuacin se muestra una pequea tabla de la valoracin del proyecto sin considerar el coeficiente 2'4 de factor de mantenimiento que aplica COCOMO. Tampoco se han aadido al presupuesto un porcentaje de beneficios por considerarse que ste ser de cdigo abierto:
Concepto
Importe ()
Recursos Humanos
47.250
Ordenador Personal..
750
Software (abierto)
Total presupuesto..
48.000
Por ltimo sealar que para realizar este proyecto, ha sido necesario estudiar con bastante detalle
muchos aspectos acerca del funcionamiento del compilador Turbo C y MS-DOS. Incluso ha
habido que modificarse el cdigo de inicializacin estndar de Turbo C, para as poder conseguir
que el sistema funcione tanto en modo independiente, como en modo anfitrin bajo MS-DOS y
Turbo C. Con todo ello se ha conseguido que el estudiante pueda usar el entorno integrado de
dicho compilador, y gracias a ello, beneficiarse de la edicin, compilacin y depuracin que el
entorno ofrece, logrando as, uno de los objetivos principales del proyecto.
Pg. 119
Referencias
8. REFERENCIAS
8.1 Bibliografa
Pg. 120
Referencias
Todas las pginas a la que se accede mediante los enlaces URL relacionados a continuacin estaban vigentes y activas a fecha 3 Mayo 2012.
http://www.minix3.org
http://minix1.woodhull.com
Pg. 121
Referencias
(Descarga D-Fend)
Pg. 122
Apndices
ANEXOS
Junto con esta memoria se adjunta un CD en el que se encuentra la memoria del proyecto en el
directorio raz y todo el software utilizado en el proyecto listo para ser usado, una vez copiado a
un dispositivo de almacenamiento con permiso de escritura en el directorio 'D-Fend'. En dicho
directorio se encuentran tambin todas las fuentes del proyecto, junto con el fichero ejecutable
para MS-DOS y la imagen binaria (.bin) para su instalacin en un dispositivo de arranque. En un
directorio aparte, se encuentra tambin la documentacin sobre las "prcticas" que se han diseado para ser realizadas por el estudiante sobre este sistema.
ProyectoFC.docx
(memoria del PFC en formato 'Word')
ProyectoFC.doc
(memoria del PFC en formato 'Word')
ProyectoFC.pdf
(memoria del PFC en formato 'pdf')
PresentacionPFC.pptx (presentacin del PFC en formato 'Powerpoint)
PresentacionPFC.ppt (presentacin del PFC en formato 'Powerpoint)
PresentacionPFC.pdf (presentacin del PFC en formato 'pdf')
D-Fend
(Software interfaz grfico de DOSBox)
mapper.txt
Pg. 123
Apndices
Dfend.exe
(Ejecutable entorno grfico D-Fend)
Defend.dat
AutoSetup (carpeta interna de D-Fend)
Bin
(carpeta interna de D-Fend)
Capture
(carpeta interna de D-Fend)
Confs
(carpeta interna de D-Fend)
DOSBox
(carpeta interna de D-Fend con emulador DOSBox)
IconLibrary (carpeta interna de D-Fend)
IconSets
(carpeta interna de D-Fend)
Lang
(carpeta interna de D-Fend)
Settings
(carpeta interna de D-Fend)
Templates (carpeta interna de D-Fend)
VirtualHD (carpeta disco duro para la maquina virtual)
SO.exe
(fichero ejecutable Sistema Operativo para MS-DOS)
SO60.bin
(imagen binaria Sistema Operativo para hacer boot)
Ficheros.c
(mdulo para el sistema de ficheros FAT)
Llamadas.c (mdulo para las llamadas al sistema)
Memoria.c (mdulo para la gestin de memoria)
Procesos.c
(mdulo para la gestin de procesos)
RTIcomun.c (cdigo comn a las RTIs)
SO.c
(mdulo de inicio y consola del S.O.)
Teclado.c
(mdulo para tratamiento del teclado)
TeclaInt.c
(mdulo RTI del teclado)
TimerInt.c
(mdulo RTI del reloj)
Windows.c (mdulo para la gestin de ventanas)
SO.dsk
(configuracin escritorio de Turbo C)
.gitignore
(configuracin ignore para el software git)
Ficheros.h
Llamadas.h
Memoria.h
Procesos.h
RTIcomun.h
Teclado.h
TeclaInt.h
TimerInt.h
Tipos.h
Windows.h
SO.map
(config. map proyecto SO de Turbo C)
2Bin.bat
(comando batch para convertir exe a bin)
SO.prj
(config. prj proyecto SO de Turbo C)
Ayuda.txt
(auxilar de S.O. para mostrar ayuda)
Leeme.txt
(notas internas sobre SO)
.git
(carpeta del proyecto que mantiene el software git)
LL_S_SO
(carpeta con la liberara de rutinas de interfaz)
Bsic-Ifz.c
(rutinas de interfaz bsico)
Fich-Ifz.c
(rutinas de interfaz de ficheros)
Pg. 124
Apndices
Proc-Ifz.c
(rutinas de interfaz de procesos)
Test-Ifz.c
(rutinas de interfaz para pruebas)
Bsic-Ifz.h
Fich-Ifz.h
Memo-Ifz.h
Proc-Ifz.h
Test-Ifz.h
Mi_C0
(carpeta con cdigo init en asm para Turbo C)
Disquete
(carpeta con los binarios de los programas de ususario)
Usrs_Prg
(carpeta con programas de usuario)
Prcticas
Practica-1.doc
(boot loader)
Practica-2.doc
(Interrupciones, Excepciones y llamadas)
Practica-3.doc
(paso de mensajes)
Practica-4.doc
(compactacin de memoria)
Git-1.7.7
CodeCounters
slocdata
(carpeta resultados sloccount)
outfile_cplx.csv (fichero resultados ucc)
sloccount-2.26-1.i386.rpm
ucc_2011.03.rar
Pg. 125
Apndices
NDICE
1
OBJETIVOS .....................................................................................................................127
INTRODUCCIN............................................................................................................127
Pg. 126
Apndices
1) OBJETIVOS
Los objetivos de esta prctica son:
Que el alumno conozca el nivel preciso sobre el que se implementa el sistema operativo de
un ordenador real (es decir inmediatamente por encima del nivel de lenguaje mquina y de la
BIOS).
Que el alumno sea capaz de implementar el arranque de un sistema operativo desde un disquete estndar utilizando programacin a bajo nivel en C.
2) INTRODUCCIN
Al encenderse el ordenador la CPU empieza a ejecutar instrucciones en memoria (ROM) a partir
de una direccin prefijada. En la ROM toma el control lo que se denomina la BIOS (Sistema de
Entrada/Salida Bsico) que es una especie de rudimentario sistema operativo proporcionado por
el fabricante del ordenador para poder operar con los dispositivos de E/S de una forma estandarizada. Gracias a que cada fabricante incorpora su propio BIOS a sus modelos clnicos del IBM
PC, ofreciendo todos ellos la misma interfaz de llamadas (al sistema BIOS), es posible que un
mismo programa funcione correctamente en cualquiera de esos ordenadores, a pesar de contar
con dispositivos perifricos que pueden ser completamente diferentes, y sin tener que hacer un
reconocimiento exhaustivo del hardware del ordenador.
La BIOS, una vez que toma el control, reconoce e inicializa los controladores de los dispositivos
ms usuales (teclado, tarjeta de vdeo, controladores de disquete y disco duro, etc.) estableciendo
los vectores de interrupcin (en la RAM) con las direcciones de las rutinas de tratamiento de
interrupcin correspondientes (residentes en la ROM). A continuacin la BIOS intenta cargar el
sistema operativo desde alguno de los dispositivos de E/S que ha reconocido (disquetera, cdrom,
disco duro, usb o tarjeta de red). En caso de que ninguno de esos dispositivos aporte el sistema
operativo, la BIOS simplemente se queda bloqueado.
En el caso de la disquetera, la BIOS espera que tenga introducido un disquete cuyo primer sector
lgico (el sector lgico 0 que est en el sector fsico 1, cabeza 0 y pista 0) tenga en algunos de
sus 512 bytes ciertos valores. Si tras leer el sector lgico 0 la BIOS encuentra esos valores correctamente establecidos, deduce que ese sector contiene adems el cdigo del sistema operativo
que se ocupa de realizar su carga desde el disquete, por lo que le cede el control, dando por terminado su cometido.
El cdigo contenido en el sector de arranque tiene en cuenta hasta cierto punto la estructura del
sistema de ficheros existente en el disquete (sector de arranque, sectores reservados, la(s)
FAT(s), el directorio raz y los sectores de datos) para realizar la carga del sistema operativo en
la memoria RAM, ubicndolo en una zona apropiada para su ejecucin. Finalmente el cdigo del
sector de arranque cede el control al sistema operativo ya cargado en la memoria.
En la programacin del sector de arranque se manejan ya a un nivel elemental todos los conceptos bsicos de un sistema operativo: la activacin de diferentes programas, la gestin de la memoria, la reubicacin de cdigo, el manejo de los dispositivos de E/S, las llamadas al sistema y el
Pg. 127
Apndices
sistema de ficheros. Por ese motivo este tipo de ejercicios de programacin juega un papel muy
importante en un primer curso de sistemas operativos.
3) TRABAJO A REALIZAR
La tarea a desarrollar en este tutorial es justamente la programacin en C (con recursos de bajo
nivel) del sector de arranque de un disquete de 3 pulgadas de doble cara y doble densidad
(1,44 MBytes) cumpliendo las siguientes especificaciones:
1) Por supuesto, la BIOS debe reconocer el disquete donde se escriba ese sector de arranque
como un disquete correcto con un sector de arranque correcto. Aqu juegan un papel fundamental los bytes 510 y 511 del sector de arranque (es decir la signatura del sector de
arranque 55 AA). Adems MS-DOS debe reconocer tambin el disquete como un disquete correcto con un sistema de ficheros FAT12 correcto, de modo que por ejemplo el comando dir a: se ejecute sin problemas.
2) Una vez que la BIOS ceda el control al cdigo del sector de arranque, ese cdigo deber
realizar la carga de lo que haya en los primeros 320 sectores lgicos consecutivos siguientes al espacio ocupado por el directorio raz del disquete. Tras la lectura de cada uno
de esos sectores debe escribirse en la pantalla un carcter '.', con el fin de poder seguir
el avance de la carga de los 320 sectores.
3) La carga de los 320 sectores mencionados debe tener como destino final las 160 K direcciones de memoria consecutivas que estn a partir del tercer KByte de la memoria principal (direccin de comienzo 000600H).
4) La carga de los 320 sectores debe respetar las siguientes zonas de memoria: la tabla de
vectores de interrupcin (de 000000H a 0003FFH), el rea de trabajo de la BIOS (de
000400H a 0004FFH) y las direcciones utilizadas por los controladores y la ROM (de
0A0000H a 0FFFFFH). Dicho de otro modo, durante la carga no debe escribirse en ninguna de las zonas de memoria mencionadas.
Es natural que la especificacin anterior plantee al alumno muchas dudas sobre multitud de pequeos detalles que significan una laboriosa tarea de documentacin. Por ese motivo vamos a
hacer a partir de aqu una exposicin pormenorizada de las tareas que es necesario que el alumno
lleve a cabo. Dichas tareas son las siguientes:
Pg. 128
Apndices
Como puede verse el programa "prog.exe" muestra en pantalla un saludo con men, donde las
opciones son: terminar el programa (volviendo a MS-DOS) o reiniciar el ordenador. En la esquina inferior derecha de la pantalla se muestra adems un rectngulo de color azul claro cuyo interior se rellena con el ltimo carcter que se haya tecleado.
El programa anterior no es, ni pretende ser, un sistema operativo, pero nos permitir comprobar
el buen funcionamiento del sector de arranque contenido en "mi_boot.bin", a la vez que permitir que el alumno se d cuenta de cmo es la programacin en el nivel ms bsico de la implementacin de un sistema operativo.
El primer problema que se nos presenta es que el fichero ejecutable "prog.exe" no comienza
directamente con la primera instruccin a ejecutar, sino que comienza con una cabecera de 512
bytes cuyo formato puede consultarse con el programa techelp y de la que podemos obtener
fcilmente su informacin si tecleamos:
C:\practi.cas\prac-2\exe2bin /h prog.exe
Pg. 129
Apndices
Esta cabecera del fichero es algo necesario para MS-DOS, pero para nuestro sistema, no debe
estar en los programas generados, por tanto debe ser suprimida. Existen muchos mtodos para
hacer esto. En nuestro caso utilizaremos el programa conversor de formato exe2bin, incluido
en el directorio c:\utils. La supresin de la cabecera se realiza de un modo transparente, ya que
en el comando compila.bat que se encuentra en el directorio de la prctica, se encuentra ya
incluida la lnea que invoca a dicho programa para llevar a cabo tal propsito. Si dentro del directorio c:\practi.cas\prac-2, ejecutamos el comando: compila prog, se generarn los ficheros
prog.exe y prog.bin. Este ltimo es la versin compilada de prog.c, sin la cabecera. Se ver que
el tamao es justo 512 bytes menos que la versin exe.
Una vez obtenido el programa "prog.bin" por el mtodo que sea, tendramos ya todos los elementos necesarios para construir nuestro disquete de arranque. Lo primero es dar formato a una
imagen de disquete (Se recuerda que la imagen debe estar montada como unidad fsica [0 1]
ya que DosBox usa tambin unidades lgicas que no admiten el comando tradicional del DOS
format. Para dicho propsito puede montarse por ejemplo la imagen de disquete del direcotorio de la prctica fdvacio.img). Por rapidez se recomienda utilizar el comando formatea.bat que se encuentra en el directorio C:\practi.cas. Este comando es muy simple, nicamente escribe el fichero formato.bin en los primeros sectores de un disquete, utilizando el programa escr_s ya conocido de la prctica anterior. El fichero escrito, formato.bin, es una imagen
binaria de un sistema de ficheros Fat12 de un disquete vaco. Con esto conseguimos tener un
disquete libre de informacin, aunque sobre el resto de sectores no se escribe nada, y no valdra
para borrar toda la informacin de un disquete, pero eso es algo que no nos preocupa.
El siguiente paso sera copiar el fichero "mi_boot.bin" (que se encuentra en el directorio
c:\imgs4so) en el sector de arranque del disquete (sector 0), utilizando el programa escr_s ya
visto. Por ltimo, hay que copiar el fichero prog.bin en el disquete. Es crucial que este fichero
sea el primer fichero que se copia en el disquete, ya que as se garantiza que ocupe los primeros
sectores del disco (a partir del 33, que es el primer sector de datos tras el directorio raz) y el disquete pueda arrancar.
Slo nos falta comprobar que el disquete que hemos preparado arranca (hace boot) el programa
"prog.bin" escrito en l. Con este fin, seleccionamos con D-fend alguno de los perfiles ya
existentes (tambin se podra crea uno nuevo para tal efecto), y vamos haciendo click sobre el
botn editar, opcin Al arrancar (abajo izquierda), arrancar imagen de disquete y seleccionamos nuestra imagen: por ejemplo fdvacia.img. El resultado es el que se muestra en la
siguiente imagen de pantalla:
Pg. 130
Apndices
El alumno debe comprobar que el programa lee y visualiza correctamente los caracteres que se
introducen por el teclado, y que pulsando la tecla 'R' (mayscula o minscula) se reinicia el
ordenador. Igualmente debe comprobar que pulsando la tecla 'T' el sistema no funciona. La
razn es porque la operacin de terminar el programa no tiene ningn sentido en este momento
al ser "prog.bin" el nico programa existente en el sistema. Cuando ejecutbamos "prog.exe"
desde MS-DOS tena sentido terminar el programa y volver al intrprete de comandos (haciendo
una llamada al sistema operativo MS-DOS), pero ahora el nico sistema operativo es el propio
"prog.bin" quien tiene que resolver todos los problemas por s mismo.
Esa situacin de desamparo se da exactamente igual en el cdigo de arranque contenido en
"mi_boot.bin". Ese cdigo tiene que leer sectores del disquete que lo contiene, tiene que cargar
en memoria dichos sectores, tiene que escribir en la pantalla y tiene que ceder el control al programa una vez cargado. Para hacer todo eso slo dispone de su propio cdigo mquina y de la
BIOS presente en la ROM.
5) ACTIVIDAD 2: Entender la generacin de cdigo necesaria
Es muy importante que el alumno se d cuenta de que tanto el cdigo del sector de arranque
"mi_boot.bin" como el fichero "prog.exe" no son programas de usuario normales escritos en
C. En los programas normales el compilador genera cdigo que se apoya sobre el sistema operativo en aspectos como los siguientes:
-
Pg. 131
Apndices
Programas aparentemente tan sencillos como el tpico "hola.c" ya visto incluyen en su cdigo
multitud de llamadas al sistema operativo. Como prueba podemos ejecutar el siguiente comando:
C:\PRACTI.CAS\PRAC-1>tdump h hola.exe | grep "CD 21"
que muestra las lneas del volcado hexadecimal de "hola.exe" que contienen los bytes "CD
21", los cuales corresponden al cdigo mquina de la instruccin INT 21 (interrupcin software
o trap 21H) utilizada para invocar las llamadas al sistema operativo MS-DOS:
Como vemos "so1_hola.exe" contiene 20 instrucciones INT 21, y por tanto invoca 20 llamadas al sistema operativo! De ah se deduce que "hola.exe" nunca podra funcionar tras ser cargado por el sector de arranque del disquete. Por el contrario "mi_boot.bin" no contiene ninguna
de esas llamadas al sistema, y lo mismo sucede con "prog.bin" (exceptuando la llamada al sistema para terminar el programa que se ha dejado a propsito).
Si queremos que el compilador tcc de Turbo C no genere por defecto llamadas al sistema tendremos que establecer una serie de opciones de compilacin especiales, como las que utilizaba el
comando compila del que debemos echar mano para compilar "prog.c":
C:\PRACTI.CAS\PRAC-2>compila prog
Pg. 132
Apndices
Otra incomodidad es que en algunos casos tendremos que ajustar nosotros mismos los segmentos
de cdigo, de datos y de pila del programa, cosa que normalmente realiza de una forma automtica el compilador haciendo uso del sistema operativo.
6) ACTIVIDAD 3: Esqueleto para el sector de arranque
La estructura del sector de arranque, que se resume en la siguiente plantilla tomada del programa
WinHex :
instruccion JMP */
instruccion NOP (90H) de relleno */
OEM, 8 caracteres */
Bytes por sector */
Sectores por cluster */
Sectores reservados */
numero de FATs */
Apndices
asm dw
asm dw
asm db
asm dw
asm dw
asm dw
asm dd
asm dd
asm db
asm db
asm db
asm dw
asm dw
asm db
asm db
inicio:
main()
}
224
2880
0xF0
9
18
2
0
0
0x00
0
0x29
nSectsSO
0x0000
'ETIQUETA
'FAT12
'
'
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Pg. 134
Apndices
permite introducir una secuencia de palabras (16 bits) separadas por comas. Finalmente la directiva dd hace lo mismo con dobles palabras (32 bits).
A continuacin aparece la etiqueta inicio: que es a donde se dirige el salto del principio del
programa, llamndose inmediatamente a la funcin main donde en principio debera encontrarse
el cdigo que se ocupa de la carga del sistema operativo y de cederle el control. Con ese fin la
funcin main puede hacer uso de funciones auxiliares que se declararan antes de ella y despus
de la funcin start. El formato del sector de arranque impone como requisito que termine con la
signatura del sector de arranque (bytes 55 AA) que debe ocupar exactamente los dos ltimos bytes
del sector. En otro caso la BIOS rechazara ese sector de arranque. Por ese motivo hemos introducido las directivas _TEXT y org 01FEH antes de introducir los dos bytes de la signatura. Habr
que tener mucho cuidado de que el cdigo del programa principal no sea tan grande que esa direccin est ya ocupada por las instrucciones anteriores del programa principal.
7) ACTIVIDAD 4: Llamadas a la BIOS de manejo de teclado y pantalla.
Nuestro sector de arranque va a necesitar escribir en la pantalla algunos caracteres, por lo que
vamos a tratar aqu el modo de hacerlo sin utilizar llamadas al sistema operativo MS-DOS y de
manera que invirtamos pocos de los preciosos 512 bytes del sector de arranque en el cdigo correspondiente. Una solucin es recurrir a la BIOS. El programa techelp documenta los servicios
que nos ofrece. La BIOS ofrece sus servicios mediante interrupciones software (traps). En el
caso de los servicios de manejo de la pantalla el nmero del vector de interrupcin que se utiliza
es el 10H (16 en decimal).
Para cada tipo de servicio hemos de indicar como parmetro en el registro AH (byte de mayor
peso del registro acumulador AX del microprocesador 8086) un nmero que identifica la operacin concreta que queremos llevar a cabo. En nuestro caso deseamos ser capaces de escribir un
carcter por la pantalla como si se tratara de un terminal, por lo que hemos de indicar en AH el
cdigo de operacin 0E hex. Veamos los detalles sobre esa operacin:
Pg. 135
Apndices
Los detalles son mnimos: para escribir un carcter en la pantalla utilizando el servicio 0eH de la
interrupcin 10H de la BIOS hemos de poner el carcter en el registro AL (byte de menor peso
de AX) y el byte de atributo de pantalla en BL (byte de menor peso del registro BX). El atributo
determina el color del carcter y el color de fondo del espacio sobre el que se escribe. El atributo
normal (carcter blanco sobre fondo negro) corresponde al byte 07H, que es el que vamos a utilizar.
Con lo anterior podemos implementar la siguiente funcin auxiliar que nos permite visualizar un
carcter por pantalla:
void printCar ( char car ) {
asm mov al,car
/* car
asm mov bl,07H
/* 07H
asm mov ah,0eH
/* 0eH
asm int 10H
/* 10H
}
->
->
->
->
caracter a escribir */
atributo normal */
escribir caracter */
servicios BIOS de manejo de la pantalla */
En cuanto a la lectura de caracteres desde el teclado podemos hacer exactamente lo mismo pero ahora utilizando la interrupcin 16H (22 decimal) de la BIOS. En la siguiente pantalla
del techelp se muestran los servicios ofrecidos a travs de esa interrupcin. As, haciendo uso
de la BIOS podemos programar del siguiente modo una funcin auxiliar que espera a que se pulse una tecla y retorna el carcter correspondiente:
char leerTecla ( ) {
char car ;
asm mov ah,00H
asm int 16H
asm mov car,al
return(car) ;
}
Vamos a ver otra posible funcin auxiliar que nos permita reiniciar el ordenador desde el programa. En un PC, cuando se enciende el ordenador o se pulsa el botn de reset se cede automticamente el control a la instruccin que se encuentra en la direccin: 0FFFF0H (1 Mega menos
16 bytes), que expresada en forma de par segmento:desplazamiento del 8086 corresponde (por
ejemplo) a la direccin FFFF:0000. Esa direccin es el punto de entrada de la BIOS que est en
la ROM, el cual inicia el ordenador. Por tanto en cualquier momento podemos reiniciar el ordenador cediendo el control a esa direccin. Podemos programar del siguiente modo la funcin de
reinicio poniendo la direccin a la que queremos ir en la pila y ejecutando una instruccin de
retorno:
void reboot ( ) {
asm push 0FFFFH
asm push 0000H
asm retf
}
Pg. 136
Apndices
asm mov al,00h
asm int 21h
*/
con el fin de poder ejecutar primeramente el programa desde MS-DOS terminando el programa
normalmente, a la vez que ilustramos cmo se hace una llamada al sistema operativo MS-DOS
concreta (vase su documentacin en el techelp). Esta llamada al sistema deber suprimirse del
cdigo del sector de arranque definitivo, dejando en su lugar la funcin reboot. A continuacin
mostramos el programa "boot_1.c" completo:
#define
#define
#define
#define
nSectsSO 320
CR
13
LF
10
ESC
27
/*
/*
/*
/*
void main () ;
void start () {
asm jmp short inicio
asm nop
asm db 'SO v2.00'
asm dw 512
asm db 1
asm dw 1
asm db 2
asm dw 224
asm dw 2880
asm db 0xF0
asm dw 9
asm dw 18
asm dw 2
asm dd 0
asm dd 0
asm db 0x00
asm db 0
asm db 0x29
asm dw nSectsSO
asm dw 0x0000
asm db 'ETIQUETA
'
asm db 'FAT12
'
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
instruccion JMP */
instruccion NOP (90H) de relleno */
OEM, 8 caracteres */
Bytes por sector */
Sectores por cluster */
Sectores reservados */
numero de FATs */
Entradas del directorio raiz */
numero de sectores en total (16 bits) */
descriptor de medio */
SectPorFAT */
Sectores por pista */
Cabezas */
Sectores ocultos */
numero de sectores en total 32 (bits) */
indica la unidad A: */
Byte que no se usa */
Extension de la signatura */
Numero de sectores a cargar */
Disponible para otros usos */
Etiqueta de volumen, 11 caracteres */
Tipo de sistema de ficheros */
->
->
->
->
caracter a escribir */
atributo normal */
escribir caracter */
servicios BIOS de manejo de la pantalla */
inicio:
main() ;
}
void printCar ( char car ) {
asm mov al,car
/* car
asm mov bl,07H
/* 07H
asm mov ah,0eH
/* 0eH
asm int 10H
/* 10H
}
char leerTecla ( ) {
char car ;
asm mov ah,00H
asm int 16H
asm mov car,al
return(car) ;
}
Pg. 137
Apndices
void prompt ( ) {
printCar('S') ;
printCar('O') ;
printCar('1') ;
printCar('>') ;
printCar(' ') ;
}
void reboot ( ) {
asm push 0FFFFH
asm push 0000H
asm retf
}
void finProgDOS () {
asm mov ah,4ch
asm mov al,00h
asm int 21h
}
void main ( ) {
char car ;
prompt() ;
while ((car = leerTecla()) != ESC) {
printCar(car) ;
if (car == CR) {
printCar(LF) ;
prompt() ;
}
}
finProgDOS();
reboot() ;
}
/* Si no se incluyen las directivas _TEXT la directiva org actua sobre
el segmento de datos y no saldria bien */
asm _TEXT segment byte public 'CODE'
asm
org 01FEh
asm
db 55h, 0AAh
asm _TEXT ends
Este comando genera los ficheros boot_1.exe y boot_1.bin. Probamos que boot_1.exe se ejecute correctamente y tras comprobar que todo va bien quitamos del fuente la funcin finProgDOS y su llamada y recompilamos, ya que ahora vamos a utilizar el fichero generado boot_1.c, que no tiene la cabecera de 512 bytes para probarlo como sector de arranque. El tamao
del fichero debe ser exactamente 512 bytes y si editamos o vemos s interior con cualquier utilidad (cn, tdump..) veremos tambin que los dos ltimos bytes son la signatura 55AA. Por ejemplo:
C:\PRACTI.CAS\PRAC-2> tdump h boot_1.bin | more
Tras la comprobacin slo nos queda escribir el sector de arranque en un disquete previamente
formateado y reiniciar la mquina virtual DosBOX como se ha hecho en otras ocasiones.
C:\PRACTI.CAS\PRAC-2>escr_s boot_1.bin 0 0
Pg. 138
Apndices
Se ver que el programa despliega enseguida su prompt "SO> " y que realiza perfectamente el
eco del teclado, volviendo a mostrar el prompt tras cada pulsacin de la tecla de retorno de carro.
La nica salida posible del programa es presionando la tecla de escape ESC, lo que da lugar al
reinicio del ordenador.
8) ACTIVIDAD 5: Llamadas a la BIOS de acceso al disco
Vamos a tratar ahora la forma en la que debe realizarse la lectura de sectores del disquete desde
el cdigo del sector de arranque. En la prctica 1 utilizamos la funcin biosdisk, ofrecida por la
biblioteca bios.h, para leer sectores. Ahora vamos a implementar nosotros mismos el acceso al
disquete utilizando directamente llamadas a la BIOS. Comenzamos echando un vistazo a la documentacin que nos proporciona el techelp:
Apndices
asm jc resetError
return(0) ;
resetError:
return(1) ;
}
Vamos a explicar un poco cada uno de los parmetros de la llamada a la BIOS para leer un sector. Est claro que en el registro DL hemos de poner el nmero de la unidad. En los registros
DH, CH y CL hay que codificar la informacin correspondiente al nmero de cabeza, pista y
sector fsico del sector lgico que se desea leer. El nmero de cabeza (0 o 1) se pone tal cual en
DH. El nmero de sector fsico (de 1 a 18) debe ponerse en CL. Finalmente el nmero de pista,
que sera ms correcto denominar nmero de cilindro (de 0 a 79) debe ponerse en CH. Se apreciar que la explicacin anterior parece diferir de lo que seala el techelp. El motivo es que el
techelp describe el caso general en el que el nmero de cilindro de un disco duro puede requerir
hasta 10 bits, debiendo aprovecharse los dos bits superiores de CL para contener los dos bits que
no caben en CH. En el caso de un disquete, con 80 cilindros, no tenemos ese problema, de ah
que todo sea mucho ms sencillo.
Siguiendo con los parmetros de la llamada a la BIOS para leer un sector, vemos que en AL hay
que indicar el nmero de sectores consecutivos que queremos leer (sin exceder un cilindro), por
lo que pondremos en AL un 1. En cuanto a la direccin de memoria donde debe quedar el sector
ledo, vemos que hay que indicarla en los registros ES (registro de segmento extra) y BX. En ES
debe ponerse el nmero de segmento de la direccin, y en BX el desplazamiento. Con esto
hemos terminado de explicar la informacin que es estrictamente necesario indicar a la BIOS en
esta llamada. Como suceda con la operacin de reset, si la BIOS detecta algn error en la lectura
del sector, activa el flag C de acarreo para indicarnos ese hecho. A continuacin mostramos el
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 140
Apndices
esqueleto de la funcin que realiza la lectura de un sector lgico del disquete, dejndose como
tarea al alumno el completar los detalles:
/* leerSector: sect es el numero de sector logico
/*
unidad es el numero de la unidad (0 = A:, 1 = B:)
/*
dir es la direccion del bufer de memoria
*/
*/
*/
sector = ... ;
cabeza = ... ;
pista = ... ;
Con el fin de comprobar que la funcin anterior se comporta correctamente el alumno puede ir al
programa "l_s.c" que implement en la prctica 1, y reemplazar la llamada a la funcin biosdisk:
biosdisk(cmd_read_sector, unidad, c, p, s, 1, &bufer) ;
A continuacin debe recompilarse el programa con el tcc (ya que "l_s.c" utiliza bibliotecas
como stdio.h dependientes de MS-DOS) y comprobarse que el programa "l_s.c" sigue funcionando bien a pesar del cambio realizado.
9) ACTIVIDAD 6: Manejo de direcciones de memoria
Tras el apartado anterior sabemos ya cmo podemos leer los 320 sectores lgicos correspondientes al sistema operativo. Esos sectores ocuparn 160K en memoria principal, por lo que el bfer
de memoria donde leemos cada uno de esos sectores debe ir desplazndose 512 bytes tras la lectura de cada sector. Los punteros que se manejan en el 8086 normalmente son desplazamientos
de 16 bits relativos al segmento de datos. Con tan solo 16 bits no es posible direccionar los 160K
de memoria donde va a cargarse el sistema operativo, por lo que necesitamos direcciones completas formadas por un nmero de segmento y un desplazamiento, las cuales requieren 32 bits en
Pg. 141
Apndices
total. En Turbo C el tipo de datos correspondiente a esas direcciones son los punteros lejanos
(far) como por ejemplo los siguientes punteros origen y destino que apuntan a un carcter:
char far * pOrigen;
char far * pDestino;
A estos punteros lejanos se les puede asignar punteros normales (near), puede escribirse su valor
con printf y el formato %08lX, y puede operarse con ellos con sumas y restas. Por ejemplo podemos copiar las 1000 posiciones de memoria que comienzan en la direccin segmentada
8765H:4321H (que corresponde en el 8086 a la direccin lineal 8B971H = 87650H+4321H)
llevndolas a partir de la direccin 8900H:1234H mediante el siguiente bucle:
pOrigen = (char far*) 0x87654321L;
pDestino = (char far*) 0x89001234L;
contador = 1000 ;
while (contador-- > 0) *destino++ = *origen++ ;
Aunque en este caso concreto no se da el problema, hay que tener mucho cuidado de que en las
sumas con punteros no se desborden los 16 bits correspondientes al desplazamiento, ya que por
ejemplo si un puntero far vale 0x0000FFFF y le sumamos 1, lo que se obtiene en Turbo C es
0x00000000, en vez del valor esperado en el 8086 que es 0x10000000. El alumno puede comprobarlo con el siguiente programa:
void main ( ) {
char far * ptr;
ptr = (char far *)0x0000FFFFL;
printf(" ptr = %08lX antes de incrementarse\n", ptr++);
printf(" ptr = %08lX despues de incrementarse\n", ptr);
}
Con el fin de solucionar el problema anterior al copiar zonas de memoria extensas, vamos a proporcionar una funcin de incremento de la direccin contenida en un puntero lejano.
void inc (void *p, unsigned i) {
if (i > 0xFFFF - *(unsigned *)p) ((unsigned *)p)[1] += 0x1000;
*(unsigned *)p += i;
}
Una vez que se dispone de la funcin auxiliar 'inc' podemos programar la carga desde el disquete de los 320 (nSectSO) sectores del sistema operativo de la siguiente manera, donde utilizamos
un puntero para indicar en cada momento la direccin de comienzo del bfer:
#define PRI_SEC_DATOS 33
char far * ptr = (char far *) 0x00600000L;
/* 0060:0000 = 00600H */
Pg. 142
Apndices
int i;
...
...
for (i = PRI_SEC_DATOS; i < nSectsSO+PRI_SEC_DATOS; i++ ) {
leerSector(i++, 0, ptr);
printCar('.');
inc(&ptr, 512);
}
7C00H
28800H
0FFFFFH
7C00H
sector de arranque
reubicado
28800H
30000H
0FFFFFH
Adems de reubicar el sector de arranque, necesitaremos que la pila que se utilice tampoco sea
sobrescrita durante la carga del sistema operativo, lo que sera tambin catastrfico (obsrvese
que no utilizamos variables globales). Con ese fin el sector de arranque debe establecer una nueva pila en una zona segura. Vamos a elegir como valor para el segmento de pila SS el mismo que
donde se copia el cdigo del sector de arranque, es decir el 3000H. En cuanto al puntero de pila
SP vamos a darle tambin el mismo valor 3000H, de manera que la cima de la pila estar en la
direccin SS:SP = 3000:3000. El establecimiento de la pila debe hacerse en la funcin start
antes de llamar a la funcin main, ya que la funcin main necesita la pila para asignar memoria a
sus variables locales. El cdigo que se necesita al final de la funcin start es el siguiente:
Pg. 143
Apndices
inicio:
asm
asm
asm
asm
asm
cli
mov ax,3000h
mov ss,ax
mov sp,ax
sti
/*
/*
/*
/*
/*
main();
La razn por la que inhibimos temporalmente las interrupciones (intruccin cli) es que la pila
interviene decisivamente en la gestin de las interrupciones, que no deben aceptarse mientras
modificamos la ubicacin de la pila.
El punto ms crtico del cdigo del sector de arranque es el momento en el que el cdigo del sector de arranque (cargado a partir de 07C00H) debe ceder el control a su copia (a partir de la direccin 30000H) por lo que vamos a indicar esos pasos en detalle:
void main ( ) {
char far *pOrigen = (char far*)0x07C00000;
char far *pDestino= (char far*)0x30000000;
int i;
. . .
/* 0000:7C00 = 07C00H */
/* 3000:0000 = 30000H */
*/
*/
*/
*/
...
...
}
/* Si no se incluyen las directivas _TEXT la directiva org actua sobre
el segmento de datos y no saldria bien */
asm _TEXT segment byte public 'CODE'
asm
org 01FEh
asm
db 55h, 0AAh
asm _TEXT ends
Vemos en el programa principal que lo primero es reubicar el sector de arranque. Tras la reubicacin se cede el control a la copia del sector de arranque justo despus de la instruccin retf.
El signo $ hace referencia en ensamblador al contador de programa, por tanto si sumamos 4 al
CP obtenemos un direccin 4 bytes posterior, que es justo la direccin de la siguiente instruccin
a retf. Esto puede comprobarse examinando el cdigo maquina con el debugger del DOS, o bien
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 144
Apndices
[tcc v boot_2.c]
Apndices
Pulsamos otra vez F7 para saltar al programa principal en la direccin CS:0104. Ya desde esa
posicin lo mejor es que nos limitemos a buscar la primera instruccin retf del programa principal utilizando la tecla de cursor abajo o Avance de Pgina.
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 146
Apndices
Una vez localizada la primera instruccin retf vemos que la instruccin siguiente est en la direccin CS:0155, que efectivamente coincide con el segundo de los valores que se apilan (que en
el programa expresamos mediante OFFSET($ + 4))
Obsrvese que algo ms adelante todo son ceros 0000, hasta llegar a la signatura (55 AA) del
sector de arranque:
Pg. 147
Apndices
Tras las explicaciones anteriores el alumno ya puede desarrollar el sector de arranque cuya especificacin se dio en el apartado 3. Se advierte de que hay que limitar al mximo el tamao del
cdigo que se genere, ya que el sector de arranque no puede ocupar ms que los 512 bytes del
primer sector. En particular debe suprimirse cualquier funcin auxiliar que no se utilice desde el
programa principal, aunque de eso ya se encarga automticamente el compilador. Los requisitos
para poder evaluarse de este ejercicio son:
-
haber desarrollado completamente el fichero "boot_2.bin" cumpliendo todas las especificaciones del apartado 3
tener suficientemente claros todos los aspectos que recoge esta prctica
Por otra parte hay que decir que sera conveniente (si queda espacio libre en el sector de arranque) realizar alguna deteccin de errores a la hora de hacer el reset de la unidad A:, o a la hora de
leer los sectores que contienen el sistema operativo. En caso de detectarse un error sera deseable
escribir algunos caracteres indicndolo y esperar a la pulsacin de una tecla para reiniciar el ordenador. En la entrega del trabajo para su evaluacin podrn plantearse (en caso de duda sobre
los conocimientos del alumno) cuestiones relativas a esas mejoras.
Pg. 148
Apndices
NDICE
1
Pg. 149
Apndices
1. OBJETIVOS
Los objetivos de esta prctica son:
Que el alumno conozca en detalle cmo se implementa el modelo de los procesos sin entrar
en algoritmos de planificacin complejos.
Que el alumno entienda cmo funciona sobre una mquina concreta el mecanismo de las
llamadas al sistema.
Que el alumno sea capaz de implementar en un sistema operativo una herramienta de sincronizacin sencilla (semforos con las operaciones: iniSemaforo, bajaSemaforo y subeSemaforo).
Que el alumno escriba algn programa de usuario que haga uso de las llamadas al sistema
disponibles y conozca todos los pasos necesarios para su ejecucin.
2. INTRODUCCIN
En la primera prctica el alumno aprendi a utilizar las llamadas al sistema de gestin de ficheros desde un programa escrito en C tomando conciencia de las operaciones bsicas de implementacin del sistema de ficheros, que son la lectura y escritura de sectores lgicos del disco.
En la segunda prctica el alumno se enfrent con la ingrata situacin de tener que escribir un
programa capaz de ejecutarse sin recurrir a un sistema operativo, y desarroll un sector de arranque capaz de cargar desde un disquete o CDROM cualquier sistema operativo sencillo.
Una vez superados estos desafos el alumno est preparado para dar el salto y meterse de lleno en
lo que realmente es un sistema operativo. Dada la complejidad del tema no podemos pretender
comenzar abordando un sistema tan acabado como MINIX. Por el contrario hemos de conformarnos con un sistema ms humilde y transparente que nos permita alcanzar los objetivos expuestos en el apartado anterior. El sistema SO que va a utilizarse es lo suficientemente sencillo
como para poder trabajar en l con la mquina virtual DosBox sin necesidad de ninguna
herramienta adicional, aparte del entorno de Turbo C utilizado desde la primera prctica.
3. TRABAJO A REALIZAR
Para empezar hemos de entender el funcionamiento del sistema operativo SO, tanto a nivel de
usuario, introduciendo comandos a travs de la consola o haciendo llamadas al sistema desde un
programa, como a nivel de la estructura de dicho programa SO en correspondencia con la de un
sistema monoltico. En relacin con este segundo nivel se mostrar la implementacin concreta
de los procesos (descriptores, tabla de procesos, cola de preparados, planificador y despachador)
y de la gestin de memoria (particiones fijas).
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 150
Apndices
A continuacin se emprender el camino de las ampliaciones del sistema SO original. Comenzaremos ilustrando la gestin de las interrupciones mediante la escritura de una rutina de tratamiento para la interrupcin del teclado generada por la combinacin de teclas Ctrl-C. Seguidamente trataremos brevemente las excepciones, escribiendo las rutinas de tratamiento de las excepciones de divisin por cero y de desbordamiento aritmtico (instruccin INTO). A partir de ah
vamos a concentrarnos en las interrupciones software (instruccin INT) que son el mecanismo
mediante el cual los programas de usuario invocan las llamadas al sistema.
La primera llamada al sistema que se implementar ser sleep, que permite a un proceso de
usuario bloquearse durante un cierto intervalo de tiempo. En su implementacin el alumno deber alterar la declaracin de los descriptores de proceso, modificar la rutina de tratamiento de la
interrupcin de reloj y escribir el manejador de la nueva llamada al sistema.
Lo siguiente ser implementar el grupo de llamadas al sistema: iniSemaforo, bajaSemaforo y
subeSemaforo, que corresponden a las operaciones del tipo abstracto de datos de los semforos,
dotando as al sistema SO de sus primeras primitivas de sincronizacin. Ser necesario definir
en el ncleo del sistema las estructuras de datos que representen a los semforos. Los manejadores de las llamadas al sistema bajaSemaforo y subeSemaforo invocarn al planificador y dispatcher del sistema con el fin de bloquear y desbloquear correctamente a los procesos.
4. RELACIN DE ACTIVIDADES A DESARROLLAR
Actividad 1: El sistema SO a nivel de usuario
Actividad 2: El sistema SO a nivel de programacin de aplicaciones
Actividad 3: Estructura e implementacin del sistema SO
Actividad 4: Implementacin de la interrupcin Ctrl-C.
Actividad 5: Compilacin del sistema SO
Actividad 6: Implementacin de excepciones
Actividad 7: Implementacin de la llamada al sistema sleep
Actividad 8: Implementacin de los semforos
Pg. 151
Apndices
Como puede observarse, la consola muestra el prompt C:\> y espera a que se introduzca un comando. Por ejemplo, podemos introducir el comando siguiente:
C:\>cls <
Pg. 152
Apndices
La denominacin de comandos internos alude a que son comandos que el intrprete ejecuta cediendo el control a una funcin que forma parte del cdigo del intrprete. Un comando externo
sera uno que el intrprete ejecuta leyendo un fichero ejecutable, cargndolo en la memoria y
cedindole el control. La desventaja de los comandos externos es que tienen un peor tiempo de
respuesta, al tener que leerse su cdigo desde el disco. Su gran ventaja es que el cdigo correspondiente a esos comandos puede actualizarse inmediatamente sin ms que sustituir el fichero
correspondiente por la ltima versin del comando, evitando as tener que recompilar el intrprete. En Linux podemos encontrar los comandos externos ms utilizados en los directorios /bin,
/sbin y /usr/bin.
SO es un sistema con multiprogramacin, lo que queda patente ejecutando el comando ps, el
cual muestra el estado de los procesos que estn residiendo en la memoria. En este momento slo
hay dos procesos, que se corresponden con el proceso servidor (servicios relacionados con ficheros) y al intrprete de comandos de la consola de SO, los cuales forman parte del propio sistema SO cargado en la direccin 0x00600, o segmento 0x60.
El comando mem muestra el espacio disponible de memoria para la carga de programas en forma de una lista encadenada llamada de huecos. Inicialmente toda la memoria a partir de donde
finaliza SO hasta el segmento direccin A000, estar disponible para programas y se reportar
en forma de un solo hueco, por ejemplo: 2A5D (comienzo segmento), 85A3 (tamao del hueco
en paragraphs - unidades de 16 bytes) y por ltimo la direccin del siguiente hueco: NIL.
El comando dump (volcado de memoria) permite visualizar partes de la memoria del mismo
modo que el programa ver_fich.exe visto anteriormente mostraba partes de un fichero binario.
Si tecleamos por ejemplo el comando dump siguiente, obtendremos una pan talla similar a la
siguiente:
La direccin 09F0:0 es el principio del segmento de datos (DS) del programa SO.
Pg. 153
Apndices
En este caso vemos que hay varios ficheros en el disquete, uno de ellos: "so60.bin", se corresponde con el sistema SO cargado durante el arranque, los dems ficheros son programas de
usuario que hacen uso de diferentes llamadas a sistema, algunas de las cuales habr que realizar
en estas prcticas. Si ejecutamos el programa hola, obtenemos lo siguiente:
Pg. 154
Apndices
Las combinaciones de teclas SHIFT IZDA-TAB y/o SHIFT DCHA-TAB sirven para cambiar el
plano de la ventana sin cambiar el foco del teclado. Si arrancamos mas procesos podemos probar
estas teclas y comprobar su funcionamiento. El programa hola finaliza con la pulsacin de una
tecla, pudiendo comprobarse con ps cmo desaparece el proceso y con mem si se ha generado
un nuevo hueco en el lugar que ocupaba dicho proceso o se ha fusionado con alguno ya existente. Por ltimo, respecto al uso de teclas especiales, hay que decir que las teclas de cursor "",
"", "" y "", sirven para mover la ventana que aparece en primer plano y si se usan conjuntamente con la tecla SHIFT (Mayus) redimensionan la ventana.
Vamos a mostrar ahora la ejecucin de varios procesos concurrentemente. Con este fin introducimos desde la consola cuatro veces el comando externo errante (el carcter ! tiene la funcin especial de repetir el ltimo comando). Recurdese el uso de TAB para retomar el foco y
las teclas de flecha de cursor para mover las ventanas:
Pg. 155
Apndices
Pueden apreciarse los espacios de memoria que han ido ocupando los procesos errante, la
memoria que ocupa cada uno de ellos y el hueco de memoria final resultante.
La funcin del programa errante es muy simple, se limita a solicitar un nmero entre 1 y 9 y a
continuacin comienza a desplazarse por la pantalla a una determinada velocidad que viene indicada por el nmero introducido, cuanto ms alto ms rpido se mueve. Este programa hace uso
reiterado de la llamada a sistema moverWindow, y tras realizar una serie de vueltas, acaba parado en la esquina inferior izquierda, muestra un mensaje y finaliza.
Con los programas errante cargados en memoria y listos para empezar su funcin, tecleamos
en cada uno de ellos un nmero entre 1 y 9 y seguidamente vamos a la consola y ejecutamos el
comando ps, obteniendo algo similar a esta pantalla:
Pg. 156
Apndices
Es interesante observar el estado de los procesos mientras se ejecutan haciendo uso del comando
ps desde la consola. En la pantalla anterior se aprecia que se estaba ejecutando la consola (procesando el comando ps) y que el resto de procesos estaban preparados para ejecutarse, para
proseguir con la ejecucin de su movimiento. Podemos deducir el estado de la cola de preparados a partir del valor de los campos sig de los descriptores de los procesos. Vemos que el siguiente del proceso np = 5 es NIL, lo que indica que se trata del ltimo proceso preparado. El
siguiente del proceso 2 es 5, lo que indica que el proceso 2 es el penltimo de la cola. El siguiente del proceso 4 es 2, por lo que el proceso 4 es el segundo proceso de la cola. Finalmente el proceso 3 tiene como siguiente al proceso 4, por lo que el proceso 3 es el primer proceso de la cola
de preparados.
Cola de preparados
CPU
El comando kill permite destruir un proceso identificado por su pid. Por ejemplo desde la
consola podemos matar cualquier proceso errante antes de que finalice ste, ejecutando por
ejemplo: c:\>kill 3 <
Pg. 157
Apndices
Pg. 158
Apndices
Pg. 159
Apndices
Figura 1-17. Los 11 pasos para hacer la llamada al sistema read(fd, &buffer, nbytes). del procedimiento de librera matarProceso,
Si vemos ahora por ejemplo la implementacin
return _AL;
Pg. 160
Apndices
Otro aspecto a sealar respecto a las llamadas al sistema y procesos de usuario es el fichero
inic_usr.c. Este fichero contiene el cdigo de inicializacin necesario para generar los programas de usuario, as como el interfaz de llamadas al sistema bsico, el cual se haya incluido
dentro de l mediante la directiva #include ..\ll_s_so\bsic-ifz.c.
Su contenido es el siguiente:
/* -------------------------------------------------------------- */
/*
Funciones de interfaz de llamadas al sistema bsicas
*/
/* -------------------------------------------------------------- */
asm DGROUP GROUP _TEXT, _DATA
/* Esta directiva es vital para que el compilador genere codigo en
el que las variables, que usan el segmento DS, tomen un offset
adecuado, es decir, para que DS y CS sean iguales (mismo segmento).
Si se omite, se asume que DS va a tomar un valor distinto a CS,
concretamente CS + tamao codigo en paragraphs, o sea, modelo small.
Todo esto es debido a que compilamos desde linea de comando no
desde el entorno integrado y sin el codigo inicial C0.obj, el cual
se encarga entre otras cosas de ello. Los offset de las variables
empienzan donde acaban los de las funciones. Si se usa TCC 3.0 hay
que usar la opcion -B para que use TASM en vez de el de inline BASM */
#include "..\tipos.h"
void main (void);
void finProceso (void);
/* declaraciones forward */
/* ----------------------------- start() --------------------------------Esta funcion debe estar situada al principio de cualquier otra funcion
de todo proceso de usuario, ya que en ella se invoca a la funcion main()
y despues se efectua la llamada a sistema de finalizacion de proceso */
void start (void) { main(); finProceso(); }
#include "..\ll_s_so\bsic-ifz.c"
Este fichero contiene la funcin start, la cual debe ser la primera funcin en el fichero y dado
que hace uso de las funciones main y finProceso, stas deben de estar declaradas anteriormente como prototipos de C. El propsito de start es servir de cdigo inicial de todo proceso
de usuario. Este cdigo inicial lo que hace es invocar a la funcin main y tras finalizar sta, realizar la llamada al sistema finProceso, de este modo las aplicaciones de usuario tienen garantizado arrancar por la funcin main, y tambin que al finalizar sta, se invoque la llamada al
sistema finProceso. Por supuesto es condicin necesaria que los programas de usuario tengan
al principio de todo, como su primer directiva include, precisamente la de este fichero: #include "..\ll_s_so\inic-usr.c" , ya que es el modo ms sencillo de garantizar que la primera funcin
que el compilador encuentre sea precisamente la funcin start. Por ltimo conviene aclarar un
poco el uso de la directiva asm DGROUP GROUP _TEXT, _DATA que aparece en el fichero inicusr.c. Esta directiva se usa para informar al compilador que debe usar un solo segmento para
cdigo y datos, al igual que se hace en el modelo tiny de turboC. Con ese modelo se simplifica el funcionamiento del arranque de las aplicaciones de usuario por parte de SO, ya que los
segmentos CS, DS y SS tienen as el mismo valor inicial que es la direccin de memoria donde
se carga del proceso.
Una vez presentada la interfaz de programacin con el sistema operativo es conveniente que nos
ejercitemos en su utilizacin poniendo a punto un programa de usuario para el sistema operativo
Pg. 161
Apndices
SO. Como viene siendo habitual, vamos a empezar por el programa de usuario para el sistema
SO, Hola mundo, cuyo fuente se encuentra en el fichero hola.c dentro del directorio:
c:\miso\usrs_prg\hola. Una cosa que debe quedar clara es que los programas ejecutables de
SO, en general, no van a funcionar en otros sistemas operativos, ya que las llamadas al sistema
en los que se basan hacen uso de la instruccin de interrupcin software (trap) INT 60h, y el
vector de interrupcin 60h, no tiene porqu soportar las funciones de servicio de llamadas al
sistema caractersticas de SO. Seguidamente se muestra el cdigo de este programa:
/* ------------------------------------------------------------------- */
/*
hola.c (programa Hola mundo para el sistema SO)
*/
/* ------------------------------------------------------------------- */
#include "..\ll_s_so\inic-usr.c" //Cod.inic.prog.usuario y llam. basicas
void main () {
printStr("\nSO: Hola mundo.\n"); leerTecla() ;
}
Un pequeo detalle en cuanto a la funcin main es que debe declararse como una funcin sin
parmetros que devuelve un valor de tipo void. Despus de escribirse la cadena de caracteres
"SO: Hola mundo." se espera a que se pulse una tecla, con el fin de poder ver tranquilamente
lo que se ha escrito. Finalmente el proceso sale de main y finaliza.
Para compilar y generar el ejecutable del programa "hola.c" la forma ms sencilla es situarse en
el directorio c:\miso\usrs_prg y ejecutar el siguiente comando:
c:\miso\usrs_prg>compila hola\hola <
Este comando se ejecuta en dos partes separadas por una pausa que exige la pulsacin de una
tecla por parte del usuario. La razn de esta pausa es para poder comprobar si hubo errores de
compilacin, ya que sin ella los mensajes de error desapareceran por la parte superior de la pantalla. Tras la pulsacin de dicha tecla habremos obtenido las siguientes pantallas:
Pg. 162
Apndices
El comando compila es un fichero .bat tpico de MSDOS (procesamiento por lotes) que invoca
al compilador de lnea de comando tcc y al montador tlink, con una serie de parmetros ya
establecidos y probados. Con estos dos programas se genera un fichero ejecutable .exe apto en
principio para MSDOS, pero no para el sistema SO, (por tener una cabecera, etc.), por ello el
comando tambin invoca a la utilidad exe2bin, que se encarga de quitar la cabecera y convertir
el fichero a un formato binario .bin. El resultado final debe ser la creacin del fichero
hola.bin que se encontrar en el directorio USRS_PRG\HOLA dentro de MISO. Este fichero
tambin se copia a la unidad de disquete A:
Para probar el programa hay que arrancar SO y ejecutarlo desde l. El sistema SO se puede
arrancar desde el perfil visto en la actividad anterior DBxBoot-SO, sin embargo es ms sencillo
seguir usando el perfil que se est usando y aprovechar la caracterstica de que el sistema SO
tambin puede ser arrancado desde el propio DOS e incluso desde el propio entorno de desarrollo de TurboC. Arrancar SO desde este perfil es muy sencillo, simplemente nos situamos en el
directorio c:\miso y tecleamos so, ya que el sistema se encuentra tambin compilado y en forma
de ejecutable para MSDOS (so.exe). Otra posibilidad consiste en arrancar SO desde el entorno
TurboC. Desde este entorno podemos abrir el proyecto so.prj y utilizar las teclas de F9/ctrl F9
para compilarlo/ejecutarlo. Usar esta posibilidad nos permite incluso utilizar el debugger integrado para depurar el sistema SO. El compilador de TurboC nos permite generar el ejecutable,
sin cdigo de depuracin o con cdigo de depuracin incluido para usarse desde el propio IDE o
desde el programa externo TurboDebugger.
Pg. 163
Apndices
Pg. 164
Apndices
En cuanto a la gestin de la memoria, hay que decir que el mtodo elegido en cuestin ha sido el
de particiones variables. El sistema utiliza una lista para gestionar los espacios de memoria libres (huecos). Al principio la lista slo tiene un elemento, el cual hace referencia a toda la memoria disponible (que el sistema previamente ha determinado). El sistema cuando por ejemplo
crea un proceso, necesita ubicarlo en memoria y para ello utiliza unas funciones de gestin de
memoria que se encargan de reservar y/o liberar cantidades de memoria arbitrarias (bloques)
segn se les solicite. Tambin es posible reservar memoria para otros propsitos, como por
ejemplo la tabla FAT del sistema de ficheros.
La estructura de datos que permite gestionar la memoria est en el fichero memoria.c y es la
siguiente:
/* ------------------------------------------------------------Tipos de datos para crear una lista de huecos en la memoria
disponible para programas. Los "links" se guardan en los
propios huecos. Especifican el tamao del hueco y un puntero
al siguiente. Este puntero es del tipo "_seg" que es especial
ya que tiene siempre un offset implicito 0 (no ocupa). Se hace
una unin con un tipo word para operar mejor con el */
typedef struct foo _seg *pHue_t; // ptr. offset siempre 0 (2 bytes)
typedef union {
pHue_t p;
/* se puede usar como puntero */
word_t w;
/* sin offset (siempre 0) o como un word */
} uHue_t;
struct foo { word_t tam; uHue_t uSig; };
word_t iniHeap, finHeap; // comienzo y fin memoria disponible
PRIVATE uHue_t priHue;
// primer elemento de la lista huecos
PRIVATE word_t memDisponible;
// en paragraphs
En el fichero memoria.h tenemos los prototipos de las funciones de gestin de memoria tomaMem() y sueltaMem() ya comentadas, adems de otras como memBIOS() para determinar la memoria disponible que reporta la BIOS, inicializarMemoria(), para inicializar
las estructuras de datos empleadas, inc() para incrementar en una cantidad un puntero largo, y
por ltimo volcar() y mostrarMemoria() para mostrar en pantalla el contenido de la
memoria. Tambin se declara mediante #define CHUNK 768, la cantidad de paragraphs que se
le asigna a todo proceso para datos + pila + buffer de teclas + video de ventana. Esta cantidad
fija se suma a la que requiera el cdigo del programa segn su tamao.
En cuanto a la gestin de los procesos la implementacin es estndar tal y como se explica en la
teora, ponindose en prctica el algoritmo de planificacin Round-Robin. La parte de cdigo
correspondiente a las operaciones bsicas con procesos est en el fichero procesos.c. Las
definiciones bsicas estn en "procesos.h" y son:
#ifndef PROCESOS_H
/* --------------------------------------------------------------- */
/*
procesos.h
*/
/* --------------------------------------------------------------- */
#include "windows.h"
#include "ficheros.h"
#define IDLE -1
#define CNSL 1
#define SERV 0
Pg. 165
Apndices
#define PWIN_SO tblProc[CNSL].pWin /* ptro. a ventana de la consola */
#define SRV_PILA 0x9000
/* origen de la pila para el servidor */
#define TICS_POR_RODAJA 2 /* num. tics por rodaja round robin */
typedef int pid_t;
// identificador de proceso
typedef enum {
LIBRE, EJECUCION, PREPARADO, BLOQUEADO
} estado_t;
/* estados de un proceso */
Pg. 166
Apndices
date_t getDate (void);
void pause (void); // implementada como llamada al sistema
#define PROCESOS_H
#endif
El tipo de datos descrProc_t corresponde al descriptor de proceso. Los campos que incluye
son: el estado de ejecucin (estado), la razn por la que el proceso est bloqueado, si fuera el
caso (esperaPor), la cola donde se pone el proceso cuando est bloqueado, si fuera el caso
(pColaBlk), el identificador del proceso (pid) y el de su padre (pPid), el campo sig (para enlazar descriptores y formar listas), un puntero a la pila donde se guardan los registros generales
para cambios de contexto (sp), la direccin y tamao de la ubicacin en memoria del proceso
(dirMem y tamMem), puntero a la ventana (incluye teclado) de trabajo del proceso (pWin). El
cluster y drive de trabajo del proceso (drvWork y clusWork), la tabla de descriptores de ficheros abiertos por el proceso (tdf[]), y por ltimo a ttulo informativo, el nombre del fichero
ejecutable.
El tipo cola_t se utiliza para representar colas de procesos y en concreto la cola de preparados.
Las declaraciones extern se encuentran declaradas nuevamente en el fichero procesos.c. La
directiva extern se utiliza para informa al compilador de que no reserve en ese momento el
espacio de la variable, nicamente tome nota de su tipo.
En el sistema SO cada proceso tiene su ventana, que es algo as como su pantalla virtual (aunque es posible que varios procesos usen la misma ventana). Por otro lado, como slo hay un teclado fsico es necesario repartirlo entre todos los procesos recurriendo a la multiplexacin en el
tiempo. Con ese fin se implementa el concepto proceso focal, de manera que los caracteres procedentes del teclado se asignan en cada momento al proceso focal, que es el que se considera que
tiene su ventana con el foco del teclado. Como ya vimos, es posible cambiar la ventana focal con
la tecla TAB.
Las funciones internas ms relevantes de gestin de procesos implementadas son:
void activarProceso (nproc_t npr);
void activarPreparado (void);
void bloquearProceso (esperaPor_t por, fpCola cola);
nproc_t crearProceso (word_t part, word_t size, char *name);
int killProcess (nproc_t npr);
La funcin activarProceso reanuda la ejecucin del proceso cuyo nmero se le pasa como
parmetro, restaurando su estado (contexto) al que tena cuando fue interrumpido o cedi el control de la cpu al S.O. En consecuencia, al llamar a esa funcin si no hay error, no habr retorno.
La funcin activarPreparado reanuda la ejecucin del primer proceso de la cola de preparados, invocando a la funcin activarProceso. Si la cola de preparados estuviese vaca, y puesto que no existe ningn proceso ocioso, el sistema activa (permite) las interrupciones (instruccin STI) y detiene el procesador (instruccin halt: HTL).
La funcin bloquearProceso pone al proceso que se le pasa como parmetro en estado de
bloqueado, especificando la razn de bloqueo que tambin se le pasa como parmetro. Si el
puntero a cola que se le pasa no es NIL pondra en dicha cola al proceso bloqueado. Esta funcin
Pg. 167
Apndices
presupone que el estado (contexto) del proceso se encuentra ya salvado en la pila, de la cual el
descriptor del proceso guarda su direccin.
La funcin crearProceso se invoca una vez que se ha conseguido memoria para el proceso y
se ha cargado el cdigo del mismo en ella. Bsicamente esta funcin inicializa los campos del
descriptor del proceso y pone ste en la cola de preparados, para cuando el planificador lo seleccione, ste pueda empezar su ejecucin.
Finalmente, killProcess mata el proceso que se le pasa como parmetro. Si fuera el que est
en ejecucin, al final, pondra en ejecucin el primero de la cola de preparados sin retornar al
punto donde se hizo esta llamada. Al eliminar el proceso realiza las siguientes acciones: Libera
su descriptor; devuelve la memoria al sistema, cierra su ventana terminal, si fuera el ltimo proceso en usar dicha ventana; cierra sus ficheros abiertos; y por ltimo, quita el proceso de la posible cola donde pudiera estar bloqueado (si fuera el caso).
8. ACTIVIDAD 4: Implementacin de la interrupcin Ctrl-C
En el PC la rutina de interrupcin del teclado (INT 09H Keyboard) correspondiente a la BIOS,
se encarga, entre otras cosas, de detectar por software si se est presionando simultneamente las
dos teclas Ctrl y Break, en cuyo caso simula una nueva interrupcin que utiliza como vector de
interrupcin, el 1Bh (INT 1BH Keyboard Break). Este hecho est documentado en el techelp, no
obstante, en el caso de la mquina virtual DosBox, esto no sucede, es decir, la combinacin CtrlBreak no produce dicha interrupcin 1B, por esta razn y para conseguir un resultado similar,
en el sistema SO se ha reemplazado esta combinacin por la combinacin Ctrl-C, la cual se
detecta en el mdulo teclaint.c y se encarga de generar dicha interrupcin, por lo que a todos
los efectos el resultado ser similar.
El objetivo de esta actividad es que el alumno programe la rutina de tratamiento de interrupcin
del vector 1B, para que en ella se maten inmediatamente todos los procesos vivos cuya ventana terminal sea la focal en el momento de la activacin de la rutina (o sea, al pulsar Ctrl-C).
El programa techelp nos indica que la direccin del vector que utiliza la interrupcin correspondiente al Ctrl-Break es la direccin de memoria 0000:006C (que sale de multiplicar el nmero del vector 1B por 4, ya que cada vector de interrupcin ocupa 4 bytes).
Vamos a centrarnos ahora en qu es lo que debe hacer la rutina de tratamiento de la interrupcin
1B para matar todos los procesos de la ventana focal. La respuesta no es complicada, ya que
acabamos de ver en la actividad anterior que el sistema operativo SO cuenta ya con la operacin que necesitamos (ver fichero: "procesos.c") que es:
int killProcess (nproc_t npr);
Pg. 168
Apndices
If (tblProc[npr].pWin==pWinFocal)
Nos dira si el proceso npr tiene como ventana la focal, es decir si es focal o no. Por otro lado,
el campo estado del descriptor nos indica el estado del proceso: LIBRE, PREPARADO, etc.
Si el estado es LIBRE, sabemos que no es un proceso vivo, y por tanto, al recorrer la tabla de
procesos, podramos descartar estos descriptores, para evitar matar un proceso no vivo. Sin
embargo, aunque se intentara matar un proceso no vivo (estado == LIBRE), no pasara nada
grave, ya que la funcin killProcess() ignora estas peticiones.
Resumiendo: En la rutina de tratamiento de interrupcin Ctrl-C hay que recorrer la tabla de
procesos, seleccionando los vivos y focales para matarlos, aunque hay que aadir un detalle importante: Si se llega a matar el proceso en ejecucin antes de finalizar el recorrido de la tabla, y
dado que la funcin killProcess() no retorna, podramos no haber conseguido el objetivo,
ya que podra haber ms procesos focales en la tabla de procesos.
Pasemos ahora a otra cuestin. Cmo incorporar la modificacin/mejora dentro del cdigo del
sistema SO? La respuesta la podemos encontrar fijndonos en la estructura de alguno de los
ficheros de SO donde se tratan las rutinas de tratamiento de interrupcin, por ejemplo "teclaint.c", del que vamos a indicar a continuacin su organizacin. Antes de eso conviene
aclarar que aunque en alguna parte del cdigo de SO que veamos haya instrucciones en ensamblador, no se va a exigir al alumno que programe en ensamblador. Dicho esto a continuacin se
muestra el cdigo bsico empleado en dicha rutina:
/* -------------------------------------------------------------------- */
/*
Rutina de tratamiento de la interrupcin xxx
*/
/* -------------------------------------------------------------------- */
#include tipos.h
#include rticomun.h
/* Numero de vector utilizado para la interrupcin xxx */
#define V_INT_xxx 0x?? // Sustituir las ?? Por su valor
PRIVATE void far * Old_VI_xxx; // para salvar antiguo vector de cBreak
/* ----------------------------------------------------------------las rutinas de tratamiento de interrupcin (RTI) salvan automticamente los registros en la pila del proceso interrumpido y adems restablecen el registro DS al valor original del S.O., con ello
los registros quedan preservados, pero como se va a establecer
una nueva pila dentro del espacio del S.O., tenemos que salvar los
punteros de pila (SS:SP) en el descriptor del proceso interrumpido.
Cuando se desee volver al punto de interrupcin se debe restaurar
antes dicha pila y despus efectuar el fin del tratamiento de la RTI
(pop's e IRET). Para establecer la nueva pila dentro de SO voy a
considerar una nueva base con un valor inicial BASE_PILA. Se dejan
0xFFFF-BASE_PILA KB para la consola cuya pila empieza en la FFFE.
------------------------------------------------------------------*/
void interrupt rti_xxx (void) {
static int i; /* ejemplo de variable local static permitido */
setNewStack(); /* se establece una nueva pila dentro de SO*/
/* Ctrl-c debe matar todos los procesos de la ventana focal (de teclado),
excepto la consola y el servidor */
/* ----- Aqu vendra el cdigo propiamente dicho de la RTI ------ */
...
etc
Pg. 169
Apndices
/* ------------------- fin del cdigo especifico -------------------*/
restoreStack(); /* se restaura la pila antes de volver */
}// rti_xxx
void redirigirInt_xxx (void) {
asm cli
Old_VI_xxx = ((ptrTVI_t) 0) [V_INT_xxx];
((ptrTVI_t) 0) [V_INT_xxxx] = (void far *) rti_xxx ;
asm sti
}
void restablecerInt_xxx (void) {
asm cli
((ptrTVI_t) 0) [V_INT_xxx] = Old_VI_xxx;
asm sti
}
/* --------------------------------------------------------------------- */
Vamos a explicar un poco este cdigo: Comienza con la directivas #include del fichero tipos.h
y rticomun.h. En el primero se encuentra en tipo ptrTVI_t que se usa ms abajo y en el
segundo,
se encuentran los prototipos de las funciones setNewStack() y
restoreStack() empleadas para establecer la nueva pila dentro de SO y restaurarla.
Seguidamente encontramos la directiva #define V_INT_xxxx, que se utiliza simplemente por
legibilidad y mantenibilidad. Las xxx deben ser sustituidas por un identificador ms o menos
nemnico de la interrupcin con la que tratamos. Despus se declara una variable del tipo
puntero lejano a funcin, la cual servir para guardar la direccin del vector de interrupcin que
hubiera antes de que SO establezca el suyo propio. Esta variable se usa en las funciones
redirigirInt_xxx() y restablecerInt_xxx() que se encargan, la primera, de salvar
y establecer el nuevo vector de interrupcin, y la segunda, de restaurar el vector original. Es
conveniente aclarar la razn de la existencia de estas funciones. La funcin
redirigirInt_xxx se requiere para establecer en el vector adecuado la direccin de la
rutina de tratamiento que se desea incorporar a SO, sin embargo no est tan claro la necesidad
de la funcion restablecerInt_xxx, ya que en principio, se puede pensar que cuando el
sistema SO acabe, no hay necesidad de restaurar los vectores modificados durante el arranque.
Si bien esto es cierto, no hay que olvidar que por varias razones, el sistema SO tambin puede
funcionar como invitado del sistema MS-DOS, y en ese caso s es necesario restaurar los
vectores a su estado original para no daar el sistema anfitrin.
Antes de seguir es conveniente recordar un poco el mecanismo de las interrupciones del
procesador. Cuando se produce una interrupcin, el procesador pasa a ejecutar la instruccin que
se encuentra en la direccin apuntada por el vector asociado a la interrupcin que se ha
producido, apilando previamente la direccin de retorno y los flags (banderas) de estado del
procesador, e inhibiendo adems las interrupciones. Una vez dentro de la RTI, lo primero que
se hace es salvar el contenido de los registros en la pila. Aunque el cdigo que se encarga de ello
no aparece explcitamente en el fuente C, el compilador lo genera automticamente cuando se
especifica que la funcin es de tipo void interrupt, como es el caso de la rutina de
tratamiento de interrupcin vista arriba: rti_xxx(). Estas funciones, adems de tener al
comienzo una serie de instrucciones de apilamiento de todos los registros del procesador,
tambin restauran el valor del registro de segmento de datos DS al valor que debe tener para
direccionar el rea de datos del sistema SO. Esto nos facilita el trabajo ya que la funcin
preserva el estado de los registros y establece el valor del DS para poder trabajar con las
variables globales, todo ello de manera implcita. A partir de ese momento habra que aadir el
cdigo necesario para lo que se requiera, pero normalmente la RTI lo que suele hacer justo a
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 170
Apndices
continuacin, es establecer una nueva pila dentro del espacio de SO, lo que le permitira no
depender del espacio disponible de pila del proceso interrumpido, y por simetra, tambin justo
antes de finalizar, tendra que restaurar la pila dejndola como estaba antes de la interrupcin.
Este comportamiento, que es el habitual para cualquier RTI, no debe realizarse en este caso, ya
que la RTI de Ctrl-C se invoca desde dentro de otra RTI, la de teclado, la cual a su vez ya ha
realizado esta accin, y volver a realizarla corrompera la pila previamente establecida dentro de
SO, por tanto, en el cdigo listado arriba, el cual se puede tomar como ejemplo bsico de una
RTI, hay que eliminar para este caso concreto las llamadas a las funciones setNewStack() y
restoreStack(). Por ltimo un detalle importante a tener en cuenta a la hora de implementar una
RTI: No se deben declarar variables de mbito local porque se ubican en pila. Si se requisiera
alguna debe declararse global local con el atributo static, lo cual hace que se ubiquen en el
segmento de datos.
Una vez programada la rutina de tratamiento (rti_xxx), necesitamos programar tambin tanto la
redireccin del vector de interrupcin para que apunte a dicha rutina, como su posterior restauracin. Para ello usaremos las funciones ya vistas arriba: redirigirInt y restablecerInt, personalizndolas para nuestro caso, quedando nicamente pendiente la cuestin de dnde invocarlas.
La respuesta es sencilla. Dentro del fichero so.c se podr ver justo al principio, de la funcin
main(), cmo se invocan el resto de funciones de redireccionamiento de los vectores de interrupcin, y tambin de igual modo, se puede ver cuando se trata el comando EXIT dentro del
switch general, que tambin se invocan todas las funciones de restauracin de los vectores de
interrupcin. En estos puntos es donde debemos insertar nuestros cambios o mejoras.
Una vez hechos estos cambios, si compilramos el proyecto SO sin ms medidas, apareceran
errores indicando que no se encuentran los prototipos de las funciones redirigirInt y restaurarInt. El motivo es muy sencillo. Dichos prototipos se han de incluir en un fichero tipo .h caracterstico de C para poder ser usado donde se requiera. Este fichero lgicamente debera llamarse break.h y debe ser incluido en el fichero fuente so.c. Podemos tomar como ejemplo otros
ficheros .h, como por ejemplo timerint.h.
Por ltimo slo nos falta integrar estos ficheros en el proyecto so.prj. Para ello basta con insertar el fichero break.c en el fichero del proyecto, abriendo la ventana de proyecto e insertando
dicho fichero. Una vez hecho todo esto, ya sera posible compilar nuevamente el proyecto SO
mediante la tecla F9 ( Ctrl-F9 para compilar y ejecutar).
En la evaluacin de este apartado se comprobar que, en el sistema SO modificado por el
alumno, la combinacin de teclas Ctrl-C mata todos los procesos cuya ventana es focal, tanto si
estn en ejecucin como si estn preparados o bloqueados.
Para poder probar que la mejora pedida al alumno funciona hay que conseguir que ms de un
proceso use una misma ventana. Para este propsito se ha incluido en el comando de ejecucin
de procesos, un parmetro adicional numrico que indica el pid del proceso de cuya ventana
queremos usar como anfitriona, por ejemplo, si tecleamos: A:>hola 2, querramos ejecutar el
programa hola pero en la ventana del proceso cuyo pid fuera 2. Al ejecutarse en la ventana
de otro proceso, de algn modo, las pulsaciones de tecla se han de repartir entre los procesos que
comparten la ventana. En este sistema se ha optado por hacer que el ltimo proceso que pida una
tecla se ponga el primero en la cola de teclas de su ventana terminal, lo cual no tiene porque ser
la mejor poltica pero puede valer para realizar pruebas.
Pg. 171
Apndices
Una vez compilado podemos ejecutar el sistema SO pulsando la combinacin de teclas Ctrl-F9
o bien usar el ratn para abrir el men -> Run -> Run. Esta opcin, al ejecutar SO sin salir del
entorno TC, no deja mucha memoria disponible y si queremos probar SO con ms memoria es
preferible salir de TC y ejecutar SO desde la lnea de comando:
C:\MISO>SO <
(Aunque ejecutemos SO desde MSDOS slo podremos acceder desde SO a los ficheros que
estn en las unidades montadas fsicamente en Dos-Box como 0, 1, 2 y/o 3.)
Se recuerda que la velocidad de ejecucin del programa puede variar mucho de ejecutarlo dependiendo de la seleccin que se haya hecho para la CPU speed.
10. ACTIVIDAD 6: Implementacin de excepciones (divisin por 0 y overflow)
En este apartado el alumno programar las rutinas de tratamiento de las excepciones de divisin
por 0 y overflow. Dado que la programacin de esas rutinas es muy parecida a la de las rutinas de
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 172
Apndices
Ms explcitamente son: el vector 00H para el caso de la excepcin de divisin por 0, y el vector
04H para el caso del overflow. Podemos conocer cmo funcionan esas excepciones consultando
los enlaces correspondientes:
Pg. 173
Apndices
Segn las pantallas anteriores las rutinas de tratamiento de las dos excepciones toman el control
automticamente al ejecutar la instruccin div (o idiv, divisin con signo) y la instruccin into (interrupcin si hay overflow) respectivamente bajo ciertas condiciones. En el primer caso la
condicin que dispara la excepcin es que el divisor valga 0, o que el cociente no quepa en el
registro destino de ese cociente (AX o AL). En el segundo caso la excepcin se produce al ejecutar into estando activado el flag de overflow.
En cuanto al tratamiento de la excepcin, deber ser el siguiente:
-
La excepcin debe provocar la muerte inmediata del proceso responsable de la excepcin excepto si es la consola o el servidor, y
Para escribir datos en la consola podemos hacer uso de algunas de las siguientes funciones auxiliares internas del sistema SO (declaradas en windows.h):
PrintStr(str) ;
PrintDec(numero, ancho);
PrintHex(numero, ancho);
En las funciones anteriores que escriben nmeros debe indicarse el nmero de cifras ( ancho)
con el que va a escribirse el nmero. Si el nmero de cifras especificado es insuficiente para el
nmero que se quiere escribir, la funcin utiliza como ancho el nmero de cifras necesario. Por
ese motivo normalmente se indica como ancho una nica cifra (es decir ancho 1). En el caso de
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 174
Apndices
Apndices
02/09/99
9899se3
Lo primero ser establecer la interfaz de la llamada al sistema con los programas de usuario (ver
los ficheros "c:\miso\ll_s_so\*.c"). Vamos a convenir que para hacer uso de esta nueva llamada al
sistema habr que aadir en el fichero proc-ifz.c la siguiente funcin de biblioteca:
/* ----- Interfaz de llamada a sistema sleep ------- */
void sleep (unsigned decimas) {
asm { MOV AH,9;
MOV BX,decimas;
INT VINT_SO } // VINT_SO : vector llamada al sistema SO
}
Lo anterior quiere decir que los programas de usuario podrn solicitar al sistema operativo que
los mantenga bloqueados durante un cierto nmero de dcimas de segundo, para lo cual ejecutarn la funcin anterior sleep(decimas). En cuanto al sistema operativo (el cual toma el control tras la instruccin (trap) INT VINT_SO, en la funcin "rti_SO" del fichero "llamadas.c"), vemos que debe interpretar el cdigo 09h que le llega en el registro AH, como una peticin de servicio sleep, debiendo ceder el control a la funcin encargada de implementar dicho servicio y que vamos a llamar so_sleep (por mantener el mismo criterio de denominacin
de las funciones de servicio). Esta funcin tomar el valor del parmetro decimas entregado
en el registro BX, donde lo dej la funcin de interfaz sleep, y se proceder a bloquear al
proceso en ejecucin y a activar al siguiente preparado.
Para conseguir este primer objetivo el sistema SO nos ofrece en el fichero llamadas.c la macro RGP(N,R), la cual nos facilita el acceso al contenido de cualquier registro guardado en la
pila de un determinado proceso (recordemos que cuando se realiza una llamada al sistema, la
rutina de tratamiento de interrupcin (RTI) guarda en la pila todos los registros). Con esta macro
podemos acceder a su contenido fcilmente, por ejemplo: RGP(nprEjec,B.X) nos dara el valor guardado en pila del registro BX del proceso al que se est atendiendo. De este modo, acceder al parmetro decimas es sencillo, bastara poner la macro tal y como se ha mostrado en el
ejemplo anterior.
Pg. 176
Apndices
Pero hay que hacerse otra pregunta. Cmo se bloquea el proceso y se activa el siguiente preparado?, afortunadamente la respuesta es sencilla. El sistema SO dispone de la funcin de uso
interno bloquearProceso(razon,&cola), la cual nos permite en una sola llamada hacer
lo siguiente: a) Dejar bloqueado al proceso especificando la causa (si ha sido por espera de teclado, por dormir, etc.), b) Poner en una cola de procesos bloqueados a dicho proceso, y por ltimo,
c) Activar el siguiente proceso preparado disponible en la cola de preparados.
Relacionado con la funcin de arriba, el sistema SO tiene definido un tipo en procesos.h denominado esperaPor_t, el cual sirve para especificar la razn de bloqueo. Al crear el servicio sleep, hay que aumentar la lista de valores de este tipo, aadiendo el valor BLK_SLEEP
para la razn durmiendo. Hay que preguntarse tambin, qu cola habra que pasar como parmetro a la funcin bloquearProceso (). Es posible no pasar ninguna utilizando NIL, en cuyo caso los procesos dormidos no formara parte de ninguna cola. No obstante, sera preferible
aadir una nueva cola para este propsito, que podramos declarar en los ficheros procesos.h y
procesos.c, tomando como modelo la forma en la que estn declaradas las otras dos colas:
preparados y aServir.
Hasta aqu ya tenemos suficiente informacin para poder dejar un proceso bloqueado y en una
cola de espera, pero tenemos que resolver ms problemas. El proceso debe ser despertado transcurrido un lapso de tiempo especificado en el parmetro decimas. Para resolverlo, en primer
lugar debemos pensar dnde guardar el tiempo que falta para que un proceso despierte. El sitio
ms evidente es el descriptor de proceso. Podemos aadir un campo llamado lapso que mantendra los ticks de reloj que faltan para que el proceso despierte. El tipo de datos del descriptor
se llama descrProc_t, y est declarado en el fichero procesos.h. Una vez resuelto esto, se
nos plantea la necesidad de convertir las dcimas de segundo en ticks de reloj, para lo cual necesitamos saber cunto tiempo es un tick de reloj. Vamos o considerar que dicho tiempo es
aproximadamente 55 ms, y que al hacer la conversin realizaremos un redondeo, tal que si la
fraccin de tick es menor de 0,5 la despreciamos y si es superior incrementamos en uno el resultado.
Una vez resuelta implementacin de la funcin de servicio sleep(), para completar el servicio, tenemos que acometer el problema de despertar al proceso en el tiempo establecido. Parece
evidente lo ms sencillo es modificar la rutina de tratamiento de interrupcin del reloj, para que
lleve la cuenta de los ticks que le quedan a un proceso para ser despertado. Esto no es complicado, bastara con decrementar el campo lapso del proceso y al llegar a 0, despertar al proceso.
Cmo despertamos a un proceso dormido?; respuesta: lo quitamos de la cola de dormidos y lo
ponemos en la cola de preparados. En el fichero procesos.h se encuentran disponibles las siguientes funciones relacionadas con colas:
void encolar (fpCola cola, nproc_t npr);
void colar (fpCola cola, nproc_t npr);
nproc_t desencolar (fpCola cola);
void quitarDeCola (fpCola cola, nproc_t npr, nproc_t nprAnt);
nproc_t buscarEnCola (fpCola cola, nproc_t npr, bool *pErr);
La funcin encolar(), aade un proceso a una cola por el final y la funcin colar() por el
principio. La funcin desencolar() quita y devuelve el primer proceso de la cola. La funcin
quitarDeCola() quita el proceso especificado en npr de cualquier posicin en la que se encuentre en la cola. Es preciso pasarle tambin en nprAnt cual es el proceso anterior. Se usa
normalmente durante el recorrido de una cola. Por ltimo, la funcin buscarEnCola(), busca
Pg. 177
Apndices
Resulta muy til el uso de grep para localizar las lneas de los ficheros del cdigo de SO que
contienen una determinada palabra. Esta opcin se encuentra integrada en el men de TurboC
en: men , GREP.
Para comprobar la correccin de la implementacin de sleep se propone utilizar el siguiente programa de usuario (disponible en c:\miso\usrs_prg\calibra):
/* ----------------------------------------------------------------------*/
/*
calibra.c
*/
/* --------------------------------------------------------------------- */
/*
programa de usuario para calibrar la llamada al sistema sleep
*/
/* --------------------------------------------------------------------- */
#include "..\ll_s_so\inic-usr.c" //cod.inic.prog.usuario y llam. basicas
#include "..\ll_s_so\proc-ifz.c" //llamadas al sistema de procesos
int leerDec (void) {
/* leee un entero sin signo */
unsigned int acum = 0;
char car = '0';
/*-------------------*/
while ((car >='0') && (car<= '9')) {
if (acum>(0xFFFF-(car-'0'))/10) return -1; /* num. demasiado grande */
acum = 10 * acum + (car - '0');
car = leerTecla();
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 178
Apndices
printCar(car);
}//while
if (car != CR) return -1;
return acum;
}//leerDec
/* cifra erronea */
Tambin podr utilizarse el programa "erraslp.c" (donde se han sustituido los retardos mediante
bucles de espera por llamadas a sleep) para comprobar cmo cambia el funcionamiento respecto de errante.c, el cual utiliza espera activa. (Si se teclea el comando type leeme.txt mientras
se mueven, por ejemplo, cuatro errantes se observar la diferencia)
12. ACTIVIDAD 8: Implementacin de los semforos
El objetivo de esta actividad es dotar al sistema SO de los objetos abstractos semforos. El
sistema va a proporcionar a los procesos un cierto nmero de semforos con el fin de que los
procesos puedan sincronizarse. Las funciones a implementar son: Inicializar, bajar y subir semforo. Limitaremos la cantidad de semforos a un mximo de 10. Las funciones de interfaz, al
igual que se hizo anteriormente con sleep, se incluirn en el fichero proc-ifz.c.
El procedimiento es similar y basta repasar la actividad anterior para repetirlo sin problemas,
aunque hay que precisar cmo sern los prototipos de estas funciones y los valores de los registros a emplear en cada una de ellas. A continuacin se muestra dicha informacin:
/* En todas estas funciones el parmetro sem se pasa en
y el cdigo de operacin en AH. En iniSemaforo CX pasa
int iniSemaforo(unsigned sem, unsigned valor); //AH=0x0B,
int bajaSemaforo(unsigned sem);
//AH=0x0C,
int subeSemaforo(unsigned sem);
//AH=0x0D,
el registro BX,
el valor */
BX=sem, CX=valor
BX=sem
BX=sem
Para los detalles sobre la realizacin de estas funciones de interfaz, sese como modelo la funcin de interfaz de sleep() vista con anterioridad.
Conviene aclarar que el uso de estos registros es arbitrario, pero hay que respetarlo si deseamos
utilizar los programas de usuario ya compilados que se entregan con la prctica, ya que estos
programas utilizan de este modo los registros. Evidentemente si los recompilamos utilizando otra
convencin de registros no habra ningn problema.
A continuacin vamos a comentar algunos aspectos de la implementacin dentro SO. En primer lugar hay que decir que el funcionamiento de los semforos debe ser coherente con lo visto
Pg. 179
Apndices
en la parte de Teora correspondiente al apartado 2.3 de comunicacin entre procesos. Por otro
lado, y ya referente a los cambios a realizar en el cdigo de SO, ya sabemos que por lo general,
cada nueva llamada al sistema debe tener su propia funcin de servicio, la cual ha de aadirse a
las ya existentes en el fichero "llamadas.c", del mismo modo en el que se hizo con la llamada
sleep y que por seguir el criterio de nombres ya existente, deberan llamarse:
so_iniSemaforo, so_bajaSemaforo y so_subeSemaforo. Recuerdese adems el uso de la
macro RGP(N,R) vista en sleep, para acceder a los registros pasados en la pila cuando se efectu la llamada.
Parece natural el declarar una tabla en SO para mantener el estado o situacin de los semforos.
Lgicamente el nmero de elementos de la tabla ser el mximo nmero de semforos soportados; en nuestro caso 10. El nmero de semforo que se pasa como parmetro en estas llamadas al
sistema ser precisamente el ndice de esta tabla, por tanto un nmero de semforo podr valer
entre 0 y 9. Cmo deben ser los elementos de esta tabla? Como respuesta podemos decir que, en
principio, sera suficiente con que tengan un campo numrico para guardar el valor del semforo
y otro del tipo cola (cola_t) para registrar los procesos bloqueados en el semforo.
No hay que olvidar inicializar la tabla de semforos. Bien en algn punto del arranque del sistema SO bien en la declaracin esttica de la misma.
Como especificacin del funcionamiento de los semforos se nos pide lo siguiente: Cuando la
operacin subeSemaforo deba desbloquear un proceso y haya varios procesos en la cola del
semforo, se desbloquear al primer proceso de la cola, es decir al que lleva ms tiempo metido
en ella (poltica FIFO). Se recuerda que todas las colas del sistema SO enlazan los descriptores
haciendo uso del campo sig del descriptor de proceso (que est definido en procesos.h y que
ya existen en SO funciones que facilitan el trabajo con colas).
Debido a que se explica en la parte de teora cmo debe ser la implementacin de los semforos,
y a que ya se han descrito las operaciones bsicas de manejo de procesos dentro del sistema SO',
es el momento de que el alumno ponga en prctica todo lo que sabe.
Para probar la correccin de la implementacin de los semforos se proporciona en el directorio
c:\miso\usrs_prg\menusem el programa de usuario menusem.c que muestra un men para
realizar cada una de las operaciones propias de los semforos. Con l podemos crear varios procesos que ejecuten el programa, y conmutar entre ellos con la tecla TAB para que unos procesos
se bloqueen con la operacin bajaSemaforo, mientras que otros procesos se encarguen de desbloquearlos mediante operaciones subeSemaforo.
Otro programa de usuario que nos permite probar el funcionamiento de los semforos es el programa c:\miso\usrs_prg\cross\cross.c. Modificacin de errante.c, en el que se cumple la
siguiente condicin:
La cuarta y octava vueltas al circuito corresponden a un tramo de circuito muy estrecho y slo se permite realizar la vuelta a un nico proceso a la vez.
Pg. 180
Apndices
NDICE
1
Pg. 181
Apndices
1.
OBJETIVOS
Que el alumno profundice sobre el mecanismo de las llamadas al sistema con parmetros que
representan direcciones de memoria.
Que el alumno sea capaz de implementar en un sistema operativo, un tipo de paso de mensajes sencillo a travs de buzones (enviaMsjBuzon, recibeMsjBuzon), con una capacidad fija
que viene establecida por una constante interna del sistema operativo.
2. INTRODUCCIN
En esta prctica se pretende profundizar en la implementacin de uno de los tipos de llamadas al
sistema ms complejos, como son las correspondientes al paso de mensajes. El tratar este tipo de
llamadas al sistema tiene la ventaja de permitirnos ilustrar la comunicacin de datos desde el
espacio de direcciones de un proceso de usuario hasta el espacio de direcciones de otro usuario
distinto pasando por el espacio de direcciones propio del sistema operativo comn a ambos, algo
que resulta muy interesante para un primer curso de sistemas operativos.
El modelo de comunicacin que se va a implementarse ser el de la comunicacin asncrona,
indirecta, y simtrica, con buzones de capacidad limitada y mensajes de tamao fijo de 16 bytes
transmitidos mediante copia (ver paso de mensajes en el apartado 2.3 del tema de procesos).
3. TRABAJO A REALIZAR
Vamos a presuponer que el alumno conoce ya el funcionamiento del sistema SO, tanto a nivel
de usuario, introduciendo comandos a travs de la consola o haciendo llamadas al sistema desde
un programa, como a nivel de la estructura del programa SO en correspondencia con la de un
sistema monoltico. En relacin con dicha estructura, se supone el conocimiento de la implementacin concreta de los procesos (descriptores, tabla de procesos, cola de preparados, el planificador y el despachador) y de la gestin de memoria.
En pocas palabras el trabajo a realizar consiste en la implementacin de los manejadores de las
llamadas al sistema cuyas rutinas de interfaz vamos a denominar enviaMsjBuzon y recibeMsjBuzon, las cuales permitirn a los procesos comunicarse a travs de un esquema de paso de mensajes basado en buzones (comunicacin asnicrona, indirecta y simtrica). Los buzones son de capacidad limitada, debiendo admitirse tambin el caso particular de que sean de capacidad nula
(comunicacin sncrona, rendez-vous).
4. ESPECIFICACIN DEL PASO DE MENSAJES
Nuestro objetivo es aadir un mecanismo de paso de mensajes al sistema SO, ampliando su
conjunto de llamadas al sistema con las siguientes llamadas, de las cuales indicamos los prototipos de las correspondientes funciones de interfaz:
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 182
Apndices
/*
/*
/*
/*
/*
--------------------------------------------------------------------buzones.h
--------------------------------------------------------------------Especificacin de las funciones de interfaz para el paso de mensajes
---------------------------------------------------------------------
#define CAPACIDAD 2
#define MBOX_MAX 10
*/
*/
*/
*/
*/
// descriptor de buzn
La idea es que el sistema operativo va a ofrecer a los programas de usuario hasta 10 buzones
identificados cada uno de ellos por un nmero del 0 al 9 que denominaremos descriptor del
buzn. Los mensajes se envan/reciben a/de los buzones por lo que la comunicacin es indirecta.
La comunicacin es asncrona porque el uso del buzn permite que el proceso receptor no tenga
que estar listo para recibir, ya que la entrega se deposita en el buzn. Slo en el caso de que el
buzn sea de capacidad nula, el mensaje se entrega directamente al proceso, en este caso la comunicacin es sncrona, ya que se requiere que el proceso est listo para recibir. Un mensaje
queda identificado por su direccin de comienzo (que podemos ver como un puntero lejano),
entendindose que el mensaje est fsicamente compuesto por el valor de los 16 bytes consecutivos que se encuentran a partir de dicha direccin de memoria. Por tanto los mensajes son de tamao fijo igual a 16 bytes. A continuacin se muestran varios ejemplos del envo de diferentes
tipos de mensajes:
mboxDesc_t buzon = 5;
char str[16] = "123456789012345";
// los strings terminan con '\0'
int tabla[8] = { 60, 100, 200, 400, 800, 1500, 3000, 5000 };
struct {
char car1, car2 ;
int i;
long long1, long2, long3 ;
} registro = { 'A', 'B', -77, 100000, 1000000, 10000000 } ;
...
enviaMsjBuzon (buzon, str);
// el mensaje es una cadena de caracteres
enviaMsjBuzon (5, tabla);
// el mensaje es una tabla de 8 enteros
enviaMsjBuzon (buzon, ®istro); // el mensaje es un registro
Para recibir un mensaje, el proceso receptor debe indicar el buzn del cual quiere recibir el mensaje, as como la direccin de destino de los 16 bytes correspondientes al mensaje. La interpretacin de la estructura del mensaje es competencia del proceso receptor, y lo normal es que el proceso emisor y el receptor se hayan puesto de acuerdo de alguna manera en el formato de los
mensajes. Lo lgico es que los mensajes se depositen en estructuras de datos anlogas a las utilizadas por el emisor. Por ejemplo el proceso receptor de los tres mensajes enviados anteriormente
podra proceder de la siguiente manera:
mboxDesc_t mailbox = 5;
char cadena[16];
int bufer[8];
struct {
char c1, c2;
int e;
Pg. 183
Apndices
long l1, l2, l3;
} reg;
...
recibeMsjBuzon (5, cadena);
recibeMsjBuzon (mailbox, bufer);
recibeMsjBuzon (5, ®);
Tras la ejecucin de esas instrucciones por parte de los procesos emisor y receptor se cumpliran
las siguientes igualdades:
cadena[0]=='1', cadena[1]=='2', ..., cadena[14]=='5', cadena[15]=='\0'
bufer[0]==60, bufer[1]==100, ..., bufer[7]==5000
reg.c1=='A', reg.e==-77, ..., reg.l3==10000000
Adems de esas condiciones, el paso de mensajes impone la sincronizacin de los procesos que
se comunican, y en esta sincronizacin interviene decisivamente la capacidad de los buzones
(constante CAPACIDAD).
Una consecuencia de que enviaMsjBuzon y recibeMsjBuzon sean llamadas al sistema es que el
sistema operativo asegura que se ejecuten como acciones atmicas e indivisibles. Dicho de otro
modo, no puede haber dos procesos que estn ejecutando simultneamente dichas funciones. Si
dos procesos intentan ejecutar a la vez estas funciones, el sistema asegura que primero un proceso ejecutar su funcin y slo despus, el otro proceso podr ejecutar la suya. Por tanto, el emisor ejecuta primero enviaMsjBuzon y el receptor ejecuta despus recibeMsjBuzon, o sucede al
contrario.
Cuando un proceso llama a recibeMsjBuzon, si hay algn mensaje en el buzn, el proceso recibe
el mensaje (transfirindolo desde del buzn a la direccin de memoria especificada en la llamada) y sale de la llamada al sistema continuando su ejecucin. Si por el contrario, no hay mensajes
en el buzn, el proceso se bloquea y se queda al final de la cola de procesos que esperan recibir
un mensaje de dicho buzn. El proceso debe permanecer bloqueado hasta que llegue un mensaje
al buzn y le toque el turno de la cola de dicho buzn, es decir, que sea el primero de dicha cola.
Cuando esto suceda, el mensaje debe ser transferido, al igual que en el caso anterior, desde el
buzn a la direccin de memoria del proceso receptor y ste debe reanudar su ejecucin (pasar a
preparado). De lo dicho hasta ahora es fcil deducir que en la implementacin de buzones debe
haber un buffer para los mensajes (de tamao mximo CAPACIDAD) y una cola (tipo cola_t) para los procesos en espera de mensaje.
Anlogamente, cuando un proceso llama a enviaMsjBuzon, si hay espacio en el buffer del buzn,
el mensaje se transfiere desde la direccin de memoria especificada en la llamada a dicho buffer,
y el proceso sale de la llamada y contina su ejecucin. Si por el contrario, el buffer del buzn
est lleno, el proceso se bloquea y se pone al final de la cola de procesos que esperan enviar un
mensaje al buzn. El proceso debe permanecer bloqueado hasta que un proceso al llamar a recibeMsjBuzon, habilite espacio en el buffer del buzn para que el mensaje pendiente de entrega de
este proceso pueda depositarse en el buzn y con ello reanudar su ejecucin.
Como especificacin del servicio de paso de mensajes hay que decir que los mensajes deben
recibirse y enviarse por estricto orden cronolgico. Es decir, si por ejemplo un proceso A enva
un mensaje a un buzn y posteriormente lo hacen los procesos B , C y D, cuando cualquier
otro proceso intente recibir de dicho buzn, debe recibir el mensaje del proceso A, y si posteriormente este proceso o cualquier otro intente recibir ms mensajes, recibir respectivamente y
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 184
Apndices
por este orden, los mensajes de los procesos B, C y D. Este concepto es anlogo al caso
recproco de intentos de recepcin de los procesos A, B, C y D y de envos posteriores de
cualquier otro proceso.
De la exposicin anterior se deduce inmediatamente una estructura de datos vlida para representar los buzones en el ncleo de SO, utilizando en cada buzn una tabla circular (campo bufer)
para representar la cola de mensajes contenidos en el buzn. Ntese que la cola del buzn puede
contener en un momento dado procesos que estn esperando dejar un mensaje en el buzn y en
otro momento dado, procesos que estn esperando recibir un mensaje del buzn. Nunca puede
darse el caso de que haya procesos esperando dejar un mensaje y en el mismo momento, que
haya procesos esperando recibir un mensaje del mismo buzn, por ello no es necesario implementar dos colas en el buzn (una para procesos receptores y otra para procesos destinatarios).
#define CAPACIDAD 2
#define MBOX_MAX 10
#define LONG_MSJ 16
struct {
cola_t cola;
unsigned char buffer[Capacidad][LONG_MSJ];
unsigned in,out;
// buffer[in/out] primera entrada libre/ocupada
unsigned numMensajes;
// si numMensajes > 0
} buzon [MBOX_MAX];
Pg. 185
Apndices
El buzn al que se enva o del que se recibe un mensaje figura tras los : de la opcin 'B', la
cual nos permite seleccionar cualquier otro buzn, mediante un nmero entre el 0 y el 9. El mensaje que se enva es la ltima cadena de caracteres (de longitud mxima 15) que se haya establecido con la opcin 'M', el cual se muestra tambin tras los : en esta opcin. Despus de recibir
un mensaje con la opcin 'R', el mensaje recibido tambin se muestra en el mismo sitio, pero
aparecer una R tras los : de esta opcin. Del mismo modo, cuando se enva un mensaje, aparecer una E tras los : de la opcin E. Vamos a empezar enviando al buzn 6 el mensaje
"mensaje 61" (pulsar: B, 6, M, mensaje 61 <, E):
Vemos que el proceso 1 realiza el envo sin problemas, y el mensaje queda en el buzn 6.
Ahora enviamos al buzn 7 los mensajes "mensaje 71" y "mensaje 72", operaciones que tambin se realizan sin problemas, quedando los dos mensajes en el buzn 7 (pulsar: B, 7, M, mensaje 71 <, E, M, mensaje 72 <, E):
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 186
Apndices
Seleccionamos ahora el buzn 8 y le enviamos los mensajes "mensaje 81", "mensaje 82" y
"mensaje 83". Observamos que ahora el proceso queda bloqueado al intentar enviar el ltimo
mensaje ya que el buzn 8 est lleno a causa del envo de los dos primeros mensajes.
La nica manera posible de desbloquear al proceso violeta (pid=2) es que otro proceso reciba un
mensaje del buzn 8. Vamos a crear tres procesos ms que ejecuten tambin el programa menumsj.bin.
Pg. 187
Apndices
Ahora vamos a hacer que el proceso marrn (pid=3) enve al buzn 8 el mensaje "mensaje 84",
y que el proceso verde (pid = 4) enve al buzn 8 el mensaje "mensaje 85". Evidentemente esos
dos procesos quedarn tambin bloqueados.
Ahora vamos a hacer que el proceso azul (pid=5) reciba del buzn 8 cinco mensajes. Observaremos que los mensajes se reciben exactamente en el orden en el que se han enviado ("mensaje
81", "mensaje 82", "mensaje 83", "mensaje 84" y "mensaje 85") y que en las tres primeras
operaciones de recepcin desbloquean sucesivamente a los procesos violeta, marrn y verde,
siguiendo estrictamente la secuencia temporal en la que se bloquearon.
Diseo e implementacin de un Sistema Operativo para fines didcticos
Pg. 188
Apndices
Ahora vamos a hacer que el proceso azul se enve a travs del buzn 5 el mensaje "mensaje 51"
a s mismo. Tras el envo debemos utilizar la opcin 'm' para introducir una cadena de caracteres
vaca, borrando as el mensaje visualizado. Al recibir del buzn 5 debemos obtener el mismo
mensaje que enviamos ("mensaje 51").
Vamos a ver qu sucede ahora cuando varios procesos se quedan bloqueados intentando recibir
de un buzn que est vaco. Para ello vamos a hacer que los procesos marrn, verde y azul intenten recibir (en ese orden) del buzn 4.
Pg. 189
Apndices
Si ahora el proceso violeta enva al buzn 4 los mensajes "mensaje 41", "mensaje 42" y
"mensaje 43" lo que debe suceder es que los procesos marrn, verde y azul se desbloquearn
por ese orden al recibir los mensajes respectivos.
Lo visto hasta aqu debe ser suficiente para que el alumno se haya familiarizado suficientemente
con las operaciones de envo y recepcin de mensajes utilizando buzones de capacidad limitada,
por lo que vamos a dejar el nivel del usuario, para pasar a ver a continuacin algunos aspectos
sobre el desarrollo de los buzones para el sistema SO.
Pg. 190
Apndices
msj
Estas funciones podran definirse en un fichero independiente, pero por razones prcticas, es
mejor incluirlas en el fichero de rutinas de interfaz ya existente c:\miso\ll_s_so\proc-ifz.c.
Como puede apreciarse, en ambas funciones de interfaz el descriptor del buzn (nBuz) se pasa a
travs del registro CX, mientras que el segmento de la direccin del mensaje (msj) se pasa a
travs de ES, y su desplazamiento a travs de BX. Como siempre el cdigo de la operacin se
pasa a travs del registro AH, siendo su valor 0x0E para el caso de enviaMsjBuzon, y 0x0F para
el caso de recibeMsjBuzon.
Como ejemplo de programa de usuario que utiliza estas llamadas al sistema tenemos el programa
c:\miso\usrs_prg\menumsj\menumsj.c, el cual hemos utilizado en el apartado 6 para ejercitarnos en el uso del paso de mensajes.
7. IMPLEMENTACIN DE LAS FUNCIONES DE SERVICIO
Por seguir con la normativa sobre los nombres de las funciones de servicio es apropiado que los
nombres de los manejadores de las llamadas al sistema correspondientes a enviaMsjBuzon y recibeMsjBuzon sean so_enviaMsjBuzon() y so_recibeMsjBuzon() respectivamente. Estas
funciones, al igual que las dems, deben ubicarse en el fichero "c:\miso\llamadas.c".
El significado del descriptor de un buzn (nBuz) utilizado en las funciones enviaMsjBuzon y
recibeMsjBuzon debe ser el ndice del buzn en la tabla buzon, es decir si nBuz es un descriptor de buzn, el acceso al mismo sera: buzon[nBuz].
Tal y como ya se coment para la llamada sleep, es posible acceder fcilmente a cualquiera de
los registros pasados en la pila a travs de la macro:
#define RGP(N,R)(tblProc[N].sp->R)
Pg. 191
Apndices
El valor del parmetro msj que es un puntero, se enva a travs de los registros ES y BX, y
para formar un puntero mediante dos words disponemos de la macro PTR(S,O). En este caso
por ejemplo podramos usarlo del siguiente modo:
char far *miMsj = PTR(RGP(nprAtnd,es),RGP(nprAtnd,B.X))
Si deseamos conocer los nombres de todos los registros que se pueden usar con el parmetro R
de la macro, podemos ver los campos de la estructura de datos regsPila_t declarada en el
fichero c:\miso\tipos.h.
Para realizar las copias de los mensajes desde una zona de memoria cualquiera a otra, necesitamos emplear punteros largos y sera deseable disponer de alguna funcin que realizara dicho
trabajo. Afortunadamente disponemos de ella. La funcin _fmemcpy() de turboC que utiliza
punteros largos y cuyo prototipo podemos incluir con #include <mem.h>. Por ejemplo, para
copiar el mensaje cuya direccin est en pMsj, al buffer i del buzon n, haramos lo siguiente:
_fmemcpy(&buzon[n].buf[i], pMsg, LONG_MSJ);
//LONG_MSJ=16
Un aspecto muy interesante es el siguiente Dnde est (dnde se guarda) el mensaje de un proceso que se ha bloqueado al intentar enviar un mensaje? La respuesta es que el mensaje est en el
espacio de direcciones del proceso emisor, y que la direccin de comienzo de ese mensaje est
en los registros ES:BX de la trama de registros guardados en pila pertenecientes al contexto del
proceso emisor. El sistema operativo no tiene ningn problema en acceder a esos campos para
posteriormente recoger el mensaje y completar ese envo. La forma de hacerlo recomendada es
mediante el uso de la macro que ya hemos visto: RGP (N,R). En este caso pondramos en el
parmetro N el numero de proceso que obtengamos de la cola del buzn, y como siempre en el
R el registro, bien es o bien B.X.
Y anlogamente, el problema anterior se vuelve a plantear y se resuelve de forma similar, para el
caso en el que lo que queremos es obtener la direccin de la variable destino donde dejar un
mensaje dirigido a un proceso receptor cuando ste est bloqueado.
Por ltimo y para acabar, se recuerda que no hay que olvidar la inicializacin de la tabla de buzones del sistema SO, esttica o dinmicamente.
Pg. 192
Apndices
NDICE
1
Pg. 193
Apndices
1.
OBJETIVOS
2. INTRODUCCIN
En esta prctica se pretende implementar un comando de la consola del sistema operativo de
prcticas que compacte los huecos de memoria.
Durante la vida del sistema el recurso memoria es asignado en bloques de tamao variable, que
normalmente se utilizan para ubicar el cdigo y los datos de los procesos. Cuando estos procesos
mueren el sistema libera la memoria ocupada por ellos, producindose un hueco, o espacio de
memoria contigua, de tamao igual al bloque de memoria que se le asign cuando fue creado.
Estos huecos acaban eventualmente estando dispersos por toda la memoria, produciendo lo que
se conoce como fragmentacin externa de memoria. El objetivo de este comando es fusionar
todos estos huecos dispersos en uno slo, eliminando as dicha fragmentacin.
3. TRABAJO A REALIZAR
Vamos a presuponer que el alumno conoce ya el funcionamiento del sistema SO, tanto a nivel
de usuario, introduciendo comandos a travs de la consola o haciendo llamadas al sistema desde
un programa, como a nivel de la estructura del programa SO, puesto que se da por hecho que
ha realizado todas las prcticas anteriores.
Tal y como ya se comentado en la introduccin, el trabajo a realizar consiste en la implementacin de un nuevo comando, al que llamaremos compac que se encargar de fusionar todos los
huecos de memoria en uno slo, eliminando completamente la fragmentacin. Para conseguirlo,
el comando tendr que mover los objetos ubicados en la memoria asegurndose de que el cambio
sea totalmente transparente para los procesos. Ello es posible gracias a que los procesos en SO
admiten reubicacin dinmica, aunque eso s, asumiendo algunas pequeas restricciones. Actualmente todos los procesos de usuario disponibles como ejemplo las cumplen. En general la
principal restriccin consiste en que los procesos no deben emplear punteros largos para acceder
a zonas de memoria que no le corresponden y que pudieran formar parte de algn objeto ubicado
en memoria con posibilidad de reubicacin. Esta restriccin podra incluso superarse si se asumieran algunas reglas de comportamiento a seguir por dichos procesos y el propio SO.
Pg. 194
Apndices
Memoria disponible
En el mdulo memoria.c se encuentra el cdigo fuente que contempla todos los aspectos relativos y relevantes acerca de la gestin de memoria en SO, aunque para entender y llegar a implementar el trabajo pedido tendremos que estudiar tambin los mdulos: ficheros.c, procesos.c y windows.c, a los que se har referencia ms adelante, indicando aquellos aspectos que
hay que conocer para poder llevar a cabo la implementacin del comando compac. En la figura
siguiente se muestra un ejemplo de mapa de memoria.
Hueco 3
Proceso C
Hueco 2
finHeap
Proceso B
Hueco 1
Proceso A
S.O. y consola
priHue
iniHeap
En l se muestran tres procesos cargados en memoria y tambin tres huecos. En la zona inferior,
en las direcciones ms bajas de memoria se encuentra SO, donde a su vez se halla en l, en su
espacio de memoria, el proceso consola. Justo donde acaba la ltima direccin de memoria del
SO empieza la memoria de uso dinmico para asignacin. El valor de comienzo de esta zona lo
indica la variable iniHeap. La variable finHeap indica el final de la memoria disponible. La
operacin finHeap-iniHeap nos da el valor de la variable memDisponible. Todas estas
variables la establece SO durante el proceso de inicializacin. El tipo de datos de estas varia-
Pg. 195
Apndices
bles es word_t (unsigned int) y por tanto lo que representan son direcciones de segmento en el
caso iniHeap y finHeap y paragraphs (16 bytes) en el caso de memDisponible.
En el mdulo memoria estn implementadas las funciones TomaMem() y SueltaMem() que
sirven respectivamente para solicitar memoria y devolverla, y aunque son muy importantes, en
principio no es necesario conocerlas en profundidad para realizar el trabajo pedido. La funcin
mostrarMemoria, que se encarga de mostrar en pantalla la lista de huecos de memoria a peticin del comando mem, es interesante estudiarla, ya que nos permite ver como se recorre la
lista de huecos, no obstante tampoco esto es estrictamente necesario para cumplir nuestros objetivos, aunque eso s, usaremos el comando mem durante el desarrollo de la prctica para ver
los resultados de nuestro trabajo.
La lista de huecos est implementada usando los propios huecos para guardar los enlaces de la
lista. El primer hueco lo indica la variable priHue y los siguientes se obtienen accediendo a la
zona del hueco mediante punteros y utilizando el campo uSig para acceder al siguiente hueco y
el campo tam para conocer el tamao del hueco en paragraphs (unidades de 16 bytes). Los
tipos de datos empleados se muestran a continuacin:
/* -----------------------------------------------------------------------Tipos de datos para crear una lista de huecos en la memoria disponible para programas. Los "links" se guardan en los propios huecos. Especifican el
tamao del
hueco y un puntero al siguiente. Este puntero es del tipo
"_seg" que es especial ya que tiene siempre un offset implcito 0 que no
ocupa espacio. Hago una unin con el tipo word_ para operar mejor con
l.
---------------------------------------------------------------------- */
typedef struct foo _seg *pHue_t; // offset siempre = 0 (ocupa un word)
typedef union {
pHue_t p;
// dato que se puede ver como puntero sin offset
word_t w;
// como un word
} uHue_t;
struct foo { word_t tam; uHue_t uSig; };
/* ----------------------------------------------------------------------*/
Pg. 196
Apndices
los procesos y las ventanas. Para obtener estos datos hay que examinar el contenido de la tabla
de informacin de las unidades de disco (infDrv[]) y la tabla de procesos (tblProc[]), para ir rellenando, en una nueva tabla de objetos, las direcciones y tamaos de los objetos ubicados en
memoria que hemos obtenido de dicho examen. Estos objetos son, en el caso de las unidades de
disco, la FAT de cada unidad montada, en el caso de los procesos, las reas de cdigo y datos
del mismo, y en el caso de las ventanas, el objeto win_t que guarda toda la informacin sobre
una ventana terminal. La introduccin de estos datos en la nueva tabla debe ser de tal modo que
la tabla debe quedar al final ordenada por direcciones, preferiblemente de menor a mayor.
Una vez que tengamos registrados en la nueva tabla todas las direcciones y tamaos de todos
objetos que el sistema SO tiene ubicados en memoria dinmica, y adems tengamos los elementos de esta tabla ordenados por su direccin de memoria, podremos recorrer esta tabla siguiendo su ordenacin, e iremos comprobando por cada elemento de la tabla, si su direccin es
menor que una previamente establecida a la que llamaremos base, cuyo valor inicial es la del
primer hueco de la lista. Si la direccin del objeto fuera menor que la de la base no habra que
reubicar el objeto y pasaramos al siguiente objeto de la tabla, si por el contrario fuera superior,
habra que reubicar el objeto, es decir habra que mover el objeto (FAT, proceso o ventana) desde su posicin de memoria actual, a la direccin de base, y en este caso actualizaramos al
nuevo valor base = base + tamao del objeto reubicado. Cuando hayamos llegado al ltimo
objeto de la tabla tendramos en base la direccin del nico hueco resultante de este proceso,
siendo su tamao igual a la diferencia entre finHeap y base (tam = finHeap - base).
El procedimiento visto hasta ahora reubicara los objetos, pero habra que hacer ms cosas para
que el sistema siga funcionando correctamente. Habra que modificar tambin todas las direcciones o punteros que el sistema SO mantiene sobre aquellos objetos que se han reubicado. De
estos punteros slo hara falta cambiar la parte de segmento de la direccin, ya que la reubicacin se ha efectuado siempre con alineamiento a paragraph.
Seguidamente vamos a comentar en funcin del objeto reubicado, qu punteros hay que modificar y dnde se encuentran stos dentro de SO.
Si el objeto reubicado es una FAT basta con modificar el puntero a la misma que se encuentra
en la tabla de informacin de unidades de disco (infDrv[]). Esta tabla est definida en ficheros.h, y en ella tenemos el campo pFat que es un puntero largo a byte (fptrb_t) que apunta a
la FAT de la unidad (si es que est montada, sino valdra NIL). Si durante la reubicacin hemos
tomado nota del nuevo segmento de reubicacin y de qu FAT se trata, podramos hacer la modificacin utilizando la macro SEG(p) que nos permite acceder a la parte segmento de un puntero. Por ejemplo, supongamos que newSeg es el nuevo segmento de direccin de memoria del
objeto FAT reubicado correspondiente a la unidad drv, entonces podramos escribir:
SEG(infDrv[drv].pFat) = newSeg;
Alternativamente, tambin podramos usar la macro PTR(S,O), que nos construye un puntero a
partir de un segmento (O) y un desplazamiento (offset, O), y podramos escribir:
infDrv[drv].pFat = PTR(newSeg,0);
Ya que las direcciones de todos los objetos ubicados en memoria dinmica tienen siempre el
offset a 0 (ya que las funciones de tomar memoria y soltar memoria devuelve un valor del tipo
word que resulta ser el segmento de la zona de memoria que se asigna o libera).
Pg. 197
Apndices
Pasemos a analizar el caso de que el objeto reubicado fuera un proceso. En este caso habra que
hacer los siguientes cambios:
a) Modificar la direccin de memoria (segmento) especificada en el descriptor del proceso que
indica dnde est cargado el proceso en memoria (dirMem).
b) Modificar el segmento del puntero (sp) que guarda la direccin de la cima de la pila (puntero
de pila o stack pointer) donde se guardan los registros del contexto del proceso.
c) Modificar los registros CS, DS y ES, que se hallan en la trama de pila del contexto del proce-
so con el nuevo valor de segmento reubicado (dirMem). Para acceder a estos valores podemos
utilizar el valor de sp del siguiente modo: tblProc[np].sp->cs=dirMem;
donde np es el numero de proceso reubicado y dirMem la nueva direccin de segmento.
Los otros dos registros, DS y ES, dado el modelo de memoria que utilizan los procesos de
usuario, podemos asumir perfectamente que tendrn el mismo valor que el registro CS, por
ello la modificacin es similar a la ya vista.
En este momento conviene hacer la siguiente reflexin: El descriptor de proceso tiene tambin
un campo puntero llamado pWin que contiene la direccin de la ventana terminal del proceso,
la cual es uno de los objetos de posible reubicacin. En el momento en el que se estn haciendo
los cambios relacionados con la reubicacin del proceso, hemos modificado los punteros que
hacan referencia a la nueva posicin del proceso, retocando ciertos campos del descriptor del
proceso, sin embargo este campo pWin hace referencia a la ventana del proceso, la cual puede
haber cambiado de posicin o no. Este cambio se tratar cuando se efecte el proceso de retoque
de los punteros mantenidos por SO referentes a las ventanas (lista de ventanas), lo cual se comentar en el punto siguiente.
Vamos a tratar por ltimo el caso en que el objeto reubicado sea una ventana terminal. Este caso
es algo ms complejo ya que las ventanas forman parte de una lista que esta ordenada por la posicin que ocupa la ventana en la pantalla (primer plano, segundo plano, etc.) y hay que modificar los punteros de dicha lista referentes a las ventanas que se hayan reubicado. En primer lugar
vamos a ver algunas de las estructuras de datos usadas por SO para el manejo de ventanas, las
cuales se encuentran definidas en el fichero windows.h:
typedef struct wfoo win_t;
typedef win_t _seg *pWin_t;
#define SIZE_KEYBUF 127 // no pasar de 255
struct wfoo {
char nombre[12];
// nombre de la ventana
// --- Informacin de ventana --pantalla_t plano;
// guarda la informacin visual (una pantalla)
int
dx, dy;
// desplazamiento del plano respecto a la pantalla
pos_t
eSI, eID; // coordenadas del marco de ventana
byte_t
atr;
// atributo normal de la ventana
pos_t
cursor;
// posicin del cursor
pWin_t pWDw, pWUp;
// ventana de debajo y de encima
// --- Informacion de teclado --byte_t keyBuf [SIZE_KEYBUF];
byte_t nKeys;
// cantidad de teclas en el buffer
byte_t ent,sal;
// posiciones de tecla entrante y saliente
cola_t cola;
// cola de procesos esperando recibir tecla
};
extern pWin_t pWinTop;
extern pWin_t pWinFocal; // ventana con el foco del teclado
Pg. 198
Apndices
Las ventanas de los procesos estn organizadas formando una lista que est ordenada con arreglo
a la posicin que ocupan a la hora de ser mostradas en pantalla. Si suponemos una tercera dimensin, la profundidad, las ventanas se muestran y en pantalla ocultndose unas a otras. Podemos hablar de las ventanas que estn encima y de las que estn debajo, o tambin la ventana de
primer plano, de la ventana del fondo, del fondo de pantalla, etc. Pues bien las ventanas forman
una lista donde la primera es la que se presenta en primer plano y la ltima la que se encuentra
ms al fondo o en ltimo plano.
Del listado de cdigo de arriba, nos interesa sobre todo la variable pWinTop que es un puntero
a la primera ventana de la lista (ventana de primer plano) y los campos pWDw y pWUp de la
estructura win_t, que son los punteros que tiene toda ventana apuntando a la ventana que se
encuentra debajo y arriba de ella respectivamente. Son estos punteros los que hay que modificar
para que se ajusten a las nuevas posiciones de memoria de las ventanas cuando stas se reubican.
No hay que olvidar que adems de los cambios en esta lista, tambin habra que cambiar el
campo pWin del descriptor del proceso, tal y como ya se ha comentado con anterioridad.
Es conveniente por ltimo hacer dos consideraciones. 1) El procedimiento de compactacin debe
efectuarse con las interrupciones inhibidas, ya que si durante la reubicacin se intentase efectuar
un cambio de proceso el resultado podra sera desastroso, y 2) A la hora de mover los objetos en
memoria hay que tener en cuenta el sentido de la copia por posibles solapamientos. TC aporta
una funcin que tiene esto en cuenta movemem() pero slo admite como parmetros punteros
cortos, y necesitamos punteros largos y por otro lado, las funciones de copia que admiten punteros largos como parmetros (_fmemcpy().. ), dejan indefinido el resultado si hay solapamiento,
por lo que tampoco pueden usarse, por lo tanto a falta de una solucin mejor, habr que implementar una funcin especfica de copia para conseguir nuestros propsitos. Para facilitar esta
tarea al alumno, se muestra a continuacin una posible implementacin de esta funcin:
/* Mueve 'sz' paragraphs desde el segmento 'o' al segmento 'd'. lo
hace en unidades dword (4 bytes) para mejorar la velocidad. lo movido
debe ser menor de 64KB y 'd' debe ser menor que 'o' si se solapan */
void mueveMem (word_t d, word_t o, word_t sz) {
word_t i;
sz <<= 4; // multiplico por 16 para pasar a bytes
for (i=0; i < sz; i+=4)
*((fptrd_t)PTR(d,i)) = *((fptrd_t)PTR(o,i));
}//mueveMem
Pg. 199