Está en la página 1de 118

UNIVERSIDAD MAYOR DE SAN SIMON

FACULTAD DE CIENCIAS Y TECNOLOGIA


DEPARTAMENTO INFORMATICA – SISTEMAS

TEXTO DE REFERENCIA

SISTEMAS
OPERATIVOS

Jorge Walter Orellana Araoz

2016
Sistemas Operativos

Tema I
INTRODUCCIÓN A LOS SISTEMAS OPERATIVOS

1.1 SISTEMA OPERATIVO


Una computador esta constituido de uno o más procesadores, memoria principal, discos,
impresoras, teclado, pantalla y otros dispositivos de entrada/salida y la interrelación de sus
componentes hace que el computador se torne en un sistema muy complejo. Se precisa escribir
código de programas que controlen todos esos componentes y los utilicen de forma correcta, por
esto, los computadores están equipados con una capa de software que se denomina sistema
operativo.

Un sistema operativo es un programa que actúa como intermediario entre el usuario de un


computador y el hardware del mismo. El propósito de un sistema operativo es crear un entorno en
el que el usuario pueda ejecutar programas de forma cómoda y eficiente.

A pesar de que se usa sistemas operativos casi a diario en dispositivos móviles, celulares,
computadoras, etc.; es difícil definir qué es un sistema operativo ya que realizan dos funciones
diferentes (complementarias):
• Proveer una máquina virtual o extendida (interfaz), es decir, un ambiente en el cual el
usuario pueda ejecutar programas de manera conveniente, protegiéndolo de los detalles y
complejidades del hardware.
• Administrar eficientemente (optimizar) los recursos del computador (componentes tanto
físicos como lógicos: el procesador, memoria, discos, ratones o archivos) para soportar los
requerimientos.

1.1.1 El sistema operativo como máquina virtual


Escribir programas que hagan uso correcto de los componentes de una computadora no es una
tarea trivial. Si cada programador tuviera que preocuparse de, por ejemplo, como funciona el disco
duro del computador, teniendo además siempre presentes todas las posibles cosas que podrían
fallar, entonces a la fecha se habría escrito una cantidad bastante reducida de programas.
Es mucho más fácil decir “escriba adiós al final del archivo datos”, que
1. Poner en determinados registros del controlador de disco la dirección que se quiere
escribir, el número de bytes que se desea escribir, la posición de memoria donde está la
información a escribir, el sentido de la operación (lectura o escritura), además de otros
parámetros;
2. Decir al controlador que efectué la operación.
3. Esperar. Decidir qué hacer si el controlador se demora más de lo esperado (¿cuánto es “lo
esperado”?).
4. Interpretar el resultado de la operación (una serie de bits).
5. Reintentar si algo anduvo mal.
6. etc.
Además, habría que reescribir el programa si se instala un disco diferente o se desea ejecutar el
programa en otra máquina. Hace muchos años que quedó claro que era necesario encontrar algún
medio para aislar a los programadores de las complejidades del hardware. Esa es precisamente
una de las tareas del sistema operativo, que puede verse como una capa de software que maneja
todas las partes del sistema, y hace de intermediario entre el hardware y los programas del
usuario. El sistema operativo presenta, de esta manera, una interfaz o máquina virtual que es más
fácil de entender y de programar que la máquina pura. Además, para una misma familia de
máquinas, aunque tengan componentes diferentes (por ejemplo, monitores de distinta resolución o
discos duros de diversos fabricantes), la máquina virtual puede ser idéntica: el programador ve
exactamente la misma interfaz.
Desde esta perspectiva, la función del sistema operativo es presentar al usuario el equivalente de
una máquina extendida o máquina virtual que es más fácil de programar que el hardware
subyacente.

Ing. Jorge Orellana A. 1


Sistemas Operativos

1.1.2 El sistema operativo como administrador de recursos


La otra tarea de un sistema operativo consiste en administrar los recursos de un computador
cuando hay dos o más programas que se ejecutan simultáneamente y requieren usar el mismo
recurso (como tiempo de CPU, memoria o impresora).
Además, en un sistema multiusuario, suele ser necesario o conveniente compartir, además de
dispositivos físicos, información. Al mismo tiempo, debe tenerse en cuenta consideraciones de
seguridad: por ejemplo, la información confidencial sólo debe ser accesada por usuarios
autorizados, un usuario cualquiera no debiera ser capaz de sobrescribir áreas críticas del sistema,
etc. (En este caso, un usuario puede ser una persona, un programa, u otro computador). En
resumen, el sistema operativo debe llevar la cuenta acerca de quién está usando qué recursos;
otorgar recursos a quienes los solicitan (siempre que el solicitante tenga derechos adecuados
sobre el recurso); y arbitrar en caso de solicitudes conflictivas.

1.2 EVOLUCIÓN DE LOS SISTEMAS OPERATIVOS


Los sistemas operativos y la arquitectura de computadores han evolucionado de manera
interrelacionada: para facilitar el uso de los computadores, se desarrollaron los sistemas
operativos. Al construir y usar los sistemas operativos, se hace obvio que ciertos cambios en la
arquitectura los simplificaría. Por eso, es conveniente echar una mirada a la historia.

1.2.1. La primera generación (1945-1955): tubos de vacío y tableros de Conexiones

El primer computador digital fue diseñado por el matemático inglés Charles Babbage hace cerca de
siglo y medio. Era un computador totalmente mecánico, que Babbage nunca pudo construir,
principalmente debido a que la tecnología de la época no era capaz de producir las piezas con la
precisión requerida.

Después de eso, poco se hizo hasta la segunda guerra, alrededor de 1940 se construyeron las
primeras máquinas calculadoras usando tubos de vacío.
Estas máquinas de varias toneladas de peso eran
diseñadas, construidas, programadas y operadas por el
mismo grupo de personas. No había ni lenguajes de
programación, ni compiladores; mucho menos sistema
operativo. Cada programa se escribía en lenguaje de
máquina, usando tableros con enchufes e interruptores
y tenía que manejar todo el sistema (lo que era factible
gracias a que el programador era el mismo que diseñó y
construyó la máquina). Con el tiempo se introdujeron las
tarjetas perforadas, para reemplazar al tablero, pero el
sistema era esencialmente el mismo.

Ing. Jorge Orellana A. 2


Sistemas Operativos

1.2.1 La segunda generación (1955-1965): transistores y procesamiento por lotes


La introducción de los transistores permitió aumentar sustancialmente la confiabilidad de los
computadores, lo que a su vez hizo factible construir máquinas comerciales. Por primera vez hubo
una separación entre diseñadores, constructores, y programadores.
La aparición de los primeros compiladores (FORTRAN) facilitó la programación, a costa de hacer
mucho más compleja la operación de los computadores. Por ejemplo, para probar un programa en
FORTRAN recién escrito, el programador debía esperar su turno, y:
1. Cargar el compilador de FORTRAN, típicamente desde una cinta magnética.
2. Poner el grupo de tarjetas perforadas correspondientes al programa FORTRAN y correr el
compilador.
3. El compilador genera código en assembler, así que hay que cargar ahora el ensamblador
para traducirlo a lenguaje de máquina. Este paso requiere poner otra cinta con el
ensamblador. Ejecutar el ensamblador, para generar el programa ejecutable.
4. Ejecutar el programa.
Si hay errores en cualquiera de estos pasos, el programador debía corregir el programa y
comenzar todo de nuevo. Obviamente, mientras el programador ponía cintas y tarjetas, y mientras
se devanaba los sesos para descubrir por qué el programa no funciona, la CPU de millones de
dólares de costo se mantenía completamente ociosa. Se idearon mecanismos para mantener a la
CPU siempre ocupada. El primero fue separar el rol de programador del rol de operador. Un
operador profesional demoraba menos en montar y desmontar cintas, y podía acumular lotes de
trabajos con requerimientos similares: por ejemplo, si se acumula la compilación de varios
programas FORTRAN, entonces el compilador de FORTRAN se carga una sola vez para todos los
trabajos.

Aún así, en la transición de un trabajo a otro la CPU se mantenía desocupada. Aparecieron


entonces los monitores residentes, que fueron los precursores de los sistemas operativos. Un
monitor residente es un pequeño programa que está siempre en la memoria del computador,
desde que éste se enciende. Cuando un programa termina, se devuelve el control al monitor
residente, quien inmediatamente selecciona otro programa para ejecutar.

Ahora los programadores, en vez de decirle al operador qué programa cargar, debían informárselo
al monitor (mediante tarjetas de control especiales):
Esto se conoce como procesamiento por lotes: el programador deja su grupo de tarjetas, y
después vuelve a retirar la salida que se emite por la impresora (y que puede ser nada más que la
notificación de que el programa tenía un error de sintaxis).

1.2.2 La tercera generación (1965-1971): circuitos integrados y multiprogramación


El procesamiento por lotes evitó que la CPU tenga que esperar tareas ejecutadas por lentos seres
humanos. Pero ahora el cuello de botella se trasladó a los dispositivos mecánicos (impresoras,
lectoras de tarjetas y de cinta), intrínsecamente más lentos que las CPUs electrónicas. Para
resolver esto, aparece, dentro de la tercera generación de computadores, la multiprogramación:
varios trabajos se mantienen permanentemente en memoria; cuando uno de ellos tiene que
esperar que una operación (como grabar un registro en cinta) se complete, la CPU continúa con la

Ing. Jorge Orellana A. 3


Sistemas Operativos

ejecución de otro trabajo. Si se mantiene un número suficiente de trabajos en la memoria, entonces


la CPU puede estar siempre ocupada.

Pero el sistema sigue siendo esencialmente un sistema de procesamiento por lotes; los
programadores no interactúan en línea con el computador, los tiempos de respuesta desde que se
deja un trabajo para ejecución hasta conocer el resultado son demasiado grandes. De ahí nace el
concepto de tiempo compartido que es una variante de la multiprogramación en la cual una CPU
atiende simultáneamente los requerimientos de varios usuarios conectados en línea a través de
terminales.

Ya que los usuarios humanos demoran bastante entre la emisión de un comando y otro, una sola
CPU es capaz de atender, literalmente, a cientos de ellos simultáneamente (bueno, en realidad,
uno después de otro, pero los usuarios tienen la ilusión de la simultaneidad). Por otra parte,
cuando no hay ningún comando que ejecutar proveniente de un usuario interactivo, la CPU puede
cambiar a algún trabajo por lote.

1.2.3 La cuarta generación (1971-Presente): computadores personales


Con el advenimiento de la integración a gran escala, que permitió concentrar en un solo chip miles,
y luego millones de transistores, nació la era de la computación personal. Los conceptos utilizados
para fabricar los sistemas operativos de la tercera generación de computadores siguen siendo, en
general, adecuados para la cuarta generación. Algunas diferencias destacables:
• Dado los decrecientes costos de las CPUs, ya no es nada de grave que un procesador esté
desocupado.
• La creciente capacidad de las CPUs permite el desarrollo de interfaces gráficas; buena parte
del código de los sistemas operativos de hoy es para manejar la interfaz con el usuario.
• Los sistemas paralelos (un computador con varias CPUs), requieren sistemas operativos
capaces de asignar trabajos a los distintos procesadores.
• Las redes de computadores y sistemas distribuidos abren nuevas posibilidades e imponen
nuevas obligaciones a los sistemas operativos.

En 1974, Intel presentó el microprocesador 8080 de ocho bits y buscó un sistema operativo para
ese procesador. Gary Kildall y un amigo construyeron primero un controlador para la unidad de
disco flexible de ocho pulgadas recién salida de Shugart Associates, enganchando así el disquete
al 8080, y dando lugar al primer microcomputador con disco. Luego Kildall escribió un sistema
operativo basado en disco llamado CP/M (Control Program for Microcomputers). Intel no pensó que
los microordenadores basados en disco fueran a tener mucho futuro, de manera que cuando Kildall
pidió quedarse con los derechos del CP/M, Intel se lo concedió. Kildall formó entonces una
compañía, Digital Research, para seguir desarrollando y vender el sistema operativo CP/M. En

Ing. Jorge Orellana A. 4


Sistemas Operativos

1977, Digital Research reescribió CP/M para que pudiera ejecutarse en los numerosos
microcomputadores que utilizaban el 8080, el Zilog Z80 u otros microprocesadores. Se escribieron
muchos programas de aplicación para ejecutarse sobre CP/M, lo que permitió a este sistema
operativo dominar por completo el mundo de los microcomputadores durante unos cinco años.

A principios de la década de 1980, IBM diseñó el PC y buscó software que se ejecutara en él.
Gente de IBM se puso en contacto con Bill Gates para utilizar bajo licencia su intérprete de BASIC.
También le preguntaron si sabía de algún sistema operativo que funcionara sobre el PC. Gates
sugirió a IBM que se pusiera en contacto con Digital Research, que entonces era la compañía de
sistemas operativos dominante. Kildall rechazó reunirse con IBM. Consecuentemente, IBM fue de
vuelta con Gates para preguntarle si podría ofrecerle un sistema operativo. Gates se percató de
que un fabricante de computadores local, Seattle Computer Products, tenía un sistema operativo
apropiado, DOS (Disk Operating System). Gates se reunió con el fabricante y se ofreció a
comprarle el sistema (supuestamente por 50.000 dólares). Luego Gates ofreció a IBM un paquete
DOS/BASIC, que IBM aceptó. IBM pidió que se hicieran ciertas modificaciones en el sistema, por lo
que Gates contrató a la persona que había escrito DOS, Tim Paterson, como empleado de su
naciente compañía, Microsoft, para que las llevara a cabo. El sistema revisado se rebautizó con el
nombre de MS-DOS (Microsoft Disk Operating System) y pronto dominó el mercado del IBM PC.

Para cuando el PC/AT de IBM salió a la venta el 1983 con la CPU 80286 de Intel, MS- DOS
estaba firmemente afianzado mientras que CP/M agonizaba. Más tarde se utilizó ampliamente MS-
DOS en el 80386 y el 80486. Aunque la versión inicial de MS-DOS era más bien primitiva, sus
versiones posteriores incluyeron funciones más avanzadas, incluyendo muchas procedentes de
UNIX. (Microsoft tenía perfecto conocimiento de UNIX, e incluso vendió durante los primeros años
de la compañía una versión para microordenador a la que llamó XENIX.)

CP/M, MS-DOS y otros sistemas operativos para los primeros microcomputadores obligaban al
usuario a introducir comandos a través del teclado. En un momento dado la cosa cambió, gracias a
las investigaciones hechas por Doug Engelbart en el Stanford Research Institute, en los años
sesenta. Engelbart invento la GUI (Graphical User Interface; Interfaz Gráfica de Usuario), provista
de ventanas, iconos, menús y ratón. Los investigadores de Xerox PARC adoptaron estas ideas,
incorporándolas a las máquinas que fabricaban. Steve Jobs, coinventor de la computadora Apple
en el garaje de su casa, visitó PARC, vio una GUI, y de inmediato se dio cuenta de su valor
potencial, algo que, de forma asombrosa, no hizo la gerencia de Xerox (Smith y Alexander, 1988).
Jobs se dedicó entonces a construir un Apple provisto de una GUI. Este proyecto condujo a la
construcción del ordenador Lisa, que resultó demasiado caro y fracasó comercialmente. El
segundo intento de Jobs, el Apple Macintosh, fue un enorme éxito, no sólo porque era mucho más
económico que Lisa, sino también porque era amigable con el usuario (user friendly), lo que
significa que iba dirigido a usuarios que no sólo carecían de conocimientos sobre computadores,
sino que además no tenían ni la más mínima intención de aprender.

Microsoft decidió desarrollar un sucesor para MS-DOS y estuvo fuertemente influenciado por el
éxito del Macintosh. La compañía produjo un sistema basado en una GUI al que llamó Windows, y
que originalmente se ejecutaba por encima de MS-DOS (es decir, era más un shell que un
verdadero sistema operativo). Durante cerca de 10 años, de 1985 a 1995, Windows no fue más
que un entorno gráfico por encima de MS-DOS. Sin embargo, en 1995 salió a la circulación una
versión autónoma de Windows, Windows 95, que incluía muchas funciones del sistema operativo y
sólo utilizaba el sistema MS-DOS subyacente para arrancar y ejecutar programas antiguos de MS-
DOS. En 1998 salió una versión ligeramente modificada de este sistema, llamada Windows 98. No
obstante tanto Windows 95 como Windows 98 contienen todavía una buena cantidad de lenguaje
ensamblador Intel de 16 bits. Otro sistema operativo de Microsoft es Windows NT (NT significa
Nueva Tecnología), que es compatible hasta cierto punto con Windows 95, pero que internamente
está reescrito desde cero. Se trata de un sistema completamente de 32 bits. El diseñador en jefe
de Windows NT fue David Cutler, quien también fue uno de los diseñadores del sistema operativo
VAX VMS, así que algunas de las ideas de VMS están presentes en NT. Microsoft confiaba en que
la primera versión de NT exterminaría a MS-DOS y todas las demás versiones anteriores de

Ing. Jorge Orellana A. 5


Sistemas Operativos

Windows porque se trata de un sistema enormemente superior, pero no fue así. Sólo con Windows
NT 4.0 comenzó a adoptarse ampliamente el sistema, sobre todo en redes corporativas. La versión
5 de Windows NT se rebautizó como Windows 2000 a principios de 1999, con la intención de que
fuera el sucesor tanto de Windows 98 como de Windows NT 4.0. Eso tampoco funcionó como se
pensaba, así que Microsoft sacó una versión más de Windows 98 llamada Windows Me
(Millennium edition). Las versiones de Windows XP, Vista, 7 y 8 están basados en Windows NT.

El otro contendiente importante en el mundo de los computadores personales es UNIX (y sus


diversos derivados). UNIX predomina en estaciones de trabajo y servidores de red. Es
especialmente popular en máquinas basadas en chips RISC de alto rendimiento. Aunque muchos
usuarios de UNIX, sobre todo programadores experimentados, prefieren un interfaz basado en
comandos en vez de una GUI, casi todos los sistemas UNIX reconocen un sistema de ventanas
llamado X Windows producido en el MIT. Linus Torvalds trabajó sobre Minix (una versión reducida
de Unix creada por Andrew Tanembaum) llamandola Linux y liberó el código fuente en internet, de
la cual se originaron infinidad de distribuciones y que también posee interfaces graficas en la
actualidad (KDE, GNOME,etc).

1.3 COMPONENTES DEL SISTEMA

Un sistema operativo crea el entorno en el que se ejecutan los programas. Podemos crear un
sistema tan grande y complejo como un sistema operativo sólo si lo dividimos en porciones más
pequeñas. Cada una de estas partes deberá ser un componente bien delineado del sistema, con
entradas, salidas y funciones cuidadosamente definidas. La interfaz entre el S. O. y los programas
del usuario se define como el conjunto de instrucciones ampliadas que proporciona el S. O. y son
las “llamadas al sistema”.

1.3.1 Gestión de procesos


Un programa no puede hacer nada si la CPU no ejecuta sus instrucciones. Podemos pensar en un
proceso como una porción de un programa en ejecución o todo el programa.
Un proceso necesita ciertos recursos, incluidos tiempo de CPU, memoria, archivos y dispositivos
de E/S, para llevar a cabo su tarea. Estos recursos se otorgan al proceso en el momento en que se
crea, o bien se le asignan durante su ejecución. Un proceso es la unidad de trabajo de un sistema.
El sistema consiste en una colección de procesos, algunos de los cuales son procesos del sistema
operativo (los que ejecutan código del sistema), siendo el resto procesos de usuario (los que
ejecutan código de algún usuario).

1.3.2 Gestión de la memoria principal


La memoria principal es crucial para el funcionamiento de un sistema de computación moderno. La
memoria principal es una matriz grande de palabras o bytes, cuyo tamaño va desde cientos de
miles hasta cientos de millones. Cada palabra o byte tiene su propia dirección. La memoria
principal es un depósito de datos a los que se puede acceder rápidamente y que son compartidos
por la CPU y los dispositivos de E/S. El procesador central lee instrucciones de la memoria
principal durante el ciclo de obtención de instrucciones, y lee y escribe datos de la memoria
principal durante el ciclo de obtención de datos.

1.3.3 Gestión de archivos


Un archivo es una colección de información relacionada definida por su creador. Por lo regular, los
archivos representan programas (en forma tanto fuente como objeto) y datos.
Los archivos de datos pueden ser numéricos, alfabéticos o alfanuméricos. Los archivos pueden ser
de forma libre, como los de texto, o tener un formato rígido. El sistema operativo posee un sistema
de archivos que se encarga de estructurar, organizar, gestionar estos archivos.

1.3.4 Gestión del sistema de E/S


Uno de los objetivos de un sistema operativo es ocultar las peculiaridades de dispositivos de
hardware específicos de modo que el usuario no las perciba.

Ing. Jorge Orellana A. 6


Sistemas Operativos

1.3.5 Gestión de almacenamiento secundario


El propósito principal de un sistema de computación es ejecutar programas. Estos programas,
junto con los datos a los que acceden, deben estar alojados en la memoria principal
(almacenamiento primario) durante la ejecución. Dado que la memoria principal es demasiado
pequeña para dar cabida a todos los datos y programas, y que pierde su información cuando deja
de recibir corriente eléctrica, el sistema de computación debe contar con algún almacenamiento
secundario para respaldar la memoria principal. La mayor parte de los sistemas de computación
modernos utiliza discos como principal medio de almacenamiento. El sistema operativo se encarga
de las siguientes actividades relacionadas con la gestión de discos: Administración del espacio
libre, Asignación del almacenamiento y Planificación del disco

1.3.6 Sistema de protección


En los primeros computadores, el operador tenía el control completo del sistema. Con el tiempo, se
fueron desarrollando los sistemas operativos, y parte del control se traspasó a ellos. Además, para
mejorar la utilización del sistema, se comenzó primero a manejar varios programas en memoria en
forma simultánea, y luego a atender simultáneamente a varios usuarios. Pero el remedio trajo su
propia enfermedad, ya que un programa podía por error o por mala intención de su creador,
modificar datos de otros programas, o peor aún, modificar el propio sistema operativo, lo que podía
“colgar” todo el sistema.

En general, un programa puede alterar el normal funcionamiento del sistema de las siguientes
formas:
• Ejecutando una instrucción de E/S ilegal (por ejemplo, que borre todos los archivos del disco).
• Sobrescribiendo áreas de la memoria que pertenecen al sistema operativo.
• No devolviendo el control de la CPU al sistema operativo.

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

1.3.6.1 Operación dual


Para asegurar una operación adecuada, se debe proteger al sistema operativo y todos los
programas del malfuncionamiento de cualquier otro programa. Para eso, el hardware debe proveer
al menos dos modos de operación.
• Modo usuario.
• Modo sistema (o privilegiado, protegido o supervisor).

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

1.3.6.2 Protección de E/S


Para prevenir que un usuario ejecute instrucciones de E/S que puedan provocar daño, la solución
es simple: las instrucciones de E/S sólo pueden ejecutarse en modo sistema. Así los usuarios no
pueden ejecutar E/S directamente, sino que deben hacerlo a través del sistema.
Para que este mecanismo de protección sea completo, hay que asegurarse que los programas de
usuario no puedan obtener acceso a la CPU en modo sistema. Por ejemplo, un programa de
usuario podría poner una dirección que apunte a una rutina propia en el vector de interrupciones.
Así, cuando se produzca la interrupción, el hardware cambiaría a modo sistema, y pasaría el
control a la rutina del usuario. O, también, el programa de usuario podría reescribir el servidor de la
interrupción.

1.3.6.3 Protección de memoria


No sólo hay que proteger el vector de interrupciones y las rutinas servidoras, sino que, en general,
queremos proteger las áreas de memoria utilizadas por el sistema operativo y por otros programas.

Ing. Jorge Orellana A. 7


Sistemas Operativos

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

1.3.6.4 Protección de CPU


Lo único que falta, es asegurarse que el sistema operativo mantenga el control del buque, o sea,
hay que prevenir, por ejemplo, que un programa entre en un ciclo infinito y nunca devuelva el
control al sistema operativo. Esto se logra con un timer (reloj).

1.3.7 Sistema de interpretación de órdenes


Uno de los programas del sistema más importantes de un sistema operativo es el intérprete de
órdenes o de comandos, que es la interfaz entre el usuario y el sistema operativo.
Algunos sistemas operativos incluyen el intérprete de órdenes en el núcleo; otros, como MS-DOS y
Unix, tratan el intérprete de órdenes como un programa especial que se está ejecutando cuando se
inicia un trabajo, o cuando un usuario ingresa en un sistema de tiempo compartido. La función del
intérprete de línea de comandos o shell es muy sencilla: obtener la siguiente orden y ejecutarla.

1.3.8 Llamadas al sistema


Son la interfaz entre el sistema operativo y un programa en ejecución. Pueden ser instrucciones en
lenguaje ensamblador (MSDOS) o pueden hacerse desde lenguajes de alto nivel como C (Unix,
Linux, Windows). El control pasa al vector de interrupciones para que una rutina la atienda. El bit
de modo se pone en modo monitor. El monitor analiza la instrucción que provocó la interrupción.
Así se ejecuta la solicitud y vuelve el control a la instrucción siguiente a la llamada al sistema. Los
parámetros asociados a las llamadas pueden pasarse de varias maneras: por registros, bloques o
tablas en memoria o pilas. Hay varias categorías de llamadas al sistema:
• Control de procesos: finalizar, abortar, cargar, ejecutar, crear, terminar, establecer y
obtener atributos del proceso, esperar un tiempo, señalar y esperar evento, asignar y
liberar memoria.
• Manipulación de archivos: crear, eliminar, abrir, cerrar, leer, escribir, reposicionar, obtener
y establecer atributos de archivo.
• Manipulación de dispositivos: solicitar, liberar, leer, escribir, reposicionar, obtener y
establecer atributos de dispositivo.
• Mantenimiento de información: obtener fecha y hora, datos del sistema, atributos.
• Comunicaciones: crear, eliminar conexión de comunicación, enviar y recibir mensajes,
transferir información de estado, etc.

1.4 ESTRUCTURA DE LOS SISTEMAS OPERATIVOS (ESTUDIO DE CASOS)

1.4.1 Estructura simple - Sistemas monolíticos.


Una forma de organizar un sistema operativo, es con una estructura simple (o sin estructura). El
sistema operativo se escribe como una colección de procedimientos, cada uno de los cuales llama
a los otros cuando lo requiere. Lo único que está bien definido es la interfaz de estos
procedimientos, en términos de parámetros y resultados.
Para generar el sistema operativo (el binario), se compilan todos los procedimientos y se enlazan
en un solo archivo (el kernel), que es el que se carga en la memoria cuando se enciende la
máquina.

El kernel es grande y cumple todas las funciones del sistema operativo. Es el caso de MS-DOS y
de las primeras versiones de Unix.

Ing. Jorge Orellana A. 8


Sistemas Operativos

Incluso en los sistemas monolíticos es posible tener al menos un poco de estructura. Los servicios
(llamadas al sistema) proporcionados por el sistema operativo se solicitan colocando los
parámetros en lugares bien definidos, como en registros o en la pila, y ejecutando después una
instrucción de trampa especial conocida como llamada al kernel o llamada al supervisor.

Esta instrucción conmuta la máquina del modo de usuario al modo de kernel y transfiere el control
al sistema operativo, lo cual se muestra como evento (1) en la figura. (La mayor parte de las CPU
tienen dos modos: modo de kernel, para el sistema operativo, en el que se permite todas las
instrucciones; y modo de usuario, para programas de usuario, en el que no se permiten
instrucciones de E/S y de ciertos otros tipos.)
A continuación, el sistema operativo examina los parámetros de la llamada para determinar cuál
llamada al sistema se ejecutará; esto se muestra como (2) en la figura. Acto seguido, el sistema
operativo consulta una tabla que contiene en la ranura k un apuntador al procedimiento que lleva a
cabo la llamada al sistema k. Esta operación, marcada con (3), identifica el procedimiento de
servicio, mismo que entonces se invoca. Una vez que se ha completado el trabajo y la llamada al
sistema ha terminado, se devuelve el control al programa de usuario (paso 4) a fin de que pueda
continuar su ejecución con la instrucción que sigue a la llamada al sistema.
Esta organización sugiere una estructura básica para el sistema operativo:
1. Un programa principal que invoca el procedimiento de servicio solicitado.
2. Un conjunto de procedimientos de servicio que llevan a cabo las llamadas al sistema.
3. Un conjunto de procedimientos de utilería que ayudan a los procedimientos de servicio.

En este modelo, para cada llamada al sistema hay un procedimiento de servicio que se ocupa de
ella. Los procedimientos de utilería hacen cosas que varios procedimientos de servicio necesitan,
como obtener datos de los programas de usuario.

1.5.2 Sistemas Estructurados

1.5.2.1 Estructuración en capas


Una generalización del modelo anterior es diseñar el sistema en capas. El primer exponente de
esta categoría fue el sistema operativo THE (Technische Hogeschool Eindhoven , Holanda, 1968),
un sistema de procesamiento por lotes. En el caso de THE, la estructuración en capas era más que
nada para modularizar el diseño (igual se compilaba y enlazaba todo en un solo gran archivo).

Ing. Jorge Orellana A. 9


Sistemas Operativos

Cada capa encapsula datos y las operaciones que manipulan esos datos. Una capa puede usar los
servicios sólo de capas inferiores.

La Capa 0 trabaja con la asignación del procesador, alterna entre los procesos cuando ocurren las
interrupciones o expiran los cronómetros y proporciona la multiprogramación básica.
La Capa 1 administra la memoria, asegura que las páginas (porciones de memoria) requeridas de
los procesos lleguen a memoria cuando fueran necesarias.
La Capa 2 administra la comunicación entre cada proceso y la consola del operador (Por sobre
esta capa, cada proceso tiene su propia consola de operador).
La Capa 3 controla los dispositivos de e / s y almacena en buffers los flujos de información entre
ellos (Por sobre la capa 3 cada proceso puede trabajar con dispositivos abstractos de E/S en vez
de con dispositivos reales).
La Capa 4 aloja los programas del usuario, los programas. del usuario no tienen que preocuparse
por el proceso, memoria, consola o control de e / s.
La Capa 5 localiza el proceso operador del sistema.

Como ventajas se pueden citar la modularización, ocultamiento de información, flexibilidad. Cada


capa se desarrolla y depura "sin mirar hacia arriba". Nuevas funciones pueden añadirse, y la
implementación de las existentes puede modificarse, mientras no se cambie su interfaz.
Las desventajas son que requiere cuidadosa planificación, para decidir qué cosa va en qué capa.
Por ejemplo, la capa que provee acceso al disco debe estar en una capa más baja que la que
administra memoria virtual, pero encima del planificador de CPU, pues el driver probablemente se
bloquea para esperar que una operación de E/S se complete. Sin embargo, en un sistema grande,
el planificador podría llegar a necesitar manejar más información de los procesos que la que cabe
en su memoria, requiriendo apoyarse en el disco. Otro potencial problema es la eficiencia, pues
algunos servicios pasan por varias capas antes de llevarse a cabo.
Una generalización mas avanzada del concepto de capas se presento con Multics (multiplexed
information and computing service) MIT, Bell Labs y General Electric.
Presenta una estructura en anillos concéntricos, siendo los interiores los privilegiados. Un
procedimiento de un anillo exterior, para llamar a un procedimiento de un anillo interior, debe hacer
el equivalente a una llamada al sistema.
Otro ejemplo de sistema operativo diseñado por capas es el OS/2, descendiente de MS-DOS.

1.5.2.2 Máquinas virtuales


Un sistema operativo de tiempo compartido cumple dos funciones: (1) multiprogramación, y (2)
proveer una máquina extendida, con una interfaz más conveniente para los programas de usuario
que la máquina pura. En la década del 70, a la IBM se le ocurrió separar completamente estas
funciones en el sistema operativo VM/370. El núcleo del sistema, llamado monitor de la máquina
virtual ejecuta directamente sobre el hardware, y su único objetivo es la multiprogramación,
ofreciendo múltiples máquinas virtuales a la capa superior.

Pero estas máquinas virtuales no son máquinas extendidas (con archivos y otras abstracciones),
como es lo habitual, sino copias exactas de la máquina original. Cada máquina virtual puede
ejecutar un sistema operativo diferente, construido para operar en la máquina original. La CPU se
comparte con planificación de CPU, la memoria con memoria virtual, y el disco se particiona.
No es simple de implementar. La máquina original tiene dos modos: monitor y usuario. El monitor
de la máquina virtual opera en modo monitor, y cada máquina virtual puede operar sólo en modo
usuario. En consecuencia se debe proveer un modo usuario virtual y un modo monitor virtual. Las

Ing. Jorge Orellana A. 10


Sistemas Operativos

acciones que en la máquina real causan el paso de modo usuario a modo monitor deben causar el
paso de modo usuario virtual a modo monitor virtual. ¿Cómo? Cuando un proceso de usuario hace
una llamada al sistema, el control se transfiere al monitor de la máquina virtual, quien devuelve el
control a la máquina virtual, simulando una llamada al sistema. Si en esas condiciones (operando
en modo monitor virtual, que en realidad no es más que modo usuario físico), se intenta accesar el
hardware (por ejemplo, una operación de E/S), el control vuelve al monitor de la máquina virtual,
quien efectúa, controladamente, la operación).

Las ventajas son que facilita el desarrollo de sistemas operativos (cuando la máquina es cara y no
se puede entregar una a cada programador). Sin este esquema, es necesario bajar la máquina
cada vez que quiere probar una modificación al sistema. También permite resolver problemas de
compatibilidad. Por ejemplo, si queremos desarrollar un nuevo sistema operativo, pero queremos
que los miles de programas DOS que existen sigan corriendo, podemos usar esta solución,
asignando una máquina virtual a cada programa DOS que se ejecute. Se puede ir más lejos, y
crear una máquina virtual distinta a la original. Así es como Windows para arquitectura Intel se
puede ejecutar, por ejemplo en una Silicon Graphics. El problema es que, en este caso, hay que
interpretar cada instrucción (en lugar de ejecutarla directamente).

1.5.2.3. Exokernel

Con el VM/370, cada proceso de usuario obtiene una copia exacta del ordenador real. Con el modo
8086 virtual del Pentium, cada proceso de usuario obtiene una copia exacta de un ordenador
diferente. Yendo un paso más lejos, algunos investigadores del M.I.T. construyeron un sistema que
proporciona a cada usuario un clon del ordenador real, pero con un subconjunto de los recursos
(Engler y otros, 1995). Así, una máquina virtual podría obtener los bloques de disco del 0 al 1023,
la siguiente podría recibir los bloques del 1024 al 2047, y así de forma sucesiva.

En la capa más baja, ejecutándose en modo núcleo, está un programa llamado exokernel. Su labor
consiste en asignar recursos a las máquinas virtuales y luego comprobar cualquier intento de
utilizarlos para garantizar que ninguna máquina trate de utilizar los recursos de cualquier otra.
Cada máquina virtual a nivel de usuario puede ejecutar su propio sistema operativo, como sobre el
VM/370 y los 8086 virtuales del Pentium, sólo que cada una está limitada a los recursos que
solicitó y que le fueron asignados.

La ventaja del esquema de exokernel es que ahorra una capa de conversión. En los otros diseños,
cada máquina virtual cree que tiene su propio disco, cuyos bloques van desde 0 hasta algún
máximo, lo que obliga al monitor de la máquina virtual a mantener tablas para convertir las
direcciones de disco (y todos los demás recursos). Con el exokernel no es necesario efectuar esa
conversión, pues lo único que tiene que hacer es mantenerse al tanto de qué recursos se han
asignado a qué máquinas virtuales. Este método sigue teniendo la ventaja de separar la
multiprogramación (en el exokernel) y el código del sistema operativo del usuario (en el espacio del

Ing. Jorge Orellana A. 11


Sistemas Operativos

usuario), pero con menos sobrecarga porque la única tarea del exokernel es evitar que las
máquinas virtuales se interfieran mutuamente.

1.5.2.3 MicroKernel (Modelo cliente – servidor)


Una tendencia en los sistemas operativos modernos consiste en llevar más lejos aún la idea de
subir código a las capas superiores y quitar tanto como sea posible del modo núcleo, dejando un
microkernel mínimo. El enfoque usual es implementar la mayor parte del sistema operativo en
procesos de usuario. Para solicitar un servicio, tal como la lectura de un bloque de un archivo, un
proceso de usuario (que ahora se denomina proceso cliente) envía una solicitud a un proceso
servidor, que realiza el trabajo y devuelve la repuesta.

Para solicitar un servicio (por ej.: lectura de un bloque de cierto archivo) según el modelo cliente –
servidor, el proceso del usuario (proceso cliente) envía la solicitud a un proceso servidor, realiza el
trabajo y regresa la respuesta.

El núcleo controla la comunicación entre los clientes y los servidores. Se fracciona el S. O. en


partes, cada una controlando una faceta, servicio a archivos, a procesos, a terminales, a memoria,
etc., cada parte pequeña y más fácilmente controlable.
Los servidores se ejecutan como procesos en modo usuario, no tienen acceso directo al hardware
y se aíslan y acotan más fácilmente los problemas.
Se adapta para su uso en los sistemas distribuidos, si un cliente se comunica con un servidor
mediante mensajes, no necesita saber si el mensaje se atiende localmente o mediante un servidor
remoto, situado en otra máquina conectada.

Algunas funciones del S. O., ( por ej. el cargado de comandos en los registros físicos del
dispositivo de e/s), presentan problemas especiales y distintas soluciones:
• Ejecución en modo núcleo, con acceso total al hardware y comunicación con los demás
procesos mediante el mecanismo normal de mensajes.
• Construcción de un mínimo de mecanismos dentro del núcleo manteniendo las decisiones de
política relativas a los usuarios dentro del espacio del usuario.

Minix [Tanenbaum, 1998], Mach [Accetta, 1986] y Amoeba [Mullender, 1990] son ejemplos de
sistemas operativos que siguen este modelo. Windows NT también sigue esta filosofía de diseño,
aunque muchos de los servidores (el gestor de procesos, gestor de E/S, gestor de memoria, etc.)
se ejecutan en modo núcleo por razones de eficiencia.

Ing. Jorge Orellana A. 12


Sistemas Operativos

Tema II
GESTIÓN DE PROCESOS

2.1. PROCESO.
Todos los computadores pueden hacer varias cosas a la vez, mientras está ejecutando un
programa de usuario puede perfectamente también estar leyendo de un disco e imprimiendo
texto en una pantalla o una impresora. En un sistema multiprogramado la CPU (Unidad Central
de Proceso) también conmuta de unos programas a otros, ejecutando cada uno de ellos durante
decenas o cientos de milisegundos. Aunque, estrictamente hablando, en cualquier instante de
tiempo la CPU sólo está ejecutando un programa, en el transcurso de 1 segundo ha podido estar
trabajando sobre varios programas, dando entonces a los usuarios la impresión de un cierto
paralelismo (pseudoparalelismo), en contraste con el auténtico paralelismo del hardware de los
sistemas multiprocesador (que tienen dos o más CPUs compartiendo la misma memoria física).
Seguir la pista de múltiples actividades paralelas resulta muy complicado, por ese motivo los
diseñadores de los sistemas operativos han desarrollado un modelo conceptual evolucionado (el
de los procesos secuenciales) que permite tratar el paralelismo de una forma más fácil.

En este modelo, todo el software ejecutable en el ordenador, incluyendo a veces al propio


sistema operativo, se organiza en un número de procesos secuenciales, o simplemente
procesos. Un proceso es justamente un programa en ejecución, incluyendo los valores actuales
del contador de programa, registros y variables. Conceptualmente cada proceso tiene su propia
CPU virtual. En realidad, la CPU real conmuta sucesivamente de un proceso a otro. Esta rápida
conmutación de un proceso a otro en algún orden se denomina multiprogramación.

Debe hacerse una distinción entre un programa y un proceso. Un programa es una entidad
estatica constituida por sentencias de programa que definen la conducta del proceso cuando se
ejecutan utilizando un conjunto de datos. Un proceso es una entidad dinámica que ejecuta un
programa sobre un conjunto de datos utilizando los recursos del sistema operativo. Dos o mas
procesos podrían estar ejecutando el mismo programa, empleando sus propios datos y recursos,
por ejemplo, si dos o más usuarios están usando simultáneamente el mismo editor de texto. El
programa es el mismo, pero cada usuario tiene un proceso distinto (y con distintos datos).

Por analogía al preparar una receta de una torta. El programa es la receta, el proceso es la
actividad que consiste en leer la receta, mezclar los ingredientes y hornear la torta.

2.1.1. Creación de Procesos


Los sistemas operativos necesitan asegurar de alguna forma que puedan existir todos los
procesos requeridos, entonces es necesaria alguna manera de poder crear y destruir los
procesos según sea necesario durante la operación del sistema. Los cuatro principales sucesos
que provocan la creación de nuevos procesos son:
• La inicialización del sistema
• La ejecución por parte de un proceso (en ejecución) de una llamada al sistema de creación
de un nuevo proceso.
• La petición por parte del usuario de la creación de un nuevo proceso.
• El inicio de un trabajo en batch (lotes).

Cuando un sistema operativo arranca, se crean típicamente varios procesos. Algunos de esos
procesos son procesos foreground (en primer plano), esto es, procesos que interactúan con los
usuarios y realizan trabajo para ellos. Otros son procesos background (en segundo plano), que
no están asociados con usuarios particulares, sino que tienen alguna función específica. Los
procesos que se ejecutan como procesos background para llevar a cabo alguna actividad tal
como el correo electrónico, las páginas web, las news o la impresión de ficheros de salida, etc,
se denominan demonios en Unix o servicios en Windows. Los sistemas grandes tienen
comúnmente docenas de ellos. En UNIX, el programa ps puede utilizarse para listar los procesos
que están en marcha. En Windows se utiliza el administrador de tareas.

Ing. Jorge Orellana A. 13


Sistemas Operativos

Adicionalmente a los procesos creados en el momento del arranque, también pueden crearse
nuevos procesos después. A menudo un proceso en ejecución puede hacer llamadas al sistema
para crear uno o más procesos nuevos para que le ayuden en su trabajo.
En sistemas interactivos los usuarios pueden arrancar un programa tecleando un comando o
haciendo clic (dos veces) con el ratón sobre un icono. Realizando cualquiera de esas acciones
se consigue que comience un nuevo proceso y se ejecute el programa correspondiente. En
sistemas UNIX basados en comandos y ejecutando X-Windows, el nuevo proceso creado se
ejecuta sobre la ventana en la cual se le activó. En Windows, cuando un proceso comienza no
tiene ninguna ventana asignada, aunque puede crear una o más. En ambos sistemas, los
usuarios pueden tener múltiples ventanas abiertas a la vez, cada una de ellas ejecutando algún
proceso. Utilizando el ratón, el usuario puede seleccionar una ventana e interactuar con el
proceso, por ejemplo, proporcionando datos de entrada cuando sean necesarios.
La última situación que provoca la creación de procesos se aplica sólo a los sistemas en batch
que podemos encontrar en los grandes mainframes. En esos sistemas los usuarios pueden
lanzar (submit) al sistema trabajos en batch (posiblemente de forma remota). Cuando el sistema
operativo detecta que dispone de todos los recursos necesarios para poder ejecutar otro trabajo,
crea un nuevo proceso y ejecuta sobre él el siguiente trabajo que haya en la cola de entrada.

En UNIX sólo existe una llamada al sistema para crear un nuevo proceso: fork. Esta llamada
crea un clon (una copia exacta) del proceso que hizo la llamada. Después del fork, los dos
procesos, el padre y el hijo, tienen la misma imagen de memoria, las mismas variables de
entorno y los mismos ficheros abiertos. Eso es todo lo que hay. Usualmente, a continuación el
proceso hijo ejecuta execve o una llamada al sistema similar para cambiar su imagen de
memoria y pasar a ejecutar un nuevo programa. En Windows, una única llamada al sistema de
Win32, CreateProcess, realiza tanto la creación del proceso como la carga del programa
correcto dentro del nuevo proceso. Tanto en UNIX como en Windows, después de crear un
proceso, tanto el padre como el hijo cuentan con sus propios espacios de direcciones disjuntos.

2.1.2. Terminación de Procesos


Tras la creación de un proceso comienza su ejecución realizando el trabajo que se le ha
encomendado. Sin embargo pronto o tarde el nuevo proceso debe terminar, usualmente debido
a una de las siguientes causas:
• El proceso completa su trabajo y termina (voluntariamente).
• El proceso detecta un error y termina (voluntariamente).
• El sistema detecta un error fatal del proceso y fuerza su terminación.
• Otro proceso fuerza la terminación del proceso (por ejemplo en UNIX mediante la llamada al
sistema kill).
La mayoría de los procesos terminan debido a que han completado su trabajo. Cuando un
compilador ha compilado el programa que se le ha dado, el compilador ejecuta una llamada al
sistema para decirle al sistema operativo que ha finalizado. Esta llamada es exit en UNIX y
ExitProcess en Windows. Los programas orientados a la pantalla soportan también la
terminación voluntaria. Los procesadores de texto, navegadores y programas similares cuentan
siempre con un icono o una opción de menú para que el usuario pueda elegir con el ratón
indicándole al proceso que borre cualquier archivo temporal que esté abierto y a continuación
termine.
La segunda causa de terminación es que el proceso descubra un error fatal. Por ejemplo, si un
usuario teclea un comando incorrecto. La tercera causa de terminación es la aparición de un
error causado por el proceso, a menudo debido a un error de programación. Algunos ejemplos
son: la ejecución de una instrucción ilegal, una referencia a una posición de memoria inexistente,
o una división por cero. La cuarta razón por la cual un proceso puede terminar, es que un
proceso ejecute una llamada al sistema diciéndole al sistema operativo que mate a algún otro
proceso. En UNIX esta llamada al sistema es kill. La función Win32 correspondiente es
TerminateProcess. En ambos casos el proceso asesino debe contar con la debida autorización.

Ing. Jorge Orellana A. 14


Sistemas Operativos

2.1.3. Estado de los procesos


Durante su existencia un proceso pasa por una serie de estados discretos, siendo varias las
circunstancias que pueden hacer que el mismo cambie de estado:

• Nuevo (new).
• Ejecutándose (running). El proceso está siendo ejecutado en la CPU. Por lo tanto a lo
más un proceso puede estar en este estado en un computador uniprocesador.
• Listo para ejecutar (ready). El proceso está en condiciones de ejecutarse, pero debe
esperar su turno de CPU.
• Bloqueado o En espera (waiting) El proceso no está en condiciones de ejecutarse.
Está esperando que algún evento ocurra, como la finalización de una operación de E/S.
También se dice que está suspendido o en espera.
• Terminado (terminated)

Se puede establecer una cola de Listos para los procesos “listos” y una cola de Bloqueados para
los “bloqueados”. La cola de Listos se mantiene en orden prioritario y la cola de Bloqueados está
desordenada, ya que los procesos se desbloquean en el orden en que tienen lugar los eventos
que están esperando.
Al admitirse un trabajo en el sistema se crea un proceso equivalente y es insertado en la última
parte de la cola de Listos.
La asignación de la CPU al primer proceso de la cola de Listos se denomina “Despacho”, que es
ejecutado por una entidad del Sistema Operativo llamada “Despachador” (dispatcher).
El “Bloqueo” es la única transición de estado iniciada por el propio proceso del usuario, puesto
que las otras transiciones son iniciadas por entidades ajenas al proceso.

2.1.4. Implementación de procesos


El sistema operativo mantiene para cada proceso un bloque de control de proceso o process
control block (PCB), donde se guarda para cada proceso la información necesaria para
reanudarlo si es suspendido, y otros datos.
• Estado: Puede ser nuevo, listo, en ejecución, en espera, detenido, etcétera
• Contador de programa: El contador indica la dirección de la siguiente instrucción que se
ejecutará para este proceso.
• Registros de CPU: El número y el tipo de los registros varía dependiendo de la arquitectura
del computador. Los registros incluyen acumuladores, registros índice, punteros de pila y
registros de propósito general, así como cualquier información de códigos de condición que
haya. Junto con el contador de programa, esta información de estado se debe guardar
cuando ocurre una interrupción, para que el proceso pueda continuar correctamente
después.
• Información para planificación: Esta información incluye una prioridad del proceso, punteros
a colas de planificación y cualquier otro parámetro de planificación que haya.
• Información para administración de memoria: Esta información puede incluir datos tales
como el valor de los registros de base y límite, las tablas de páginas o las tablas de
segmentos, dependiendo del sistema de memoria empleado por el sistema operativo.

Ing. Jorge Orellana A. 15


Sistemas Operativos

• Información de estado de E/S: La información incluye la lista de dispositivos de E/S


asignadas a este proceso, una lista de archivos abiertos, etcétera.
• Estadísticas y otros: tiempo real y tiempo de CPU usado, identificador del proceso,
identificador del dueño, tiempo real consumido, límites de tiempo, números de cuenta,
números de trabajo o proceso, y demás.

Cuando el Sistema Operativo cambia la atención de la CPU entre los procesos, utiliza las áreas
de preservación del PCB para mantener la información que necesita para reiniciar el proceso
cuando consiga de nuevo la CPU.

2.1.5. Cambios de contexto (context switch)


Cuando el sistema operativo entrega la CPU a un nuevo proceso, debe guardar el estado del
proceso que estaba ejecutando, y cargar el estado del nuevo proceso. El estado de un proceso
comprende el PC (Contador del programa), y los registros de la CPU. Además, si se usan las
técnicas de administración de memoria que veremos más adelante, hay más información
involucrada. Este cambio, que demora de unos pocos a mil microsegundos, dependiendo del
procesador, es puro overhead o sobrecosto, puesto que entretanto la CPU no hace trabajo útil
(ningún proceso avanza). Considerando que la CPU hace varios cambios de contexto en un
segundo, su costo es relativamente alto.
Algunos procesadores tienen instrucciones especiales para guardar todos los registros de una
vez. Otros tienen varios conjuntos de registros, de manera que un cambio de contexto se hace
simplemente cambiando el puntero al conjunto actual de registros. El problema es que si hay
más procesos que conjuntos de registros, igual hay que apoyarse en la memoria. Por ser un
cuello de botella tan importante para el desempeño del sistema operativo se emplean estructuras
nuevas (hilos) para evitarla hasta donde sea posible.

2.1.6. Jerarquia de procesos


La secuencia de creación de procesos genera un árbol de procesos. Para referirse a las
relaciones entre los procesos de la jerarquía se emplean los términos de padre, hermano o
abuelo. Cuando el proceso A solicita al sistema operativo que cree el proceso B, se dice que A

Ing. Jorge Orellana A. 16


Sistemas Operativos

es padre de B y que B es hijo de A. Bajo esta óptica, la jerarquía de procesos puede


considerarse como un árbol genealógico. Algunos sistemas operativos, como Unix, mantienen de
forma explícita esta estructura jerárquica de procesos (un proceso sabe quién es su padre),
mientras que otros sistemas operativos como el Windows NT no la mantienen.

Por ejemplo, en Unix, cuando se carga el sistema operativo, se inicializa un proceso init. Este
proceso lee un archivo que dice cuántos terminales hay, y crea un proceso login para cada
terminal, que se encarga de solicitar nombre y contraseña. Cuando un usuario entra, login
determina qué shell le corresponde al usuario, y crea otro proceso para ejecutar esa shell. A su
vez, la shell crea más procesos según los comandos que ejecute el usuario, generándose así
todo un árbol de procesos: cada proceso tiene cero o más hijos, y exactamente un padre (salvo
init, que no tiene padre). Haciendo una analogia en Windows, aun cuando este sistema operativo
no mantiene esta jerarquia y todos son iguales, unos de los primeros procesos seria System,
luego Winlogon para crear una sesion y sobre ella crea su shell que es el Explorer, en el cual
se ejecutaran todos los demas programas como hijos de este proceso.

2.2. PROCESOS LIVIANOS, HILOS, HEBRAS O THREADS


En los sistemas operativos tradicionales, cada proceso tiene su propio espacio de direcciones y
un único flujo (hilo) de control. Sin embargo, frecuentemente hay situaciones en las que es
deseable contar con múltiples hilos de control (threads) en el mismo espacio de direcciones
ejecutándose quasi-paralelamente, como si fueran procesos separados (excepto que comparten
el mismo espacio de direcciones).

Una hebra (thread, hilo o proceso liviano) es un hilo de control dentro de un proceso. Un proceso
tradicional tiene sólo una hebra de control. Si usamos threads, entonces podemos tener varias
hebras dentro de un proceso. Cada hebra representa una actividad o unidad de computación
dentro del proceso, es decir, tiene su propio PC, conjunto de registros y stack, pero comparte
con las demás hebras el espacio de direccionamiento y los recursos asignados, como archivos
abiertos y otros.

En muchos aspectos los procesos livianos son similares a los procesos pesados, comparten el
tiempo de CPU, y a lo más un thread está activo (ejecutando) a la vez, en un monoprocesador.
Los otros pueden estar listos o bloqueados. Pero los procesos pesados son independientes, y el
sistema operativo debe proteger a unos de otros, lo que acarrea algunos costos. Los procesos
livianos dentro de un mismo proceso pesado no son independientes, cualquiera puede acceder a

Ing. Jorge Orellana A. 17


Sistemas Operativos

toda la memoria correspondiente al proceso pesado. En ese sentido, no hay protección entre
threads, nada impide que un thread pueda escribir, por ejemplo, sobre el stack de otro.

2.2.1. Comparando hebras con procesos


• El cambio de contexto entre hebras de un mismo proceso es mucho más barato que el
cambio de contexto entre procesos; gracias a que comparten el espacio de
direccionamiento, un cambio de contexto entre threads no incluye los registros y tablas
asociados a la administración de memoria.
• La creación de threads también es mucho más barata, ya que la información que se requiere
para mantener un thread es mucho menos que un PCB. La mayor parte de la información del
PCB se comparte entre todos los threads del proceso.
• El espacio de direccionamiento compartido facilita la comunicación entre las hebras y el
compartimiento de recursos.

2.2.2. Implementación de hebras


Hay librerías de hebras que permiten implementar hebras dentro de procesos normales o
pesados sin apoyo del sistema operativo. La planificación y manejo de las hebras se hace dentro
del proceso. El sistema operativo no tiene idea de las hebras; sólo ve y maneja un proceso como
cualquier otro.
La alternativa es que el propio sistema operativo provea servicios de hebras; así como se
pueden crear procesos, se pueden también crear nuevas hebras dentro de un proceso, y éstos
son administrados por el sistema operativo.
La ventaja del primer esquema respecto del segundo, es que los cambios de contexto entre
hebras de un mismo proceso son extremadamente rápidos, porque ni siquiera hay una llamada
al sistema de por medio. La desventaja es que si una de las hebras hace una llamada
bloqueante (Ej., solicita E/S), el sistema operativo bloquea todo el proceso, aún cuando haya
otras hebras listos para ejecutar.

En el caso de sistemas operativos de servidores, cada hebra puede manejar una solicitud. El
diseño es más sencillo y este esquema mejora la productividad, porque mientras unos hilos
esperan, otros pueden hacer trabajo útil. Por ejemplo servidor de archivos, que recibe solicitudes
para leer y escribir archivos en un disco. Para mejorar el rendimiento, el servidor mantiene un

Ing. Jorge Orellana A. 18


Sistemas Operativos

caché con los datos más recientemente usados, en la eventualidad que reciba algún
requerimiento para leer esos datos, no es necesario acceder el disco.
Cuando se recibe una solicitud, se le asigna a un thread para que la atienda. Si ese thread se
bloquea porque tuvo que acceder el disco, otros threads pueden seguir atendiendo otras
solicitudes. La clave es que el buffer debe ser compartido por todos los threads, lo que no podría
hacerse si se usa un proceso pesado para cada solicitud. POSIX (Linux) especifica una serie de
políticas de planificación, aplicables a procesos y procesos ligeros, que debe implementar
cualquier sistema operativo que ofrezca esta interfaz. En Windows la unidad básica de ejecución
es el proceso ligero y, por tanto, la planificación se realiza sobre este tipo de procesos.

2.3. PLANIFICACIÓN (SCHEDULING) DE PROCESOS


En un ambiente de multiprogramación es frecuente que en un momento dado haya múltiples
procesos compitiendo por el uso de la CPU al mismo tiempo. Esta situación se da siempre que
dos o más procesos están simultánemente en el estado preparado. Si sólo hay una CPU
disponible, es necesario hacer una elección para determinar cual de esos procesos será el
siguiente que se ejecute. La parte del sistema operativo que realiza esa elección se denomina el
planificador (scheduler), y el algoritmo que se utiliza para esa elección se denomina el algoritmo
de planificación.
Existen diferentes algoritmos de planificación que deben cumplir con criterios basicos, como:

• Maximizar el uso de la CPU (que se mantenga ocupada el mayor tiempo posible).


• Maximizar la Productividad (throughput), considerando que productividad (o rendimiento) es
el número de procesos que se ejecutan por unidad de tiempo.
• Minimizar el Tiempo de retorno (turn around time), que es el tiempo transcurrido desde que
el proceso ingresa hasta que termina, sumando espera para entrar en memoria, en cola de
procesos listos, ejecutándose en CPU y haciendo E/S.
• Minimizar el Tiempo de espera (waiting time), que es el tiempo que el proceso está en la cola
de listos.
• Minimizar el Tiempo de respuesta (response time), que es el tiempo que transcurre desde
que se presenta una solicitud hasta que se tiene respuesta. Es el tiempo que tarda en
comenzar a responder, no incluyendo el tiempo de la respuesta en sí.
• La equidad (Justicia), que todos los procesos tienen que ser tratados de igual forma y a
todos se les debe dar la oportunidad de ejecutarse.
• Recursos equilibrados, que la política de planificación que se elija debe mantener ocupados
los recursos del sistema, favoreciendo a aquellos procesos que no abusen de los recursos
asignados, sobrecargando el sistema y bajando la performance general.

En un sistema operativo los recursos compartidos exigen la organización en colas de las


solicitudes pendientes. Tenemos colas para la planificación del uso de la CPU como también
colas para el uso de dispositivos (device queues), que son utilizadas para ordenar los
requerimientos que varios procesos pueden tener sobre un dispositivo específico.
Al ser creados, los procesos se colocan en la cola de trabajos, formada por los procesos que aún
no residen en la memoria principal, pero listos para su ejecución. La residencia en memoria es
imprescindible para la ejecución de un proceso. La cola de trabajos listos (ready queue) es
aquélla formada por los procesos que están listos para la ejecución, en memoria principal. Son
los procesos que compiten directamente por CPU.
Los elementos de cualquiera de estas colas no son los procesos en sí, sino sus PCB’s, que
están en memoria. La cola de procesos listos (ready queue) se almacena como una lista
enlazada donde la cabecera (header) de la ready queue tiene punteros al primer y al último PCB
de los procesos de la lista. Cada PCB apunta al próximo en la lista.

2.3.1. Procesos orientados a la E/S y procesos orientados a la CPU


En un sistema hay procesos con mayor proporción de ráfagas E/S (I/O bound process) y otros
con mayor necesidad de CPU que de E/S (CPU bound process). Si cuando se seleccionan
procesos para ejecutar se eligieran todos I/O bound, tendríamos las colas de dispositivo llenas

Ing. Jorge Orellana A. 19


Sistemas Operativos

de solicitudes y, probablemente, la CPU ociosa. Si se eligen muchos procesos limitados por CPU
la cola de listos estará llena y las colas de dispositivo, vacías. Por eso es muy importante de qué
manera se realiza la selección de trabajos para mantener el sistema en equilibrio. Esta selección
la realizan los planificadores o schedulers.

El planificador (scheduler) forma parte del núcleo del sistema operativo. Entra en ejecución cada
vez que se activa el sistema operativo y su misión es seleccionar el proceso que se ha de
ejecutar a continuación.

El activador (dispatcher) también forma parte del sistema operativo y su función es poner en
ejecución el proceso seleccionado por el planificador. Debe ser muy rápido pues una de sus
funciones es encargarse del cambio de contexto (context switch). Al tiempo entre detener un
proceso y comenzar a correr otro se le llama dispatch latency.
El dispatcher es también el que se encarga de pasar a modo usuario el proceso que esta
activando y “saltar” a la dirección de la instrucción que comienza la ejecución del programa.

2.3.2. Temporizador de Intervalos o Reloj de Interrupción


El proceso al cual está asignada la CPU se dice que está en ejecución y puede ser un proceso
de Sistema Operativo o de usuario.
El Sistema Operativo dispone de mecanismos para quitarle la CPU a un proceso de usuario para
evitar que monopolice el sistema. El Sistema Operativo posee un “reloj de interrupción” o
“temporizador de intervalos” para generar una interrupción, en algún tiempo futuro específico o
después de un transcurso de tiempo en el futuro; la CPU es entonces despachada hacia el
siguiente proceso. En cada interrupción del reloj el Sistema Operativo decide si el proceso que
se está ejecutando continúa o si el proceso agotó su tiempo de CPU y debe suspenderse y ceder
la CPU a otro proceso.

Un proceso retiene el control de la CPU hasta que ocurra alguna de las siguientes situaciones:
• La libera voluntariamente.
• El reloj la interrumpe.
• Alguna otra interrupción atrae la atención de la CPU.

El reloj de interrupción ayuda a garantizar tiempos de respuesta razonables a usuarios


interactivos, ya que evita que el sistema se “cuelgue” a un solo usuario en un ciclo infinito y
permite que los procesos respondan a “eventos dependientes del tiempo”.

2.3.3. Niveles de Planificación del Procesador


Se consideran tres niveles importantes de planificación:

• Planificación de alto nivel (largo plazo o long term). Determina a qué trabajos se les va a
permitir competir activamente por los recursos del sistema, lo cual se denomina Planificación
de admisión.

Ing. Jorge Orellana A. 20


Sistemas Operativos

• Planificación de nivel intermedio (mediano plazo o medium term). Determina a qué procesos
se les puede permitir competir por la CPU. Responde a fluctuaciones a corto plazo en la
carga del sistema y efectúa “suspensiones” y “activaciones” (reanudaciones) de procesos.
Debe ayudar a alcanzar ciertas metas en el rendimiento total del sistema.
• Planificación de bajo nivel (corto plazo o short term). Determina a qué proceso listo se le
asigna la CPU cuando esta queda disponible y asigna la CPU al mismo, es decir que
“despacha” la CPU al proceso. La efectúa el Despachador del Sistema Operativo.

Los distintos Sistemas Operativos utilizan varias Políticas de Planificación, que se instrumentan
mediante Mecanismos de Planificación.

2.4. ALGORITMOS DE PLANIFICACIÓN


En entornos diferentes se necesitan algoritmos de planificación diferentes. Esto se debe a que
cada área de aplicación (y cada tipo de sistema operativo) tiene objetivos diferentes. En otras
palabras, lo que el planificador debe optimizar no es lo mismo en todos los sistemas. Es
necesario distinguir aquí tres entornos:

• Batch (lotes)
• Interactivo
• Tiempo Real.

En los sistemas en batch, no existen usuarios que estén esperando impacientemente por una
rápida respuesta ante sus terminales. En consecuencia, son aceptables los algoritmos no
expulsores, o los algoritmos expulsores con largos periodos de tiempo para cada proceso. Con
este enfoque se reduce el número de cambios de proceso, mejorando por tanto el rendimiento.
En un entorno con usuarios interactivos es indispensable que haya expulsiones para impedir que
un proceso acapare la CPU, negando cualquier servicio de la CPU a los demás. Incluso aunque
ningún proceso tenga intención de ejecutarse eternamente, es posible que debido a un error en
el programa un proceso mantenga parados a todos los demás indefinidamente. La expulsión es
necesaria para impedir ese comportamiento.
En los sistemas con restricciones de tiempo real, por extraño que parezca, la expulsión es
algunas veces innecesaria debido a que los procesos saben que no pueden ejecutarse durante
largos periodos de tiempo y usualmente hacen su trabajo y rápidamente se bloquean. La
diferencia con los sistemas interactivos es que los sistemas en tiempo real sólo ejecutan
programas pensados como parte de una misma aplicación. Los sistemas interactivos por el
contrario son sistemas de propósito general y pueden ejecutar programas arbitrarios no
cooperantes o incluso maliciosos.

2.4.1. Planificación en Sistemas en Batch

2.4.1.1. FCFS (first-come, first-served) Primero en llegar, primero en ser atendido.

Ing. Jorge Orellana A. 21


Sistemas Operativos

Se le asigna la CPU al proceso que la requirió primero. Se maneja a través de una cola FIFO
(First In First Out), y cuando un proceso requiere CPU, su PCB se coloca la final de la cola.
Cuando se debe elegir un proceso para asignarle CPU se elige el proceso cuya PCB esta
primera en la cola.

Es un algoritmo que no usa expropiación, y atiende a los procesos por estricto orden de llegada
a la cola de listos (READY). Cada proceso se ejecuta hasta que termina, o hasta que hace una
llamada bloqueante (de E/S), o sea, ejecuta su fase de CPU completa. El código para
implementar este algoritmo es simple y comprensible. Pero el tiempo promedio de espera puede
ser largo.

Considerando que los procesos P1, P2 y P3 están LISTOS para ejecutar su siguiente fase de
CPU, cuya duración será de 24, 3 y 3 milisegundos, respectivamente. Si ejecutan en el orden P1,
P2, P3, entonces los tiempos de espera son: 0 para P1, 24 para P2 y 27 para P3, o sea, en
promedio, 17 ms. Pero si se ejecutan en orden P2, P3, P1, entonces el promedio es sólo 3 ms.
En consecuencia, FCFS no asegura para nada que los tiempos de espera sean los mínimos
posibles; peor aún, con un poco de mala suerte pueden llegar a ser los máximos posibles.

Suponiendo que se tiene un proceso intensivo en CPU (CPU bound) y varios procesos intensivos
en E/S (I/O bound). Entonces podría pasar lo siguiente: El proceso intensivo en CPU toma la
CPU por un período largo, suficiente como para que todas las operaciones de E/S pendientes se
completen. En esa situación, todos los procesos están LISTOS, y los dispositivos desocupados.
En algún momento, el proceso intensivo en CPU va a solicitar E/S y va a liberar la CPU.
Entonces van a ejecutar los otros procesos, pero como son intensivos en E/S, van a liberar la
CPU muy rápidamente y se va a invertir la situación: todos los procesos van a estar
BLOQUEADOS, y la CPU desocupada. Este fenómeno se conoce como efecto convoy, y se
traduce en una baja utilización tanto de la CPU como de los dispositivos de E/S. Obviamente, el
rendimiento mejora si se mantienen ocupados la CPU y los dispositivos (o sea, conviene que no
haya colas vacías).

2.4.1.2. SJF (Shortest-job-first Scheduling). Primero el trabajo más corto.


Este algoritmo elige entre los procesos de la cola de listos, aquel que tenga la próxima ráfaga de
CPU mas corta. Para ello este dato debe ser conocido. Es un algoritmo que permite que el
tiempo de espera promedio sea bajo. Se puede utilizar en planificadores de largo plazo, pero no
en los de corto plazo pues no hay manera de conocer la medida de la próxima ráfaga. Se podría
aproximar considerando el valor de la previa.

Suponiendo que se tiene tres procesos cuyas próximas fases de CPU son de a, b y c
milisegundos de duración. Si ejecutan en ese orden, el tiempo medio de espera es:

(0 + a + (a + b))/3 = (2a+b)/3

El primer proceso que se ejecute es el que tiene mayor incidencia en el tiempo medio, y el
último, tiene incidencia nula. En conclusión, el tiempo medio se minimiza si se ejecuta siempre el
proceso con la menor próxima fase de CPU que esté LISTO. Además, es una buena manera de
prevenir el efecto convoy. Lo malo es que para que esto funcione, hay que adivinar el futuro,
pues se requiere conocer la duración de la próxima fase de CPU de cada proceso.
Lo que se hace es predecir la próxima fase de CPU en base al comportamiento pasado del
proceso, usando un promedio exponencial. Suponiendo que la predicción para la n-ésima fase
es Tn, entonces, se actualiza el estimador para predecir Tn+1

Ing. Jorge Orellana A. 22


Sistemas Operativos

Tn+1= (1-alpha) Tn + alpha Tn

El parámetro alpha, entre 0 y 1, controla el peso relativo de la última fase en relación a la historia
pasada.
j n+1
Tn+1 = (1-alpha) Tn + alpha(1-alpha) Tn-1 + ... + alpha (1-alpha) Tn-j + ... + alpha T0

Mientras más antigua la fase menos incidencia tiene en el estimador. Un valor atractivo para
alpha es 1/2, ya que en ese caso sólo hay que sumar los valores y dividir por dos.

2.4.2. Planificación en Sistemas Interactivos

2.4.2.1. Round-Robin Scheduling (RR). Planificación por turno circular o Carrousel.


Se la asigna a cada proceso de la cola de listos un intervalo de tiempo de CPU. Ese intervalo es
llamado time slice o quantum. Para implementar este algoritmo la cola de listos se mantiene
como una cola FIFO, y la CPU se va asignando dándole un máximo de un quantum a cada
proceso.
Si el proceso no va a usar todo ese tiempo, usa lo necesario y libera la CPU. Se elige entonces
otro proceso de la cola. Si excede el quantum, se produce una interrupción.
En ambos casos al dejar la CPU hay un cambio de contexto. El tiempo promedio de espera
puede ser largo. Su performance depende fuertemente de la elección del quantum. Y el tiempo
gastado en el cambio de contexto es una variable que se debe tener muy en cuenta. Se debe
determinar un quantum que sea mucho mayor que el tiempo de cambio de contexto. Se utiliza en
los sistemas de tiempo compartido (time-sharing). El punto interesante es encontrar el quantum
adecuado. Si es muy grande, se degenera en FCFS, pero tampoco puede ser demasiado
pequeño, porque entonces el costo en cambios de contexto es preponderante.

Por ejemplo, si un cambio de contexto toma 5 ms, y se fija el quantum en 20 ms, entonces 20%
del tiempo de la CPU se perderá en sobrecosto. Un valor típico es 100 ms. Una regla que suele
usarse es que el 80% de las fases de CPU deben ser de menor duración que un quantum. Con
respecto a FCFS, se mejora el tiempo de respuesta y la utilización de la CPU, ya que se
mantienen más balanceadas las colas listos (READY) y bloqueadas (BLOCKED). Pero RR
tampoco asegura que los tiempos de espera sean los mínimos posibles. Usando el mismo
ejemplo anterior, y considerando un quantum de 4ms, pero sin considerar costos de cambio de
contexto, si el orden es P1, P2, P3 entonces el tiempo medio de espera es 5.66ms (P1 espera
6ms, P2 espera 4ms. y P3 espera 7ms.)

2.4.2.2. Planificación por prioridad


Se le asocia un valor a cada proceso que representa la prioridad, y se le asigna la CPU al
proceso de la cola de listos (ready) que tenga la mayor prioridad.
Los valores asociados a la prioridad son un rango fijo de 0 a N, según cada sistema. También lo
determina cada sistema si el número mas bajo es la más alta o la más baja prioridad.
La prioridad puede ser fija o variable, externa o interna. Si es fija, ese valor no varia en el ciclo de
vida del proceso. Si es variable, significa que ese valor puede cambiar dinámicamente de
manera tal que haya factores, por ejemplo, el tiempo que lleva esperando en colas, que puedan
ayudar a que haya un mejor nivel de competencia elevando la prioridad de procesos postergados

Ing. Jorge Orellana A. 23


Sistemas Operativos

y evitar una situación indeseable llamada starvation (inanición). A la técnica de elevar la prioridad
a un proceso de acuerdo al tiempo que hace que esta en el sistema se le llama envejecimiento
(aging).
Si la prioridad es interna, es determinada en función del uso de los recursos (memoria, archivos
abiertos, tiempos). Si es externa, puede decidirse darle alta prioridad a un proceso de
importancia, a consideración del operador. POSIX (Linux) y Win32 (Windows) proporcionan
planificación basada en prioridades.

Hay muchos criterios para definir la prioridad, por ejemplo:


• Según categoría del usuario.
• Según tipo de proceso: sistema, interactivo, o por lotes; o bien, intensivo en CPU o
intensivo en E/S.
• Según cuanto hayan ocupado la CPU hasta el momento
• Para evitar que un proceso de baja prioridad sea postergado en demasía, aumentar
prioridad mientras más tiempo lleve esperando: envejecimiento (aging).
• Para evitar que un proceso de alta prioridad ejecute por demasiado tiempo, se le puede
ir bajando la prioridad.

2.4.2.3. Colas multinivel (Múltiples colas)


En un sistema conviven procesos mixtos. Puede haber requerimientos de tiempo de respuesta
distintos si conviven procesos interactivos con procesos batch (o para aquellos que provienen del
Unix, procesos en foreground y procesos en background).
Para complicar más la cosa, se puede agrupar los procesos en distintas clases, y usar distintos
algoritmos de planificación intra-clase, más algún algoritmo inter-clases. Por ejemplo, los
procesos interactivos y los procesos por lotes tienen distintos requerimientos en cuanto a
tiempos de respuesta. Entonces, se puede planificar los procesos interactivos usando RR, y los
procesos por lotes según FCFS, teniendo los primeros prioridad absoluta sobre los segundos.

Existen algoritmos que contemplan esta situación dividiendo la cola de listos (ready) en distintas
colas según el tipo de proceso, estableciendo una competencia entre las colas y entre los
procesos del mismo tipo entre si. Por ejemplo, se puede tener una cola para
• Procesos de sistema.
• Procesos interactivos.
• Procesos de los alumnos.
• Procesos por lotes.

Cada cola usa su propio algoritmo de planificación, pero se necesita un algoritmo de planificación
entre las colas. Una posibilidad es prioridad absoluta con expropiación. Otra posibilidad: asignar
tajadas de CPU a las colas. Por ejemplo, a la cola del sistema se le puede dar el 60% de la CPU
para que haga RR, a la de procesos por lotes el 5% para que asigne a sus procesos según
FCFS, y a las otras el resto.

Ing. Jorge Orellana A. 24


Sistemas Operativos

Por otra parte, se podría hacer que los procesos migren de una cola a otra. Por ejemplo: varias
colas planificadas con RR, de prioridad decreciente y quantum creciente. La última se planifica
con FCFS. Un proceso en la cola i que no termina su fase de CPU dentro del quantum asignado,
se pasa al final de la siguiente cola de menor prioridad, pero con mayor quantum. Un proceso en
la cola i que sí termina su fase de CPU dentro del quantum asignado, se pasa al final de la
siguiente cola de mayor prioridad, pero con menor quantum. Ejemplo:

Cola 0: quantum=10 ms, 40% de CPU.


Cola 1: quantum=20 ms, 30% de CPU.
Cola 2: quantum=35 ms, 20% de CPU.
Cola 3: FCFS, 10% de CPU.

En este modelo con retroalimentación, un proceso puede “moverse” entre colas, es decir, según
convenga puede llegar a asignarse a otra cola. Los procesos se separan de acuerdo a la ráfaga
de CPU que usan. Si esta usando mucha CPU se lo baja a una cola de menor prioridad. Se trata
de mantener los procesos interactivos y de mucha E/S en las colas de mayor prioridad.
Si además un proceso estuvo demasiado tiempo en una cola de baja prioridad puede moverse a
una cola de mayor prioridad para prevenir inanición (starvation).

2.4.3. Planificación en Sistemas de Tiempo Real


Un sistema de tiempo real es uno en el cual el tiempo juega un papel esencial. Típicamente, se
tiene uno o más dispositivos físicos externos al ordenador que generan estímulos a los cuales
debe reaccionar el ordenador de la manera apropiada y dentro de un plazo de tiempo prefijado.
Por ejemplo, el computador interno de un reproductor de discos compactos recibe los bits tal y
como salen de la unidad y debe convertirlos en música en un intervalo de tiempo muy ajustado.
Si el cálculo tarda demasiado, la música sonará rara. Otros sistemas en tiempo real monitorizan
pacientes en la unidad de cuidados intensivos de un hospital, controlan el piloto automático de

Ing. Jorge Orellana A. 25


Sistemas Operativos

un avión y controlan los robots en una fábrica automatizada. En todos estos casos, producir la
respuesta correcta demasiado tarde es a menudo tan malo como no producir ninguna respuesta.
Los sistemas en tiempo real se clasifican generalmente en sistemas de tiempo real estricto (hard
real time) y sistemas de tiempo real moderado (soft real time). En los sistemas de tiempo real
estricto hay plazos absolutos que deben cumplirse, pase lo que pase. En los sistemas de tiempo
real moderado el incumplimiento ocasional de un plazo aunque es indeseable, es sin embargo
tolerable. En ambos casos, el comportamiento en tiempo real se logra dividiendo el programa en
varios procesos cuyo comportamiento es predecible y conocido por adelantado. Generalmente,
tales procesos son cortos y pueden terminar su trabajo en mucho menos de un segundo.
Cuando se detecta un suceso externo, el planificador debe planificar los procesos de tal modo
que se cumplan todos los plazos.
Los algoritmos de planificación para tiempo real pueden ser estáticos o dinámicos. Los primeros
toman sus decisiones de planificación antes de que el sistema comience a ejecutarse. Los
segundos toman las decisiones en tiempo de ejecución. La planificación estática sólo funciona si
se está perfectamente informado por anticipado sobre el trabajo que debe realizarse y los plazos
que deben cumplirse. Los algoritmos de planificación dinámica no tienen estas restricciones.

2.4.4. Planificación apropiativa, expropiativa o expulsiva (Preemptive Scheduling)


Hay que considerar en el esquema de planificación en que momento se realiza la selección. Un
algoritmo no apropiativo es aquel que una vez que le da la CPU a un proceso se ejecuta hasta
que termina o hasta que se bloquea hasta la ocurrencia de determinado evento. En cambio, un
algoritmo apropiativo es aquel en que un proceso que se esta ejecutando puede ser interrumpido
y pasado a cola de listos (ready queue) por decisión del sistema operativo. Esta decisión puede
ser porque llego un proceso de mayor prioridad, por ejemplo. Si bien los algoritmos apropiativos
tienen un mayor costo que los no apropiativos, el sistema se ve beneficiado con un mejor
servicio pues se evita que algún proceso pueda apropiarse de la CPU.
No obstante si se utilizaran algoritmos apropiativos debe considerarse si el sistema operativo
esta preparado para desplazar en cualquier momento un proceso. Por ejemplo, si ese proceso
en ese momento, a causa de una llamada al sistema (system call) esta modificando estructuras
de kernel y se lo interrumpe, esas estructuras pueden quedar en un estado inconsistente. O
puede ocurrir con procesos que comparten datos y uno de ellos esta modificándolos al ser
interrumpido.

En Unix, por ejemplo se espera que se termine de ejecutar el system call para permitir
apropiación.

Relacionemos la idea de apropiación con los diferentes algoritmos vistos.


• FCFS es no apropiativo. Cuando se le da la CPU a un proceso, este la mantiene hasta
que decide liberarla, porque termino, o porque requiere I/O.
• SJF puede ser apropiativo o no. Si mientras se esta ejecutando un proceso llega uno
cuya próxima ráfaga de CPU es mas corta que lo que queda de ejecutar del activo,
puede existir la decisión de interrumpir el que se esta ejecutando o no. Al SJF
apropiativo, se le llama shortest-remaining-time-first. (primero el de tiempo restante más
corto).
• Prioridades puede ser apropiativo o no. Si mientras se esta ejecutando un proceso llega
a la cola de ready un proceso de mayor prioridad que el que se esta ejecutando puede
existir la decisión de interrumpir el que se esta ejecutando o no. Si es no apropiativo, en
lugar de darle la CPU al nuevo proceso, lo pondrá primero en la cola de ready.
• RR es apropiativo pues si el proceso excede el tiempo asignado, hay una interrupción
para asignarla la CPU a otro proceso.

Ing. Jorge Orellana A. 26


Sistemas Operativos

2.5. LA PLANIFICACIÓN DE PROCESOS EN AMBIENTES MULTIPROCESADOR

Cuando hay varias CPUs (y una memoria común), la planificación también se hace más
compleja. Se podría asignar una cola de listos (READY) a cada procesador, pero se corre el
riesgo de que la carga quede desbalanceada; algunos procesadores pueden llegar a tener una
cola muy larga de procesos para ejecutar, mientras otros están desocupados (con la cola vacía).
Para prevenir esta situación se usa una cola común de procesos listos, para lo cual hay dos
opciones:

• Cada procesador es responsable de su planificación, y saca procesos de la cola listos


(READY) para ejecutar. El problema es que hay ineficiencia por la necesaria sincronización
entre los procesadores para acceder la cola.
En el caso en que los procesadores son idénticos, cualquier procesador disponible puede
usarse para atender cualquier proceso en la cola. No obstante, debemos tener en cuenta
que puede haber dispositivos asignados de manera privada o única a un procesador, y si un
proceso quiere hacer uso de ese dispositivo deberá ejecutarse en ese procesador. Otra
ventaja es implementar load sharing (carga compartida), permitiendo que si un procesador
esta ocioso puedan pasarse procesos de la cola de otro procesador a este, o hacer una cola
común, y la atención se realiza de acuerdo a la actividad de cada uno.
• Dejar que sólo uno de los procesadores planifique y decida qué procesos deben correr los
demás: multiprocesamiento asimétrico. Se asume una estructura de procesadores
master-slave que asigna a un procesador la toma de decisiones en cuanto a scheduling y
otras actividades, dedicándose los otros procesadores a ejecutar código de usuario. La
ventaja es que solo el procesador master accede a estructuras del kernel, evitando
inconsistencias.

2.6. PLANIFICACIÓN EN WINDOWS (Win32) Y LINUX (POSIX)


El kernel de Windows está diseñado utilizando POO. Posee una capa de abstracción de
hardware (HAL), la cual es la única que se comunica directamente con el procesador; el resto del
kernel está diseñado para utilizar la interfaz de la HAL. La unidad mínima de ejecución no es el
proceso sino el hilo. Un hilo puede estar en alguno de estos seis estados: listo, standby
(siguiente a ejecutar), en ejecución, en espera, en transición (un nuevo hilo) y terminado.

Windows utiliza una planificación basada en colas múltiples de prioridades. Posee 32 niveles de
colas, clasificadas en clase de Tiempo Real fija (16-31) y prioridad dinamica (0-15). Las colas se
recorren de mayor a menor ejecutando los hilos asociados. Cada cola es manejada por medio de
un algoritmo de RR, aun así, si un hilo de mayor prioridad llega, el procesador le es asignado a
éste. La prioridades más altas son favorecidas. La prioridades de un thread no pueden ser
reducidas.

Los procesos en Linux pueden ser divididos en tres categorías, relacionadas con la prioridad:
interactivos, por lotes y de tiempo real. Los procesos TR son manejados bien por un algoritmo
FIFO o RR. Los demás procesos son despachados utilizando planificación RR con un sistema de
envejecimiento basado en créditos, donde el siguiente proceso a ejecutar es aquel que más
créditos posea. Los procesos TR son considerados prioritarios sobre cualquier otro proceso en el
sistema, por lo que serán ejecutados antes que los demás. Algunos aspectos de la estructura
interna del kernel que caben destacarse son:
• La PCB está representada por la estructura task_struct. Ésta indica el tipo de planificación
(FIFO,RR) por medio del campo policy, la prioridad (priority), el contador del programa
(counter), entre otros.
• La función goodness otorga una “calificación” al proceso pasado como parámetro. Dicha
puntuación oscila entre -1000 (no elegible) y +1000 (TR). Los procesos que comparten una
zona de memoria ganan una puntuación equivalente a su prioridad.
• El quantum varía según el proceso y su prioridad. La duración base es de aprox. 200ms.
• La función switch_to es la encargada de salvar la información de un proceso y cargar el
siguiente.

Ing. Jorge Orellana A. 27


Sistemas Operativos

• Las funciones sched_{get/set}scheduler se refieren al mecanismo de planificación asociado


a ese proceso.
• Una nueva copia del proceso actual es creada mediante la llamada al sistema fork. Para
ejecutar un nuevo programa se utiliza la función execve.
• Las prioridades bajas son favorecidas. La mayoría de los procesos usan políticas de
prioridad dinámica. El valor “nice” establece la prioridad base de un proceso. Los valores de
nice van desde -20 a +20 (más grande = menor prioridad). Los usuarios no privilegiados sólo
pueden especificar valores de nice positivos. Los procesos normales se ejecutan sólo
cuando no quedan procesos de tiempo real (de prioridad fija) en la cola de listos.

Ing. Jorge Orellana A. 28


Sistemas Operativos (2010017)

Tema III
Comunicación y Sincronización de Procesos
Los procesos que se ejecutan de forma concurrente en un sistema se pueden clasificar como
procesos independientes o cooperantes. Un proceso independiente es aquel que ejecuta sin
requerir la ayuda o cooperación de otros procesos. Los procesos son cooperantes cuando están
diseñados para trabajar conjuntamente en alguna actividad, para lo que deben ser capaces de
comunicarse e interactuar entre ellos.
Tanto si los procesos son independientes como cooperantes, pueden producirse una serie de
interacciones entre ellos. Estas interacciones pueden ser de dos tipos:
• Interacciones motivadas porque los procesos comparten o compiten por el acceso a
recursos físicos o lógicos. Por ejemplo, dos procesos totalmente independientes pueden
competir por el acceso a disco, en este caso, el sistema operativo deberá encargarse de que
los dos procesos accedan ordenadamente sin que se cree ningún conflicto.
• Interacción motivada porque los procesos se comunican y sincronizan entre sí para alcanzar
un objetivo común. Por ejemplo, un compilador se puede construir mediante dos procesos: el
compilador propiamente dicho, que se encarga de generar código ensamblador, y el proceso
ensamblador, que obtiene código en lenguaje máquina a partir del ensamblador.

Estos dos tipos de interacciones obligan al sistema operativo a incluir mecanismos y servicios
que permitan la comunicación y la sincronización entre procesos (IPC InterProcess
Communication).

3.1 Condiciones de competencia.


Muchos problemas se pueden resolver más fácilmente o más eficientemente si se usa procesos
(o hebras) cooperativos, que se ejecutan concurrentemente, técnica que se conoce como
programación concurrente. La programación concurrente es una herramienta poderosa, pero
introduce algunos problemas que no existen en la programación secuencial (no concurrente).
Suponiendo que se usan dos hebras concurrentes para determinar cuántos números primos hay
en un intervalo dado.

Siendo numPrimos una variable global, podemos ejecutar primos(1,5000) en paralelo con
primos(5001,10000) para saber cuántos números primos hay entre 1 y 10000. ¿El problema?
numPrimos es una variable compartida que se accesa concurrentemente, y puede quedar mal
actualizada si se dan determinadas intercalaciones de las operaciones de las dos hebras.

Para ejecutar una operación como numPrimos=numPrimos+1 se suelen requerir varias


instrucciones de máquina, como ser:

Suponiendo que, más o menos simultáneamente, las dos hebras encuentran su primer número
primo. Podría darse el siguiente timing (inicialmente, numPrimos==0).

Ing. Jorge Orellana A. 29


Sistemas Operativos (2010017)

El resultado es que numPrimos queda en 1, pero lo correcto sería 2. El problema se produce


porque las dos hebras o procesos tratan de actualizar una variable compartida al mismo tiempo;
o mejor dicho, una hebra comienza a actualizarla cuando la otra no ha terminado. Esto se
conoce como competencia por datos (data race, o también, race condition) cuando el resultado
depende del orden particular en que se intercalan las operaciones de procesos concurrentes. La
cosa se resuelve si se garantiza, de alguna manera, que sólo una hebra a la vez puede estar
actualizando variables compartidas.

3.2 El problema de la sección crítica


Éste es uno de los problemas que con mayor frecuencia aparece cuando se ejecutan procesos
concurrentes tanto si son cooperantes como independientes. Si se considera un sistema
compuesto por n procesos { P0, P1, ... , Pn }, en el que cada uno tiene un fragmento de código,
que se denomina sección crítica, en el que se accesa a variables comunes, o en general,
recursos compartidos. La característica más importante de este sistema es que cuando un
proceso se encuentra ejecutando código de la sección crítica, ningún otro proceso puede
ejecutar en su sección.
Para resolver el problema de la sección crítica es necesario utilizar algún mecanismo de
sincronización que permita a los procesos cooperar entre ellos sin problemas. Este mecanismo
debe proteger el código de la sección crítica y su funcionamiento básico es el siguiente:

• Cada proceso debe solicitar permiso para entrar en la sección crítica mediante algún
fragmento de código, que se denomina de forma genérica entrada en la sección crítica.
• Cuando un proceso sale de la sección crítica debe indicarlo mediante otro fragmento de
código, que se denomina salida de la sección crítica. Este fragmento permitirá que otros
procesos entren a ejecutar el código de la sección crítica. La estructura general, por tanto,
de cualquier mecanismo que pretenda resolver el problema de la sección crítica es la
siguiente:
Entrada en la sección crítica
Código de la sección crítica
Salida de la sección crítica

Cualquier solución que se utilice para resolver este problema debe cumplir los tres requisitos
siguientes:

• Exclusión mutua: si un proceso está ejecutando código de la sección crítica, ningún otro
proceso lo podrá hacer.
• Progreso (Ausencia de postergación innecesaria): si ningún proceso está ejecutando
dentro de la sección crítica, la decisión de qué proceso entra en la sección se hará sobre los
procesos que desean entrar. Los procesos que no quieren entrar no pueden formar parte de
esta decisión. Además, esta decisión debe realizarse en tiempo finito.
• Espera acotada (Entrada garantizada -ausencia de inanición): debe haber un límite en el
número de veces que se permite que los demás procesos entren a ejecutar código de la
sección crítica después de que un proceso haya efectuado una solicitud de entrada y antes
de que se conceda la suya.

A continuación se examinan varias propuestas de solucion, de manera que mientras un proceso


esté ocupado actualizando la memoria compartida en su región crítica, ningún otro proceso
pueda entrar en su región crítica y provocar algún problema.

Ing. Jorge Orellana A. 30


Sistemas Operativos (2010017)

3.2.1 Primer intento (Inhabilitación de interrupciones)


La solución más sencilla es hacer que cada proceso inhiba todas las interrupciones nada más
entrar en su región crítica y que las rehabilite nada más salir de ella. Con las interrupciones
inhibidas, no puede ocurrir ninguna interrupción del reloj. Después de todo, la CPU sólo se
conmuta a otro proceso como resultado de una interrupción del reloj o de otro dispositivo, y con
las interrupciones inhibidas la CPU no puede conmutarse a otro proceso. Entonces, una vez que
un proceso ha inhibido las interrupciones, puede examinar y actualizar la memoria compartida
sin temor a intromisiones de otros procesos. Se descarta por peligroso, un proceso de usuario
con capacidad de deshabilitar interrupciones puede colgar el sistema. Además, no funcionaría en
multiprocesadores.

3.2.2 Segundo intento (Variables de Bloqueo)


Se considera una variable compartida (variable turno) cuyo valor es inicialmente 0. Cuando un
proceso quiere entrar en su región crítica, primero examina la variable turno. Si el turno está a 0,
el proceso lo pone a 1 y entra en la región crítica. Si el turno ya estaba a 1, el proceso espera
hasta que vuelva a valer 0. Entonces, un 0 significa que ningún proceso está en la región crítica,
y un 1 significa que algún proceso está en su región crítica.

Suponiendo que un proceso lee el turno y observa que vale 0. Antes de que pueda poner el turno
a 1, se planifica otro proceso que pasa a ejecución y pone el turno a 1. Cuando el primer proceso
se ejecute de nuevo, pondrá a 1 también el turno, y se tendrá a dos procesos en sus regiones
críticas al mismo tiempo.

3.2.3 Tercer intento (Alternancia Estricta)


Otro intento de solucion permite manejar más información, un arreglo para los procesos
interesados en ingresar a la seccion critica, interesado[i] que está en verdadero si proceso i
quiere entrar a la sección crítica y falso si no lo quiere.

Tampoco funciona ya que si ambos procesos ponen interesado[i] en TRUE al mismo tiempo,
ambos quedan esperando para siempre. Se podría cambiar el orden, verificar primero y setear
despúes la variable interesado, pero entonces se podría terminar con los dos procesos en la
sección crítica.

3.2.4 Cuarto intento (Solución de Peterson)


Combinando las dos ideas anteriores se puede obtener un algoritmo que sí funciona.

Antes de utilizar las variables compartidas (es decir, antes de entrar en su región crítica), cada
proceso invoca entrar_en_region con su propio número de proceso, 0 o 1, como parámetro. Esta
llamada puede provocar su espera, si es necesario, hasta que sea seguro entrar. Cuando
termine de utilizar las variables compartidas, el proceso invoca abandonar_region para indicar
que ha terminado y para permitir a los demás procesos entrar, si así lo desean.

Ing. Jorge Orellana A. 31


Sistemas Operativos (2010017)

Inicialmente no hay ningún proceso en su región crítica. Ahora el proceso 0 llama a


entrar_en_region. Indica su interés poniendo a TRUE su componente del array interesado y pone
el valor 0 en turno. Ya que el proceso 1 no está interesado, entrar_en_region retorna
inmediatamente. Si el proceso 1 llama ahora a entrar_en_region, quedará atrapado en esa
función hasta que interesado[0] pase a valer FALSE, algo que sólo ocurre cuando el proceso 0
llama a abandonar_region para salir de la región crítica.
Si se considera el caso en que ambos procesos llaman a entrar_en_region casi
simultáneamente. Ambos deben asignar su número de proceso a la variable turno. La asignación
que va a prevalecer es la del último proceso que realice la asignación; siendo anulada la primera
de las asignaciones por la sobreescritura. Supongamos que el proceso 1 es el último que hace la
asignación, de manera que turno es 1. Cuando ambos procesos entran en la sentencia while, el
proceso 0 la ejecuta cero veces y entra en su región crítica. El proceso 1 se pone a dar vueltas
en el bucle y no entra en su región crítica hasta que el proceso 0 sale de su región crítica.

3.2.5 Solución con ayuda del hardware


Muchos sistemas tienen instrucciones especiales que ayudan bastante a la hora de resolver el
problema de la sección crítica. En general, son instrucciones que de alguna manera combinan
dos o tres operaciones en una sola operación atómica. Por ejemplo, TEST-AND-SET (TS) lee el
contenido de una dirección de memoria en un registro, y pone un 1 en la misma dirección, todo
en una sola acción atómica o indivisible, en el sentido que ningún otro proceso puede accesar
esa dirección de memoria hasta que la instrucción se complete. De cierto modo es equivalente a:

Con esto, una solución al problema de la sección crítica, en pseudoasssembler, sería:

Inicialmente, la variable lock está en 0. Este protocolo provee exclusión mutua sin postergación
innecesaria, y sirve para n procesos, pero puede haber inanición.

Ing. Jorge Orellana A. 32


Sistemas Operativos (2010017)

3.2.6 Semáforos
Los inconvenientes de las soluciones anteriores, son que es difícil generalizarlas para problemas
de sincronización más complejos, o simplemente distintos, de la sección crítica y derrochan
CPU, ya que usan espera ocupada (busy waiting); cuando un proceso quiere entrar a la sección
crítica, está permanentemente chequeando si puede hacerlo.

Si el sistema operativo tuviera un mayor conocimiento de la situación, podría bloquear a un


proceso que quiere entrar a la sección crítica, sin concederle oportunidad de ejecutar, mientras
no pueda entrar. Una posibilidad es usar semáforos. Un semáforo S es un tipo abstracto de dato
que puede manipularse mediante dos operaciones: P (o wait o down) y V (o signal o up), y cuyo
efecto es equivalente a:

La operación wait sobre un semáforo comprueba si el valor es mayor que 0. Si lo es,


simplemente decrementa el valor (es decir utiliza una de las señales guardadas). Si el valor es 0,
el proceso procede a dormirse sin completar la operación bajar por el momento. La
comprobación del valor del semáforo, su modificación y la posible acción de dormirse, se
realizan como una única acción atómica indivisible. Está garantizado que una vez que comienza
una operación sobre un semáforo, ningún otro proceso puede acceder al semáforo hasta que la
operación se completa o hasta que el proceso se bloquea. Esta atomicidad es absolutamente
esencial para resolver los problemas de sincronización y evitar que se produzcan condiciones de
competencia.
La operación signal incrementa el valor del semáforo al cual se aplica. Si uno o más procesos
estuviesen dormidos sobre ese semáforo, incapaces de completar una anterior operación de
wait, el sistema elige a uno de ellos (por ejemplo de forma aleatoria) y se le permite completar
esa operación wait pendiente. Entonces, después de ejecutarse un signal sobre un semáforo con
procesos dormidos en él, el semáforo sigue valiendo 0, pero habrá un proceso menos, dormido
en él. La operación de incrementar el semáforo y despertar un proceso es también indivisible. La
operación de signal nunca bloquea al proceso que la ejecuta.

El problema de la sección crítica se puede resolver fácilmente con un semáforo S, con valor
inicial 1.

A nivel de sincronizacion, si se quiere que un proceso P2 ejecute un trozo de código C2 pero


sólo después que P1 haya completado otro trozo C1, se usa un semáforo para sincronizar con
valor inicial 0.

Ing. Jorge Orellana A. 33


Sistemas Operativos (2010017)

3.2.7. Monitores
Los semáforos son una herramienta general y eficiente para sincronizar procesos, pero siguen
siendo de bajo nivel. Las soluciones mediante semáforos no son ni muy limpias ni muy claras, y
siempre están sujetas a errores como la omisión o mala ubicación de una operación wait o
signal.
Los monitores son una herramienta de sincronización más estructurada que encapsula
variables compartidas en conjunto con los procedimientos para accesar esas variables.

Los monitores tienen una importante propiedad que los hace útiles para conseguir exclusión
mutua: en cualquier instante solamente un proceso puede estar activo dentro del monitor. Los
monitores son construcciones del lenguaje, por lo que el compilador sabe que son especiales, de
manera que puede tratar las llamadas a los procedimientos del monitor de forma diferente que a
otras llamadas a procedimientos normales. En la mayoría de los casos, cuando un proceso llama
a un procedimiento de un monitor, las primeras instrucciones del procedimiento comprueban si
cualquier otro proceso está actualmente activo dentro del monitor. En ese caso, el proceso que
hizo la llamada debe suspenderse hasta que el otro proceso abandone el monitor. Si ningún otro
proceso está utilizando el monitor, el proceso que hizo la llamada puede entrar inmediatamente.
Corresponde al compilador implementar la exclusión mutua sobre las entradas al monitor. La
forma más común de implementarla es utilizando una variable de exclusión mutua o un semáforo
binario. Debido a que es el compilador y no el programador el que organiza las cosas para
conseguir la exclusión mutua, resulta mucho más difícil que se produzcan errores. En cualquier
caso, la persona que escribe el monitor no tiene que preocuparse de cómo consigue asegurar la
exclusión mutua el compilador dentro del monitor. Es suficiente con que sepa que metiendo
todas las regiones críticas en procedimientos del monitor, nunca habrá dos procesos que
ejecuten sus regiones críticas a la vez.

Un monitor es, en consecuencia, un tipo de dato abstracto, y por lo tanto:


• Los procesos pueden llamar a los procedimientos del monitor en cualquier momento, pero
no pueden accesar directamente las variables encapsuladas por el monitor.
• Los procedimientos dentro del monitor no pueden accesar variables externas al monitor: sólo
pueden accesar las variables permanentes del monitor (i y c en el ejemplo), las variables
locales al procedimiento, y los argumentos del procedimiento (x en el ejemplo).
• Las variables permanentes del monitor deben estar inicializadas antes que ningún
procedimiento sea ejecutado.

Para esperar eventos (sincronización por condición) se usan variables de condición. Una
variable de condición c tiene asociada una cola de procesos, y soporta dos operaciones:
• wait(c): suspende al proceso invocador, lo pone al final de la cola asociada a c, y libera el
monitor. O sea, aunque no se haya completado un procedimiento, el proceso deja de estar
activo dentro del monitor, y por lo tanto otro proceso puede ejecutar dentro del monitor.
• signal(c): despierta al proceso al frente de la cola asociada a c, si es que existe. En
monitores con semántica signal-and-continue, el proceso que hace signal continúa dentro

Ing. Jorge Orellana A. 34


Sistemas Operativos (2010017)

del monitor; el proceso despertado, si existe, debe esperar su oportunidad para volver a
entrar al monitor y continuar después del wait. En monitores con semántica signal-and-wait,
si es que el signal despierta a un proceso, entonces el proceso señalizador suspende su
ejecución en favor del proceso señalizado; el señalizador debe esperar (posiblemente
compitiendo con otros procesos) para poder volver al monitor.

Las variables de condición no son contadores, ya que no acumulan las señales para un uso
posterior como hacen los semáforos. Entonces si una variable de condición recibe una señal sin
que haya ningún proceso esperándola, la señal se pierde para siempre. En otras palabras el wait
debe ocurrir antes que el signal. Esta regla simplifica mucho la implementación, y en la práctica
no representa ningún problema ya que, si es necesario, es fácil seguir la pista del estado de
cada proceso utilizando variables adicionales. Antes de llevar a cabo un signal, el proceso puede
ver si esa operación es necesaria, o no, inspeccionando esas variables.

Algunos lenguajes de programación reales soportan los monitores, aunque no siempre en la


forma diseñada, por ejemplo Java. Java es un lenguaje orientado a objetos que soporta threads
a nivel de usuario y que permite agrupar juntos varios métodos (procedimientos) formando
clases. Añadiendo la palabra reservada synchronized a la declaración de un método, Java
garantiza que una vez que un thread comienza a ejecutar ese método, no se le permite a ningún
otro thread comenzar a ejecutar cualquier otro método synchronized de esa clase.
Los métodos sincronizados de Java difieren de los monitores clásicos en un aspecto esencial:
Java no dispone de variables de condición. En su lugar, ofrece dos procedimientos wait y notify
que son los equivalentes de wait(c) y signal(c) salvo que cuando se utilizan dentro de métodos
sincronizados no están expuestos a que se produzcan condiciones de competencia.

3.2.8. Paso de mensajes


Las primitivas vistas hasta ahora sirven para sincronizar procesos, pero si se quiere que los
procesos intercambien información se tiene que agregar variables compartidas a la solución.
Se puede intercambiar información mediante paso de mensajes.

Este método de comunicación entre procesos utiliza dos primitivas, enviar y recibir, que igual que
los semáforos y de forma diferente a los monitores son llamadas al sistema en vez de
construcciones del lenguaje de programación. Como tales, pueden incluirse fácilmente en
librerías de procedimientos, tales como

enviar(destinatario, &mensaje);
recibir(remitente, &mensaje);

La primera llamada envía un mensaje a un destinatario dado y la segunda recibe un mensaje de


un remitente dado (o de cualquiera, si al receptor no le importa el remitente). Si no está
disponible ningún mensaje, el receptor puede bloquearse hasta que llegue uno. De forma
alternativa, puede retornar inmediatamente indicando un código de error.

El paso de mensajes se utiliza comúnmente en sistemas de programación paralela. Un sistema


de paso de mensajes bien conocido es MPI (Message-Passing Interface) que es una librería
para lenguajes de programación como el C.

3.3. Problemas clásicos de sincronización

3.3.1. Productor-consumidor con buffer limitado


El problema del productor consumidor (también conocido como el problema de buffer
acotado) es un ejemplo clásico de un problema de multi- proceso de sincronización. En el
problema se describen dos procesos, el productor y el consumidor, que comparten un común,
búffer fijo utilizado como una cola. El trabajo del productor es generar un conjunto de productos y
colocarlos en el buffer (almacen). Al mismo tiempo, el consumidor consume los productos (es
decir, los retira del buffer) de una sola pieza a la vez. El problema es asegurarse de que el

Ing. Jorge Orellana A. 35


Sistemas Operativos (2010017)

productor no intente agregar productos en el búffer si está lleno y que el consumidor no va a


tratar de consumir productos de un búffer vacío. La solución se puede alcanzar por medio de la
comunicación entre procesos, usando semáforos. El problema también puede ser generalizado
para tener múltiples productores y consumidores, como se muestra en el código siguiente:

int cantProductos = 0; // cantidad de productos que se encuentran en el buffer


const int tamBuffer = 5; //tamaño buffer
const int MAXPRODUCCION = 10; //numero de elementos a producir
int estadoConsumo = 0;
int estadoProduccion = 1;
int ultimo = 0;
int reciente = 0;
int colaProduccion[tamBuffer];
semaphore semProductor = 1; // Si el buffer esta lleno, lo duerme
semaphore semConsumidor = 0; // Si el buffer esta vacio, lo duerme
semaphore semBuffer = 1; // controla la estricta alternancia, Si esta en 1 el consumidor consume
// Si esta en 0, el productor produce y el consumidor deja de consumir

void productor(int ID) {


int producto = 1;
while(producto <= MAXPRODUCCION) {
wait(semProductor);
wait(semBuffer);
colaProduccion[reciente] = ID*100+producto;
reciente = (reciente + 1) % tamBuffer;
cantProductos = cantProductos + 1;
cout << "Productor " << ID << " produce el producto " << ID*100+producto << endl;
producto = producto + 1;
cout << "...En la cola de produccion estan: " << cantProductos << " productos\n\n";
signal(semBuffer);
if(cantProductos < tamBuffer) {
signal(semProductor);
estadoProduccion = 1;
} else {
estadoProduccion = 0;
}
if ((estadoConsumo == 0) && (estadoProduccion == 1)) {
signal(semConsumidor);
estadoConsumo = 1;
}
}
}

void consumidor(int ID) {


int producto = 1;
int p;
while(producto <= MAXPRODUCCION) {
wait(semConsumidor);
wait(semBuffer);
p = colaProduccion[ultimo];
ultimo = (ultimo + 1) % tamBuffer;
cantProductos = cantProductos - 1;
cout << "Consumidor " << ID << " consumio el producto " << p << endl;
producto = producto + 1;
cout << ".....en la cola de produccion estan: " << cantProductos << " productos\n\n";
signal(semBuffer);
if(cantProductos > 0) {
signal(semConsumidor);
estadoConsumo = 1;
} else {
estadoConsumo = 0;
}
if((estadoProduccion == 0) && (estadoConsumo == 1)) {
signal(semProductor);
estadoProduccion = 1;
}
}
}

void main() {
cobegin {
productor(1);
productor(2);
consumidor(1);
consumidor(2);
}
}

Ing. Jorge Orellana A. 36


Sistemas Operativos (2010017)

y su ejecucion sería:

3.3.2. Lector-Escritor
Un objeto, tal como un archivo o un registro de una base de datos, es compartido por varios
procesos concurrentes. Hay procesos escritores, que modifican el objeto, y procesos lectores,
que sólo lo consultan. Puede haber múltiples procesos leyendo el objeto simultáneamente, pero
cuando hay un proceso escribiendo, no puede haber ningún lector ni ningún otro escritor. La
solución se puede alcanzar por medio de la comunicación entre procesos, usando monitores:

monitor LectorEscritor {
int contadorLector; // lectores concurrentes
int ocupado;
condition OKleer, OKescribir;

void EmpiezaLeer() {
if (ocupado || !empty(OKescribir))
waitc(OKleer);
contadorLector = contadorLector + 1;
cout << "Numero lectores concurrentes " << contadorLector << '\n';
signalc(OKleer);
}

void FinLeer() {
contadorLector = contadorLector - 1;
if (contadorLector == 0)
signalc(OKescribir);
}

void EmpiezaEscribir() {
if (ocupado || (contadorLector != 0))
waitc(OKescribir);
ocupado = 1;
}

void FinEscribir() {
ocupado = 0;
if (empty(OKleer))
signalc(OKescribir);
else
signalc(OKleer);
}

init {
contadorLector = 0; ocupado = 0; //inicializacion
}
} // fin monitor

void Lector(int N) {
int I;
for (I = 1; I < 2; I++) {
EmpiezaLeer();
cout << N << " esta leyendo" << '\n';
FinLeer();
}
}

void Escritor(int N) {
int I;

Ing. Jorge Orellana A. 37


Sistemas Operativos (2010017)

for (I = 1; I < 2; I++) {


EmpiezaEscribir();
cout << N << " esta escribiendo" << '\n';
FinEscribir();
}
}

void main() {
cobegin {
Lector(1); Lector(2); Lector(3);
Escritor(1); Escritor(2);
}
}

y su ejecucion sería:

3.3.3. Los filósofos comensales


Cinco filósofos pasan la vida alternando entre comer y pensar, alrededor de una mesa redonda.
Como son medio torpes, requieren dos tenedores para comer, pero como son pobres, sólo
tienen 5 tenedores. Han acordado que cada uno usará sólo los tenedores a su izquierda y a su
derecha. Entonces, de los 5 filósofos, sólo 2 pueden comer al mismo tiempo, y no pueden comer
dos que son vecinos. La solución se puede alcanzar por medio de la comunicación entre
procesos, usando monitores, con resultados gráficos también:

#include "gdefs.cm"
const
int PEN = 0; int HAM = 1; int COM = 2;
int Mesa = 100;
int pos[5][4];
int inc = 0;
int i;
monitor FilosofoComensal {
int estFil[5];//estado de todos los filósofos
condition libre[5];//condicion del cubierto de cada filósofo

void comer(int fil){


if((estFil[fil]==HAM) && (estFil[(fil+4)%5]!=COM) && (estFil[(fil+1)%5]!=COM)){
estFil[fil] = COM;
//Filósofo Comiendo
cout << "Filosofo comiendo "<< fil <<endl;
create(fil, TRIANGLE, RED, pos[fil][0], pos[fil][1], pos[fil][2], pos[fil][3]);
signalc(libre[fil]);
}
}
void mirar(int fil){
if(estFil[fil]==PEN){
estFil[fil] = HAM;
comer(fil);
if(estFil[fil] != COM){
//Filósofo Hambriento
cout<< "Filosofo hambriento "<< fil <<endl;
create(fil, TRIANGLE, YELLOW, pos[fil][0], pos[fil][1], pos[fil][2], pos[fil][3]);
waitc(libre[fil]);
}
}
}
void pensar(int fil){
if(estFil[fil]!=PEN){
//Filósofo Pensando
cout<< "Filosofo pensando "<< fil <<endl;
create(fil, TRIANGLE, GREEN, pos[fil][0], pos[fil][1], pos[fil][2], pos[fil][3]);
estFil[fil] = PEN;
comer ((fil+4)%5);
comer ((fil+1)%5);
}
}
init{

Ing. Jorge Orellana A. 38


Sistemas Operativos (2010017)

for(i = 0; i<= 4; i++){


estFil[i]=PEN; //inicializacion
}
//Posiciones Graficas de los Filosofos
pos[0][0] = 275; pos[0][1] = 160; pos[0][2] = 30; pos[0][3] = 30;
pos[1][0] = 230; pos[1][1] = 230; pos[1][2] = -30; pos[1][3] = -30;
pos[2][0] = 245; pos[2][1] = 245; pos[2][2] = 30; pos[2][3] = 30;
pos[3][0] = 305; pos[3][1] = 245; pos[3][2] = 30; pos[3][3] = 30;
pos[4][0] = 320; pos[4][1] = 230; pos[4][2] = -30; pos[4][3] = -30;
}
}

void filosofo(int fil){


create(fil, TRIANGLE, GREEN, pos[fil][0], pos[fil][1], pos[fil][2], pos[fil][3]);
while (inc < 100){// tiempo de vida de un filosofo
if ((random(2))==PEN) pensar(fil);
else mirar(fil);
inc = inc + 1;
}
pensar(fil);
//los filosofos se van
create(fil, TRIANGLE, WHITE, pos[fil][0], pos[fil][1], pos[fil][2], pos[fil][3]);
}

void main(){
create(Mesa, CIRCLE, BLACK, 250, 200, 50, 50);
cobegin{
filosofo(0);
filosofo(1);
filosofo(2);
filosofo(3);
filosofo(4);
}
}

y su ejecucion sería:

Ing. Jorge Orellana A. 39


Sistemas Operativos (2010017)

Tema IV
Bloqueos Irreversibles
(Bloqueos mutuos, deadlocks, Interbloqueos o Abrazo mortal)

La administración de los recursos es una de las principales tareas del sistema operativo, ya que
tienen que ofrecer mecanismos que permitan a los procesos acceder de forma exclusiva a los
recursos. Cuando un proceso solicita ciertos recursos y éstos no están disponibles en ese
momento, entra en un estado de espera. Si se tienen muchos procesos que compiten por
recursos finitos, puede darse una situación en la que un proceso está bloqueado esperando por
un recurso que nunca se liberará, porque lo posee otro proceso también bloqueado. Una ley de
principios de siglo, en Kansas, grafica esta situacion con el siguiente enunciado "cuando dos
trenes se aproximan a un cruce, ambos deben detenerse completamente, y ninguno podrá
continuar hasta que el otro se haya ido."

Un sistema se compone de un número finito de recursos que son distribuidos entre un número
de procesos que compiten por ellos. Los recursos pueden ser:

• Físicos: ciclos de CPU, espacio en memoria, dispositivos de E/S (impresoras, unidades


de cinta, etc.).
• Lógicos: archivos, tablas del sistema, registro de bases de datos, semáforos.

Los recursos son clasificados en diferentes tipos, cada uno de los cuales se compone de algún
número de instancias iguales. Si un sistema tiene dos CPU’s, entonces el tipo CPU tiene dos
instancias. Similarmente, el tipo IMPRESORAS puede tener cinco instancias. Si un proceso pide
una instancia de un tipo de recurso, la asignación de cualquier instancia de ese tipo atenderá la
petición. Si este no es el caso, entonces las instancias no son idénticas y las clases de tipos de
recursos no están bien definidas. Un proceso debe solicitar un recurso antes de usarlo y liberarlo
después de usarlo. Un proceso puede solicitar tantos recursos como sean necesarios para llevar
a cabo la tarea para la cual ha sido diseñado. Obviamente el número de recursos solicitados no
debe exceder el número de recursos disponibles en el sistema. En otras palabras, un proceso no
debe pedir tres impresoras si en el sistema solo existen dos.

Una computadora normalmente tendrá varios recursos que pueden ser otorgados. Algunos
podrán tener varias referencias idénticas, como en el caso de las unidades de cinta. Si se tienen
varias copias disponibles de un recurso, cualquiera de ellas se puede utilizar para satisfacer
cualquier solicitud de recurso. Los recursos son de dos tipos:

• Apropiativos
• No apropiativos

Recursos apropiativos. Los recursos apropiativos son aquellos que se pueden tomar del
proceso que le posee sin efectos dañinos. La memoria es un ejemplo de recursos apropiativos.
Por ejemplo considerar un sistema con 512 Kb de memoria de usuario, una impresora, y dos
procesos de 512 Kb que se desean imprimir cada uno.
• El proceso A solicita y obtiene la impresora, para entonces comienza a calcular los valores
que va a imprimir. Antes de terminar el cálculo excede su quantum de tiempo y se
intercambia.
• El proceso B se empieza a ejecutar e intenta sin éxito adquirir la impresora.
• Potencialmente se tendría una situación de bloqueo, puesto que A tiene la impresora y B la
memoria y ninguno puede continuar sin el recurso que posee el otro.
• Por fortuna es posible apropiarse de la memoria de B y sacarlo de ella e intercambiarlo con
A.
• A puede ejecutarse entonces, imprimir y liberar después la impresora. No ocurre un bloqueo.

Ing. Jorge Orellana A. 40


Sistemas Operativos (2010017)

Recursos no apropiativos . Los recursos no apropiativos son aquellos que no se pueden tomar
de su poseedor activo sin provocar un fallo de cálculo. Por ejemplo, si un proceso comienza a
imprimir una salida, se toma la impresora y se le da otro proceso, el resultado será una salida
incomprensible. Las impresoras no son apropiables. En general los bloqueos se relacionan con
los recursos no apropiables.

La secuencia de eventos necesarios para utilizar un recurso es la siguiente:


1. Solicitar el recurso.
2. Utilizar el recurso.
3. Liberar el recurso.

• Petición. Si la petición no puede ser satisfecha inmediatamente (por ejemplo, el recurso esta
siendo utilizado por otro proceso), entonces el proceso solicitante debe esperar hasta que
pueda adquirir el recurso.
• Uso. El proceso puede utilizar un recurso (por ejemplo, si el recurso es una impresora en
línea, el proceso puede imprimir en la impresora).
• Liberación. El proceso libera el recurso. Si el recurso no esta disponible cuando es
requerido, el proceso solicitante se ve forzado a esperar. En algunos sistemas operativos, el
proceso se bloquea automáticamente cuando falla la solicitud el recurso y se desbloquea
cuando esta disponible. En otros sistemas, la requisición falla en un código de error y
corresponde al recurso solicitante esperar un poco y volverlo a intentar.

La petición y liberación del recurso son peticiones al sistema. Ejemplos de llamadas al sistema
son: Petición/Liberación de dispositivos, Abrir/Cerrar archivos y Asignar/Desasignar memoria. El
uso de recursos puede también hacerse sólo a través de llamadas al sistema. Por lo tanto, para
cada uso, el sistema operativo chequea para asegurarse de que el proceso usuario ha pedido y
se le han asignado los recursos. Una tabla del sistema registra cuando un recurso está libre o
asignado, y si está asignado, a qué proceso. Si un proceso pide un recurso que está asignado en
ese momento a otro proceso, éste puede ser agregado a una cola de procesos en espera de ese
recurso.

4.1. Bloqueos

Un conjunto de procesos se encuentra en estado de interbloqueo cuando cada uno de ellos


espera un suceso que sólo puede originar otro proceso del mismo conjunto. Por ejemplo, si dos
procesos desean imprimir cada uno un enorme archivo en cinta.
• El proceso A solicita permiso para utilizar la impresora (recurso 1), el cual se le concede.
• El proceso B solicita permiso para utilizar la unidad de cinta (recurso 2) y se le otorga.
• El proceso A solicita entonces la unidad de cinta (recurso 2), pero la solicitud es denegada
hasta que B la libere. En este momento, en vez de liberar la unidad de cinta, el proceso B
solicita la impresora (recurso 1).
Los procesos se bloquean en ese momento y permanecen así por siempre. A esta situación se le
llama BLOQUEO.

Ing. Jorge Orellana A. 41


Sistemas Operativos (2010017)

En ambientes de multiprogramación, varios procesos pueden competir por un número finito de


recursos. Un proceso solicita recursos, y si los recursos no están disponibles en ese momento, el
proceso entra en un estado de espera. Puede suceder que los procesos en espera nunca
cambien de estado, debido a que los recursos que han solicitado están siendo detenidos por
otros procesos en espera. Por ejemplo, esta situación ocurre en un sistema con cuatro unidades
de disco y dos procesos. Si cada proceso tiene asignadas dos unidades de disco pero necesita
tres, entonces cada proceso entrará a un estado de espera, en el cual estará hasta que el otro
proceso libere las unidades de cinta que tiene asignadas. Esta situación es llamada Abrazo
Mortal (Deadlock, interbloqueo o bloqueo mutuo). Para prevenir un abrazo mortal o recuperarse
de alguno que ocurra, el sistema puede tomar alguna acción relativamente extrema, tal como el
derecho de tomar los recursos (preemption of resources) de uno o más de los procesos que se
encuentren en el abrazo mortal.

4.2. Condiciones para producir un interbloqueo


Según Coffman (1971), existen cuatro condiciones que deben cumplirse para que haya bloqueo
o interbloqueo. Una situación puede surgir sí y solo sí las siguientes cuatro condiciones ocurren
simultáneamente en un sistema:
• Exclusión mutua. Cada recurso se asigna por lo regular exactamente a un proceso o bien
esta disponible. Al menos un recurso es mantenido en un modo no-compartible; esto es, sólo
un proceso a la vez puede usar el recurso. Si otro proceso solicita ese recurso, tiene que ser
retardado hasta que el recurso haya sido liberado.
• Retener y esperar. Los procesos que regularmente contienen recursos otorgados antes
pueden solicitar nuevos recursos. Debe existir un proceso que retenga al menos un recurso
y esté esperando para adquirir recursos adicionales que están siendo retenidos por otros
procesos.
• No existe el derecho de desasignar (No expropiación). Los recursos previamente
otorgados no pueden extraerse por la fuerza de un proceso. Deben ser liberados
explícitamente por el proceso que los contiene. Los recursos no pueden ser desasignados
(preempted); esto es, un recurso sólo puede ser liberado voluntariamente por el proceso que
lo retiene, después de que el proceso ha terminado su tarea.
• Espera circular. Debe haber una cadena de dos o más procesos, cada uno de los cuales
esté esperando un recurso contenido en el siguiente miembro de la cadena. Debe existir un
conjunto {p0, p1,...,pn} de procesos en espera tal que p0 esté esperando por un recurso que
está siendo retenido por p1, p1 está esperando por un recurso que está siendo retenido por
p2,..., pn-1 está esperando por un recurso que está siendo retenido por pn y pn está
esperando por un recurso que está siendo retenido por p0.

4.3. Modelación de Interbloqueos


Los interbloqueos pueden describirse utilizando un grafo dirigido y bipartito G(N,A) llamado grafo
de asignación de recursos que consta en un conjunto de N nodos (vértices) y E arcos. Hay 2
tipos de nodos (Procesos y Recursos) y 2 tipos de arcos (Solicitud y Asignación).
Los círculos representan procesos, los cuadrados recursos. Un arco desde un recurso a un
proceso indica que el recurso ha sido asignado al proceso. Un arco desde un proceso a un
recurso indica que el proceso ha solicitado el recurso, y está bloqueado esperándolo. Entonces,
si hacemos el grafo con todos lo procesos y todos los recursos del sistema y encontramos un
ciclo, los procesos en el ciclo están bajo bloqueo mutuo.

Ing. Jorge Orellana A. 42


Sistemas Operativos (2010017)

4.4. Métodos para manejar el interbloqueo


El punto de vista más simple es pretender que no existe el problema (El Algoritmo del Avestrúz
o de Ostrich). Consiste en no hacer absolutamente nada (esconder la cabeza). Los S.O. que
ignoran el problema de los bloqueos asumen la siguiente hipótesis: La mayoría de los usuarios
preferiría un bloqueo ocasional, en vez de una regla que restringiera a todos los usuarios en el
uso de los distintos tipos de recursos. El problema es que se debe pagar un cierto precio para
encarar el problema del bloqueo: en restricciones para los procesos y en el uso de recursos.

Se presenta una contradicción entre la conveniencia y lo que es correcto. Es muy difícil encontrar
teóricamente soluciones prácticas de orden general aplicables a todos los tipos de S.O. Un
criterio de orden general utilizado por los S.O. que no hacen tratamiento específico del bloqueo
consiste en:
• Intentar acceder al recurso compartido.
• De no ser factible el acceso:
o Esperar un tiempo aleatorio.
o Reintentar nuevamente.

Principalmente, existen cuatro métodos para manejar el interbloqueo. Podemos usar algún
protocolo para asegurar que el sistema nunca entrará en un estado de abrazo mortal.
Alternativamente, podemos permitir que el sistema entre en un estado de abrazo mortal
(bloqueo) y después recuperarse. Pero el recuperarse puede ser muy difícil y muy caro.

• Prevención del bloqueo (Deadlock Prevention).


• Evitación del bloqueo (Deadlock Avoidance).
• Detección del bloqueo.
• Recuperación del bloqueo.

4.4.1. Prevención de Bloqueos


La estrategia básica de la prevención del interbloqueo consiste, a grandes rasgos, en diseñar un
sistema de manera que esté excluida, a priori, la posibilidad de interbloqueo.
Los métodos para prevenir el interbloqueo consisten en impedir la aparición de alguna de las
cuatro condiciones necesarias, antes mencionadas. Al asegurarnos de que por lo menos una de
estas condiciones no se presente, podemos prevenir la ocurrencia de un bloqueo.

Exclusión mutua. Si ningún recurso se puede asignar de forma exclusiva, no se produciría


interbloqueo. Sin embargo, existen recursos para los que no es posible negar la condición de
exclusión mutua, pues la propia naturaleza de los mismos obliga a que sean utilizados en
exclusión mutua.
No obstante, es posible eliminar esta condición en algunos recursos. Por ejemplo, una impresora
es un recurso no compatible pues si se permite que dos procesos escriban en la impresora al
mismo tiempo, la salida resultará caótica. Pero con el spooling de salida varios procesos pueden

Ing. Jorge Orellana A. 43


Sistemas Operativos (2010017)

generar salida al mismo tiempo. Puesto que el spooler nunca solicita otros recursos, se elimina el
bloqueo originado por la impresora.
El inconveniente es que no todos los recursos pueden usarse de esta forma (por ejemplo, la
tabla de procesos no se presenta al spooling y, además, la implementación de esta técnica
puede introducir nuevos motivos de interbloqueo, ya que el spooling emplea una zona de disco
finita).

Retener y esperar. Con el fin de asegurar que la condición de retener y esperar nunca ocurra en
el sistema, se debe garantizar que siempre que un proceso solicite un recurso, éste no pueda
retener otros recursos.
Un protocolo que puede ser usado requiere que cada proceso solicite y le sean asignados todos
los recursos antes de que empiece su ejecución. Esto puede ser implantado haciendo que las
llamadas al sistema solicitando recursos se hagan antes que todas las otras llamadas al sistema.
Un protocolo alternativo permite que un proceso solicite recursos cuando no tiene ningún recurso
asignado. Un proceso puede solicitar algunos recursos y utilizarlos, pero, antes de que pueda
volver a pedir recursos adicionales, debe liberar todos los recursos que tiene previamente
asignados.
Para ejemplificar la diferencia entre estos dos protocolos, consideremos un proceso que copia un
archivo de una unidad de cinta(1) a una unidad de disco, ordena el archivo en disco, después
imprime los resultados a una impresora en línea y finalmente copia el archivo de disco a la
unidad de cinta(2).
Si todos los recursos deben ser solicitados al inicio del proceso, entonces el proceso debe iniciar
solicitando la unidad de cinta(1), la unidad de disco, la impresora en línea y la unidad de cinta(2).
Este proceso mantendrá las unidades de cinta (1) y (2) durante la ejecución completa aunque
sólo las utilice al principio y al final de su ejecución respectivamente.
El segundo método permite al proceso el solicitar inicialmente solo la unidad de cinta(1) y la
unidad de disco. Ahora puede copiar el archivo de la unidad de cinta (1) a la unidad de disco y
ordenarlo, posteriormente debe liberar la unidad de cinta(1) y la unidad de disco. El proceso
debe volver a solicitar la unidad de disco y la impresora en línea para hacer la impresión.
Después de imprimir debe liberar la unidad de disco y la impresora. Ahora debe de volver a
solicitar la unidad de disco y la unidad de cinta(2) para copiar el archivo a cinta, debe liberar
ambos recursos y terminar.
Existen dos desventajas principales en estos protocolos:
• La primera, la utilización de los recursos puede ser muy baja, debido a que varios de los
recursos pueden estar asignados pero no son utilizados la mayor parte del tiempo.
• Segundo, puede ocurrir Inanición (Starvation). Un proceso que necesite varios recursos que
sean muy solicitados puede tener que esperar indefinidamente mientras al menos uno de los
recursos que necesita esté asignado siempre a algún otro proceso. En esta segunda
condición, retener y esperar, si se puede impedir que los procesos que tienen los recursos
esperen la llegada de más recursos podemos erradicar los estancamientos. La solución que
exige que todos los procesos soliciten todos los recursos que necesiten a un mismo tiempo y
bloqueando el proceso hasta que todos los recursos puedan concederse simultáneamente
resulta ineficiente por dos factores:
o En primer lugar, un proceso puede estar suspendido durante mucho tiempo,
esperando que concedan todas sus solicitudes de recursos, cuando de hecho podría
haber avanzado con sólo algunos de los recursos.
o Y en segundo lugar, los recursos asignados a un proceso pueden permanecer sin
usarse durante periodos considerables, tiempo durante el cual se priva del acceso a
otros procesos.

No expropiación. La tercera condición necesaria es que no debe de existir el derecho de


desasignar recursos que han sido previamente asignados. Con el fin de asegurar que esta
condición no se cumpla, el siguiente protocolo puede ser usado: Si un proceso que está
reteniendo algunos recursos solicita otro recurso que no puede ser asignado inmediatamente (es
decir, el proceso tiene que esperar), entonces todos los recursos que tiene este proceso en
espera son desasignados. Entonces, los recursos son liberados implícitamente. Los recursos

Ing. Jorge Orellana A. 44


Sistemas Operativos (2010017)

liberados son agregados a la lista de los recursos por los cuales está esperando el proceso. El
proceso sólo puede volver a ser reinicializado cuando haya obtenido otra vez todos los recursos
que tenía asignados, así como el nuevo recurso que estaba solicitando. Alternativamente si un
proceso solicita algunos recursos, primero verificamos si estos están disponibles. Si es así,
entonces se le asignan. De otro modo, se verifica si están asignados a alguno de los procesos
que están en espera de recursos adicionales. Si es así, los recursos deseados son desasginados
del proceso en espera y asignados al proceso solicitante. De otra manera (no están disponibles,
ni asignados a procesos en espera) el proceso que hace la solicitud debe esperar. Pero,
mientras esta esperando, algunos de sus recursos pueden ser desasignados solamente si estos
son solicitados. Un proceso puede ser reinicializado cuando se le asigna el recurso que había
solicitado y recupera cualquier recurso que haya sido desasignado mientras esperaba. Este
protocolo es utilizado usualmente sobre recursos cuyo estado puede ser fácilmente salvado y
restablecido posteriormente, ejemplo de ellos son los registros del CPU y el espacio en la
memoria. Este no puede ser aplicado a recursos tales como impresoras. Esta tercera condición
(llamada también sin prioridad) es aún menos premisoria que la segunda. Si a un proceso se le
ha asignado la impresora y se encuentra a la mitad de la impresión, y tomamos a la fuerza la
impresora porque no se dispone de una graficadora, se engendraría una confusión.

Espera circular. Con el fin de asegurarse de que la condición de espera circular nunca se
presente, se puede imponer un ordenamiento total sobre todos los tipos de recursos. Esto es,
asignamos a cada tipo de recurso un número entero único, el cual permita comparar dos
recursos y determinar cuándo uno precede al otro en el ordenamiento.
Más formalmente, sea R = {r1, r2,..., rn} el conjunto de tipos de recursos. Puede definirse una
función uno a uno F:R->N, donde N es el conjunto de números naturales. Por ejemplo, si el
conjunto R de tipos de recursos incluye unidades de disco (UDD), unidades de cinta (UDC),
lectoras ópticos (LO) e impresoras (I), entonces la función F puede ser definida como sigue:
F(LO) =1 F(UDD) = 5 F(UDC) = 7 F(I) = 12
Se puede considerar el siguiente protocolo para prevenir interbloqueo:
Cada proceso puede solicitar recursos solamente en un orden creciente de numeración. Esto es
un proceso puede solicitar inicialmente cualquier número de instancias de un tipo de recurso,
digamos ri. Después de esto, el proceso puede solicitar instancias de recursos del tipo rj si y solo
si F(rj) > F(ri). Si varias instancias del mismo tipo de recurso son necesitadas, debe hacerse una
sola petición para todas las instancias. Por ejemplo, usando la función definida anteriormente, un
proceso que quiere usar el lector óptico (LO) y la impresora (I) debe solicitar primero el (LO) y
después la (I).
Alternativamente, se puede requerir simplemente que siempre que un proceso solicite una
instancia del recurso tipo rj, éste tenga que liberar cualquier recurso del tipo ri tal que F(ri) >=
F(rj). Debe notarse que la función F debe definirse de acuerdo al ordenamiento de uso normal de
los recursos en el sistema. Por ejemplo, usualmente se utiliza primero una (UDD) antes que la
(I), por lo cual es razonable definir F(UDD) < F(i).
Esta última condición (espera circular) se puede eliminar en varias formas. Una manera consiste
en simplemente tener una regla que afirme que un proceso sólo tiene derechos a un recurso
único en cualquier momento. Si necesita un segundo recurso debe devolver el primero.
Otra manera de evitar la espera circular es la de ofrecer una numeración global de todos los
recursos. Ahora la regla es que los procesos pueden solicitar recursos siempre y cuando
devuelva el recurso que tenga y el que solicite sea mayor que el que está utilizando.

4.4.2. Evasión de Bloqueos


En vez de restringir la forma o el orden en que los procesos deben solicitar recursos, antes se
chequea que sea seguro otorgar dichos recursos. Es decir, si se presentan las condiciones
suficientes para un interbloqueo, todavía es posible evitarlos por medio de una restricción en la
asignación de los procesos para tratar de buscar estados seguros. Estas restricciones aseguran
que al menos una de las condiciones necesarias para el interbloqueo no pueda presentarse y por
lo tanto, tampoco el interbloqueo.
Otro método para evitar interbloqueo consiste en requerir información adicional sobre cómo se
solicitarán los recursos. Esta información puede ser:

Ing. Jorge Orellana A. 45


Sistemas Operativos (2010017)

• La necesidad máxima de recursos de los procesos que se esta ejecutando.


• La asignación actual de recursos a procesos.
La cantidad actual de instancias libres de cada recurso. Con base a la información que ya se
tiene de la forma y del orden en que se solicitarán los recursos, se puede tomar la decisión de
ejecutar el proceso o si debe esperar. Por lo tanto, la evitación del interbloqueo sólo anticipa la
posibilidad de interbloqueo y asegura que no exista nunca tal posibilidad.

Estados seguros e inseguros


• Un estado de asignación de recursos se considera seguro si en él no hay posibilidad de
interbloqueo. Para que un estado sea seguro, es necesario que los procesos formen una
secuencia segura. Una secuencia segura es una ordenación de los procesos de modo que
los recursos que aún pueden pedir cualquier proceso pueden ser otorgados con los recursos
libres más los recursos retenidos por los demás procesos. Con base a ello, cuando un
proceso realice una solicitud de recursos, el sistema se los concederá sólo en el caso de que
la solicitud mantenga al sistema en un estado seguro.
• Un estado inseguro es aquel en el que puede presentarse un interbloqueo.

Algoritmo del banquero para un sólo recurso Este algoritmo fue ideado por Dijkstra (1965).
Se creó en la forma en que un banquero trata a un grupo de clientes, a quienes les otorga líneas
de crédito. El banquero sabe que no todos los clientes (4) A, B, C y D necesitarán su limite de
crédito máximo de inmediato, de manera que sólo ha reservado 10 unidades en lugar de 22 (total
máximo disponible) para darles servicios (con este ejemplo asumiremos que los clientes son los
procesos, las unidades de crédito son los recursos y el banquero es el sistema operativo). Los
clientes emprenden sus respectivos negocios y solicitan préstamos de vez en cuando, es decir
solicitan recursos. En cierto momento, la situación es como se muestra en el siguiente cuadro:

Este estado es seguro, puesto que con dos unidades restantes, el banquero puede retrasar
todas las solicitudes excepto la de C lo cual permite que C termine y libere sus cuatro recursos.
Con cuatro unidades disponibles, el banquero puede permitir que B ó D tengan las unidades
necesarias. Considerar lo que ocurriría si se otorgara una solicitud de B de una unidad adicional,
analizando esto en el esquema anterior se tendría este nuevo esquema:

Se tendría un estado inseguro, ya que si todos los clientes solicitaran de pronto su máximo
préstamo, el banquero no podría satisfacerlas y se tendría un bloqueo.
Un estado inseguro no tiene que llevar a un bloqueo, puesto que un cliente podría no necesitar
toda su línea de crédito, pero el banquero no puede contar con ese comportamiento.
El algoritmo del banquero consiste entonces en estudiar cada solicitud al ocurrir ésta y ver si su
otorgamiento conduce a un estado seguro. En caso afirmativo, se otorga la solicitud; en caso
contrario, se le pospone. Para ver si un estado es seguro, el banquero verifica si tiene los
recursos suficientes para satisfacer a otro cliente. En caso afirmativo, se supone que estos
préstamos se le volverán a pagar; entonces verifica al siguiente cliente cercano al límite y así
sucesivamente. Si en cierto momento se vuelve a pagar todos los préstamos, el estado es
seguro y la solicitud original debe ser aprobada.

Ing. Jorge Orellana A. 46


Sistemas Operativos (2010017)

Algoritmo del banquero para varios recursos. El algoritmo del banquero de puede generalizar
para el control de varios recursos. Por ejemplo, dado un sistema con cinco procesos, P1 a P5, y
tres tipos de recursos, A, B y C, de los que existen 10, 5 y 7 ejemplares, respectivamente.
Supongamos que en el instante actual tenemos la siguiente situación del sistema:

Se calcula la matriz de necesidad, restando la matriz de Asignacion de la matriz Maximo


requerido.

Se compara el vector disponible con la matriz de necesidad y se cubre los requerimientos de un


proceso, luego se recupera toda la asignacion maxima que junto al saldo se convierte en el
nuevo disponible, como se ilustra a continuacion:

Disponible = (3 3 2)
Se puede atender a P2 o P4, se elige P2 = - (1 2 2)
Nuevo disponible después de la asignación = (2 1 0)
Se recupera todos los recursos asignados a P2 = + (3 2 2)
Nuevo disponible después de recuperar recursos = (5 3 2)

Se puede atender a P4 o P5, se elige P4 = - (0 1 1)


Nuevo disponible después de la asignación = (5 2 1)
Se recupera todos los recursos asignados a P2 = + (2 2 2)
Nuevo disponible después de recuperar recursos) = (7 4 3)

Se puede atender a P3 o P5, se elige P5 = - (4 3 1)


Nuevo disponible después de la asignación = (3 1 2)
Se recupera todos los recursos asignados a P2 = + (4 3 3)
Nuevo disponible después de recuperar recursos = (7 4 5)

Se puede atender a P3 = - (6 0 0)
Nuevo disponible después de la asignación = (1 4 5)
Se recupera todos los recursos asignados a P2 = + (9 0 2)
Nuevo disponible después de recuperar recursos = (10 4 7)

Se puede atender a P1 = - (7 4 3)
Nuevo disponible después de la asignación = (3 0 4)
Se recupera todos los recursos asignados a P2 = + (7 5 3)
Nuevo disponible después de recuperar recursos = (10 5 7)

El orden de atencion de los procesos es P2, P4, P5, P3 y P1; secuencia que permite un estado
seguro de asignacion de recursos.

Ing. Jorge Orellana A. 47


Sistemas Operativos (2010017)

Desventajas del algoritmo del Banquero


El algoritmo del banquero es interesante debido a que proporciona un medio de asignar los
recursos para evitar el interbloqueo. Permite proseguir a los trabajos que en una situación de
prevención de interbloqueo tendría que esperar.

Sin embargo el algoritmo contiene un número de desventajas importantes que pueden hacer que
un diseñador escoja otro enfoque para el problema del estancamiento, las cuales se listan a
continuación:
• El algoritmo requiere que existan un número fijo de recursos asignables. Como los recursos
suelen requerir servicios, bien por avería o bien por mantenimiento preventivo, no podemos
contar con que el número de recursos se mantenga siempre constante.
• El algoritmo requiere que la población de usuarios se mantenga constante. Esto también es
irrazonable. En los sistemas multiprogramación actuales, la población de usuarios cambia
constantemente.
• El algoritmo requiere que el banquero garantice que todas las peticiones serán concedidas
dentro de un intervalo de tiempo finito. Está claro que, en sistemas reales, se necesitan
garantías mucho mayores que ésta.
• El algoritmo requiere que los clientes (es decir, los trabajos) garanticen que los préstamos
van a ser pagados (osea que los recursos van a ser devueltos) dentro de un intervalo de
tiempo finito. También en este caso se necesitan garantías mucho mayores que ésta para
sistemas reales.
• El algoritmo requiere que los usuarios indiquen sus necesidades máximas por adelantado. Al
irse haciendo más dinámica la asignación de recursos resulta cada vez más difícil conocer
las necesidades máximas de un usuario.

4.4.3. Detección de Bloqueos


A diferencia de los algoritmos vistos, para evitar interbloqueos, que se deben ejecutar cada vez
que existe una solicitud, el algoritmo utilizado para detectar circularidad se puede correr siempre
que sea apropiado: cada hora, una vez al día, cuando el usuario note que el rendimiento se ha
deteriorado o cuando se queje un usuario.
Dicho algoritmo se puede explicar utilizando las gráficas de recursos dirigidos y “reduciéndolas”.
Los pasos para reducir una gráfica son los siguientes:

Se intenta ir terminando los procesos hasta que terminen todos


1. Si todos pueden ser terminados, se concluye que no hay interbloqueo
2. Sin no se pueden terminar todos los rpocesos, se concluye que los que no se han podido
terminar provocan interbloqueo.
Para finalizar los procesos se busca un proceso que tenga todos los recursos que necesita. Para
reducir se debe finalizar el proceso y liberara todos sus recursos. Por ejemplo, dada una
situacion sin interbloqueo:

Ing. Jorge Orellana A. 48


Sistemas Operativos (2010017)

Como ya no se pude producir interbloqueo el proceso termina. En el caso siguiente analizaremos


la situacion con interbloqueo:

Ing. Jorge Orellana A. 49


Sistemas Operativos (2010017)

No se puede reducir mas, por lo que existe un interbloqueo.

4.4.4. Recuperación de Bloqueos


Una vez que se detecta el interbloqueo hay que desarmarlo y devolver el sistema a la normalidad
con tanta rapidez como sea posible. Existen varios algoritmos de recuperación, pero todos tienen
una característica en común: requieren por lo menos una víctima, un trabajo consumible, mismo
que, al ser eliminada del interbloqueo, liberará al sistema. Por desgracia, para eliminar la víctima
generalmente hay que reiniciar el trabajo desde el principio o a partir de un punto medio
conveniente.
• El primer método y más simple de recuperación, y el más drástico es terminar los trabajos
que están activos en el sistema y volver a arrancarlos desde el principio.
• El segundo método es terminar sólo los trabajos incluidos en el interbloqueo y solicitar a sus
usuarios que los vuelvan a enviar.
• El tercero es identificar qué trabajos intervienen en el interbloqueo y terminarlos uno por uno
y comprobar la desaparición del bloqueo mutuo después de cada eliminación hasta
resolverlo. Una vez liberado el sistema, se deja que los trabajos restantes terminen su
procesamiento; luego, los trabajos detenidos se inician de nuevo desde el principio.
• El cuarto procedimiento se puede poner en efecto sólo si el trabajo mantiene un registro, una
instantánea de su progreso, de manera que se pueda interrumpir y después continuar sin
tener que reiniciar desde el principio. En general, este método es el preferido para trabajos
de ejecución larga, a fin de ayudarlos a tener una recuperación rápida.
Hasta ahora los cuatro procedimientos anteriores comprenden los trabajos que se encuentran en
interbloqueo. Los dos métodos siguientes se concentran en trabajos que no están en
interbloqueo y los recursos que conservan.
• Uno de ellos, el quinto método de la lista, selecciona un trabajo no bloqueado, retira los
recursos que contiene y los asigna a procesos bloqueados, de manera que pueda
reanudarse la ejecución (esto deshace el interbloqueo).
• El sexto método detiene los trabajos nuevos e impide que entren al sistema, lo que permite
que los que no estén bloqueados sigan hasta su terminación y liberen sus recursos. Con
menos trabajos en el sistema, la competencia por los recursos se reduce, de suerte que los
procesos bloqueados obtienen los recursos que necesitan para ejecutarse hasta su
terminación. Este método es el único aquí listado que no necesita una víctima. No se
garantiza que funcione, a menos que el número de recursos disponibles exceda los
necesarios en por lo menos uno de los trabajos bloqueados para ejecutar (esto es posible
con recursos múltiples).
Deben considerarse varios factores al elegir la víctima para que tengan el efecto menos negativo
sobre el sistema. Los más comunes son:
• La prioridad del trabajo en consideración. Por lo general no se tocan los trabajos de alta
prioridad.
• El tiempo de CPU utilizado por el trabajo. No suelen tocarse los trabajos que están a
punto de terminar.
• La cantidad de tareas en que repercutiría la elección de la víctima.

Ing. Jorge Orellana A. 50


Sistemas Operativos (2010017)

Además, los programas que trabajan con bases de datos también merecen un trato especial. Los
trabajos que están modificando datos no se deben elegir para su terminación, ya que la
consistencia y validez de la base de datos se pondría en peligro. Afortunadamente, los
diseñadores de muchos sistemas de bases de datos han incluido mecanismos complejos de
recuperación, por lo que se minimiza el daño a la base de datos si se interrumpe una transacción
o se termina antes de completarse.

Ing. Jorge Orellana A. 51


Sistemas Operativos (2010017)

Tema V
Administración de Memoria
La memoria es un recurso escaso, y para aprovecharla bien hay que administrarla bien. A pesar
de que la memoria es más barata cada día, los requerimientos de almacenamiento crecen en
proporción similar. Parafraseando la ley de Parkinson, “Los programas se desarrollan para
ocupar toda la memoria disponible para ellos”.
La organización y administración de la memoria principal, memoria primaria, memoria real o
almacenamiento de un sistema ha sido y es uno de los factores más importantes en el diseño de
los S. O. [Deitel] y sus funciones son:
• Llevar un control de las partes de memoria que se están utilizando y de aquellas que no.
• Asignar espacio en memoria a los procesos cuando estos la necesitan y liberarla cuando
estos finalicen.
• Administrar el intercambio entre memoria principal y secundaria durante el swapping.

5.1. JERARQUÍA DE ALMACENAMIENTO


Los programas y datos tienen que estar en la memoria principal para poder ejecutarse o ser
referenciados [Deitel]. Los programas y datos que no son necesarios de inmediato pueden
mantenerse en el almacenamiento secundario.
El almacenamiento principal es más costoso y menor que el secundario pero de acceso más
rápido. La mayor parte de las computadoras tienen una jerarquía de memoria, con una cantidad
pequeña de memoria caché muy rápida, costosa y volátil, algunos megabytes de memoria
principal (RAM) volátil de mediana velocidad y mediano precio, y cientos o miles de megabytes
de almacenamiento en disco lento, económico y no volátil. Corresponde al sistema operativo
coordinar el uso de estas memorias.

• Caché de nivel 1: 8 a 128 kb. empaquetados dentro del chip; por lo mismo, la velocidad
de acceso es de unos pocos nanosegundos.
o L1 DC: (Level 1 date cache) se encarga de almacenar datos usados
frecuentemente y cuando sea necesario volver a utilizarlos, inmediatamente los
utiliza, por lo que se agilizan los procesos.
o L1 IC: (Level 1 instruction cache): se encarga de almacenar instrucciones
usadas frecuentemente y cuando sea necesario volver a utilizarlas,
inmediatamente las recupera, por lo que se agilizan los procesos.
• Caché de nivel 2: 256 Kb a 4Mb, 12-20 ns (L2 viene integrada en el microprocesador, se
encarga de almacenar datos de uso frecuente y agilizar los procesos)
• Caché de nivel 3: hasta 6 Mb, 15-35 ns Con este nivel de memoria se agiliza el acceso a
datos e instrucciones que no fueron localizadas en L1 ó L2. Si no se encuentra el dato
en ninguna de las 3, entonces se accederá a buscarlo en la memoria RAM.
• Memoria RAM: hasta 32 Gb, 60-70ns
• Disco duro. Para almacenamiento estable, y también para extender la RAM de manera
virtual, hasta 2 Tb, 8ms.
• Cinta. 1 a 40 Gb.

La cahe L2 utiliza gran parte del dado del chip, ya que se necesitan millones de transistores para
formar una memoria caché grande; ésta se crea tilizando SRAM (RAM estática) en oposición a la
RAM normal, que es dinámica (DRAM).
Mientras que la DRAM puede crearse utilizado un transistor por bit (además de un condensador),
son necesarios 6 transistores (o más) para crear un bit de SRAM. Por lo tanto, 256 KB de
memoria caché L2 necesitarían más de 12 millones de transistores.

Tanto Intel como AMD no han incluido memoria caché L2 en algunas series para hacer
productos más baratos. Sin embargo, no hay duda de que cuanto mayor es la memoria caché
(tanto L1 como L2), más eficiente será la CPU y mayor será su rendimiento.

Ing. Jorge Orellana A. 52


Sistemas Operativos (2010017)

5.2. ADMINISTRACIÓN DE MEMORIA


Los sistemas de administración de memoria se pueden dividir en dos clases, los que trasladan
procesos entre la memoria y el disco durante la ejecución (intercambio y paginación) y los que no
lo hacen. Estudiaremos primero los segundos.

5.2.1. Monoprogramación sin intercambio ni paginación


La forma más simple de administrar memoria es ejecutando sólo un programa a la vez,
compartiendo la memoria con el sistema operativo. Por ejemplo, MS-DOS. Cuando un usuario
digita un comando, el sistema operativo carga el programa correspondiente en la memoria, y lo
ejecuta. Cuando el programa termina, el sistema operativo solicita un nuevo comando y carga el
nuevo programa en la memoria, sobreescribiendo el anterior.

En la figura se muestran tres variaciones sobre este tema. El sistema operativo puede estar en la
base de la memoria en RAM (memoria de acceso aleatorio), como se muestra en (a), o puede
estar en ROM (memoria sólo de lectura) en la parte superior de la memoria, como en (b), o los
controladores de dispositivos pueden estar en la parte superior de la memoria en una ROM con
el resto del sistema en RAM hasta abajo, como se muestra en (c). Este último modelo es
utilizado por los sistemas MS-DOS pequeños, por ejemplo. En las IBM PC, la porción del sistema
que está en ROM se llama BIOS (Basic Input Output System, sistema básico de entrada salida).

5.2.2. Multiprogramación con particiones fijas


Tener varios procesos en la memoria a la vez implica que cuando un proceso está bloqueado
esperando que termine una E/S, otro puede usar la CPU. Así, la multiprogramación aumenta el

Ing. Jorge Orellana A. 53


Sistemas Operativos (2010017)

aprovechamiento de la CPU. Sin embargo, incluso en computadoras personales hay ocasiones


en las que resulta útil poder ejecutar dos o más programas a la vez.
La forma más fácil de lograr la multiprogramación consiste simplemente en dividir la memoria en
n particiones, posiblemente desiguales, y asignar cada una de las partes a un proceso.
Cuando llega un trabajo, se le puede colocar en la cola de entrada de la partición pequeña que
puede contenerlo. Puesto que las particiones están fijas en este esquema, cualquier espacio de
una partición que un trabajo no utilice se desperdiciará. En la siguiente figura se puede ver un
sistema de particiones fijas y colas de entrada individuales. La desventaja de repartir los trabajos
entrantes en colas distintas se hace evidente cuando la cola de una partición grande está vacía
pero la cola de una partición pequeña está llena.

Particiones de memoria fijas con colas de entrada individuales para cada partición

Una organización alternativa sería mantener una sola cola. Cada vez que se libera una partición,
se selecciona el trabajo más cercano al inicio de la cola que cabe en esa partición, se carga en
dicha partición y ejecuta. Puesto que no es deseable desperdiciar una partición grande en un
trabajo pequeño, una estrategia diferente consiste en examinar toda la cola de entrada cada vez
que se libera una partición y escoger el trabajo más grande que cabe en ella. Este último
algoritmo discrimina contra los trabajos pequeños, considerándolos indignos de recibir toda la
partición, en tanto que usualmente es deseable dar el mejor servicio, y no el peor, a los trabajos
más pequeños (que supuestamente son interactivos).

Particiones de memoria fija con una sola cola de entrada.

5.2.2.1. Relocalización (reubicación) y Protección


Cuando se mantienen diversas tareas en diferentes particiones de memoria se presentan dos
problemas esenciales que es preciso resolver: la reubicación y la protección.
El termino reubicable se refiere a la posibilidad de cargar y ejecutar un programa dado en un
lugar arbitrario de memoria. Si un mismo programa se ejecuta varias veces, no siempre va a
quedar en la misma partición. Es evidente que diferentes trabajos se ejecutarán en diferentes
direcciones. Cuando se enlaza (link) un programa o se lo vuelve ejecutable (es decir, cuando el
programa principal, los procedimientos escritos por el usuario y los procedimientos de biblioteca
se combinan en un solo espacio de direcciones), el enlazador necesita saber en qué dirección de
la memoria va a comenzar el programa. Por ejemplo, supongamos que la primera instrucción es
una llamada a un procedimiento que está en la dirección absoluta 100 dentro del archivo binario
(ejecutable) producido por el enlazador. Si este programa se carga en la partición 1, esa
instrucción saltará a la dirección absoluta 100, que está, dentro del sistema operativo. Lo que se
necesita es una llamada a 100K + 100. Si el programa se le carga en la partición 2, deberá
efectuarse como una llamada a 200K + 100, etc.

Ing. Jorge Orellana A. 54


Sistemas Operativos (2010017)

Una posible solución es modificar realmente las instrucciones en el momento en que el programa
se carga en la memoria. Se suma 100K a cada una de las direcciones de un programa que se
carga en la partición 1, se suma 200K a las direcciones de los programas cargados en la
partición 2, etc. A fin de efectuar la reubicación durante la carga, de esta manera el enlazador
debe incluir en el programa binario una lista o mapa de bits que indique cuáles bytes del
programa son direcciones que deben reubicarse y cuáles con códigos de operación, constantes
u otros elementos que no deben reubicarse. La reubicación durante la carga no resuelve el
problema de la protección. Un programa mal intencionado siempre puede construir una nueva
instrucción y saltar a ella. Dado que los programas en este sistema usan direcciones de memoria
absolutas en lugar de direcciones relativas a un registro, no hay forma de impedir que un
programa construya una instrucción que lea o escriba cualquier byte de la memoria. En los
sistemas multiusuario, no es conveniente permitir que los procesos lean y escriban en memoria
que pertenece a otros usuarios.

Una solución alternativa tanto al problema de reubicación como al de protección consiste en


equipar la máquina con dos registros especiales en hardware, llamados registros de base y de
límite. Cuando se planifica un proceso, el registro de base se carga con la dirección del principio
de su partición, y el registro de límite se carga con la longitud de la partición. A cada dirección de
memoria generada se le suma automáticamente el contenido del registro de base antes de
enviarse a la memoria. De este modo, si el registro de base es de 100K, una instrucción CALL
100 se convierte efectivamente en una instrucción CALL 100K + 100, sin que la instrucción en sí
se modifique. Además, las direcciones se cotejan con el registro de límite para asegurar que no
intenten acceder a memoria fuera de la partición actual. El hardware protege los registros de
base y de límite para evitar que los programas de usuario los modifiquen.

5.2.2.2. Fragmentación
La mayor dificultad con las particiones fijas esta en encontrar una buena relación entre los
tamaños de las particiones y la memoria requerida para los trabajos. La fragmentación de
almacenamiento ocurre en todos los sistemas independientemente de su organización de
memoria. Existen dos tipos de desaprovechamiento de memoria:
a) La fragmentación interna, que consiste en aquella parte de la memoria que no se está
usando pero que es interna a una partición asignada a una tarea; por ejemplo si una tarea
necesita m bytes de memoria para ejecutarse y se le asigna una partición de n bytes (con
n>m), está desaprovechando n-m bytes.

Ing. Jorge Orellana A. 55


Sistemas Operativos (2010017)

b) La fragmentación externa, que ocurre cuando una partición disponible no se emplea porque
es muy pequeña para cualquiera de las tareas que esperan.

La selección de los tamaños de las particiones es una relación entre estos dos casos de
fragmentación. Por ejemplo, 7 Mb asignados a los usuarios se reparten en 7 particiones de 1 Mb,
y la cola de tareas contiene tareas con requerimientos de 400 Kb, 1600 Kb, 300 Kb, 900 Kb, 200
Kb, 500 Kb y 800 Kb. Se ve que hay un trabajo de 1600 Kb que no se podrá ejecutar nunca, los
otros sin embargo si se ejecutaran pero en conjunto desperdiciaran 3044 Kb, casi la mitad de
memoria disponible. Hay, pues en este caso 1024 Kb de fragmentación externa y 3044 Kb de
fragmentación interna. Si se divide la memoria en cuatro particiones de 512 Kb, dos de 1 Mb y
una de 3 Mb, todos los trabajos se ejecutarían sin fragmentación externa y con una pérdida de
memoria en fragmentación interna de 2648 Kb.

5.2.3. Multiprogramación de Partición Variable


Las particiones fijas tienen el inconveniente de que hay que determinar sus mejores tamaños con
el fin de minimizar la fragmentación interna y externa. Pero si se ejecutan un conjunto dinámico
de procesos, será difícil que se encuentre la partición correcta de la memoria. La solución de
este problema está en permitir que los tamaños de las particiones varíen dinámicamente. Los
procesos ocupan tanto espacio como necesitan, pero obviamente no deben superar el espacio
disponible de memoria [Deitel].

No hay límites fijos de memoria, es decir que la partición de un trabajo es su propio tamaño. Se
consideran “esquemas de asignación contigua”, dado que un programa debe ocupar posiciones
adyacentes de almacenamiento. Los procesos que terminan dejan disponibles espacios de
memoria principal llamados “agujeros” o áreas libres:
• Pueden ser usados por otros trabajos que cuando finalizan dejan otros agujeros
menores.
• En sucesivos pasos los agujeros son cada vez más numerosos pero más pequeños, por
lo que se genera un desperdicio de memoria principal.
La combinación de agujeros (áreas libres), consiste en fusionar agujeros adyacentes para formar
uno sencillo más grande. Se puede hacer cuando un trabajo termina y el almacenamiento que
libera tiene límites con otros agujeros.

5.2.3.1. Compresión o Compactación de Almacenamiento


Puede ocurrir que los agujeros (áreas libres) separados distribuidos por todo el almacenamiento
principal constituyan una cantidad importante de memoria:
• Podría ser suficiente (el total global disponible) para alojar a procesos encolados en
espera de memoria.
• Podría no ser suficiente ningún área libre individual.

Ing. Jorge Orellana A. 56


Sistemas Operativos (2010017)

La técnica de compresión de memoria implica pasar todas las áreas ocupadas del
almacenamiento a uno de los extremos de la memoria principal:
• Deja un solo agujero grande de memoria libre contigua.
• Esta técnica se denomina “recogida de residuos”.

Una de las principales desventajas de la compresión es que consume recursos del sistema.

El sistema debe detener todo mientras efectúa la compresión, lo que puede afectar los tiempos
de respuesta.
Implica la relocalización (reubicación) de los procesos que se encuentran en la memoria. La
información de relocalización debe ser de accesibilidad inmediata. Una alta carga de trabajo
significa mayor frecuencia de compresión que incrementa el uso de recursos.

5.2.3.2. Estrategias de Colocación del Almacenamiento (Selección de partición)


Se utilizan para determinar el lugar de la memoria donde serán colocados los programas y datos
que van llegando y se las clasifica de la siguiente manera:
• First Fit Estrategia de primer ajuste: El S.O. revisa en todas las secciones de memoria
libre. Un proceso nuevo es colocado en el primer agujero disponible con tamaño
suficiente para alojarlo. A menos que el tamaño del proceso coincida con el tamaño del
agujero, este sigue existiendo, reducido por el tamaño del proceso.
• Next Fit Estrategia del próximo ajuste: En el primer ajuste, ya que todas las búsquedas
empiezan al comienzo de la memoria, siempre se ocupan más frecuentemente los
agujeros que están al comienzo que los que están al final de la memoria. Este algoritmo
intenta mejorar el desempeño al distribuir sus búsquedas mas uniformemente sobre el
espacio de memoria, manteniendo el registro de que agujero fue el ultimo en ser

Ing. Jorge Orellana A. 57


Sistemas Operativos (2010017)

asignado. La próxima búsqueda empieza en el último agujero asignado y no al comienzo


de la memoria.
• Best Fit Estrategia de mejor ajuste: Este algoritmo revisa la lista completa de agujeros
para encontrar el agujero más pequeño cuyo tamaño es mayor o igual que el tamaño del
proceso.
• Worst Fit Estrategia de peor ajuste: Consiste en colocar un programa en el agujero en el
que quepa de la peor manera, es decir en el más grande posible. El agujero restante es
también grande para poder alojar a un nuevo programa relativamente grande.

5.2.3.3. Intercambio de Almacenamiento (Swapping)


Si en un sistema interactivo no es posible mantener a todos los procesos en memoria, se puede
usar el disco como apoyo para extender la capacidad de la memoria, pasando los procesos
temporalmente a disco y traerlos dinámicamente para que se ejecuten.
Podemos usar dos enfoques de administración de memoria generales, dependiendo (en parte)
del hardware disponible. La estrategia más sencilla, llamada intercambio, consiste en traer a la
memoria cada proceso en su totalidad, ejecutarlo durante un tiempo, y después colocarlo otra
vez en el disco. La otra estrategia, llamada memoria virtual, permite a los programas ejecutarse
aunque sólo estén parcialmente en la memoria principal.
El sistema operativo mantiene una tabla que indica qué partes de la memoria están
desocupadas, y cuáles en uso. Las partes desocupadas son agujeros en la memoria;
inicialmente, toda la memoria es un solo gran agujero. Cuando se crea un proceso o se trae uno
del disco, se busca un agujero capaz de contenerlo, y se pone el proceso allí.
Las particiones ya no son fijas, sino que van cambiando dinámicamente, tanto en cantidad como
en ubicación y tamaño. Además, cuando un proceso es pasado a disco, no hay ninguna garantía
de que vuelva a quedar en la misma

El funcionamiento de un sistema con intercambio se ilustra en la figura. Inicialmente, sólo el


proceso A está en la memoria. Luego se crean o se traen del disco los procesos B y C. A
termina o se intercambia al disco. Luego llega D y B sale. Por último, entra E.

5.2.4. Paginación
El que los procesos tengan que usar un espacio contiguo de la memoria es un impedimento serio
para optimizar el uso de la memoria. Se podría considerar que las direcciones lógicas sean
contiguas, pero que no necesariamente correspondan a direcciones físicas contiguas. Es decir,
dividir la memoria física en bloques de tamaño fijo, llamados marcos (frames), y dividir la
memoria lógica (la que los procesos ven) en bloques del mismo tamaño llamados páginas.

Ing. Jorge Orellana A. 58


Sistemas Operativos (2010017)

Se usa una tabla de páginas para saber en qué marco se encuentra cada página. Obviamente,
se requiere apoyo del hardware. Cada vez que la CPU intenta accesar una dirección, la dirección
se divide en número de página p y desplazamiento d (offset). El número de página se transforma
en el marco correspondiente, antes de accesar físicamente la memoria.

El tamaño de las páginas es una potencia de 2, entre 512 bytes y 8 kb. Si las direcciones son de
m bits y el tamaño de página es 2n, entonces los primeros m-n bits de cada dirección forman p, y
los restantes n forman d. Este esquema es parecido a tener varios pares de registros base y
límite, uno para cada marco.
Cada proceso tiene su propia tabla, cuando la CPU se concede a otro proceso, hay que cambiar
la tabla de páginas a la del nuevo proceso. La paginación en general encarece los cambios de
contexto. La seguridad sale gratis, ya que cada proceso sólo puede accesar las páginas que
están en su tabla de páginas. Hay fragmentación interna, y de media página por proceso, en
promedio. Esto sugeriría que conviene usar páginas chicas, pero eso aumentaría el sobrecosto
de administrar las páginas. En general, el tamaño de página ha ido aumentando con el tamaño
de la memoria física de una máquina típica.
Por ejemplo, en la figura siguiente, el computador genera direcciones de 24 bits, lo que permite
un espacio de direcciones de 16 Mb; se consideran marcos de páginas y páginas de 4 Kb. Un
proceso de 14 kb necesita, pues, cuatro páginas numeradas de 0 a 3 y cada página se le asigna
un marco de página de la memoria física. Para hacer esta asignación se usa una tabla de
páginas, que se construye cuando se carga el proceso y que contiene tantas entradas como
paginas tenga este.

Ing. Jorge Orellana A. 59


Sistemas Operativos (2010017)

La tabla de páginas tiene cuatro entradas. Cada dirección lógica se divide en dos partes: el
número de página, que se emplea como un índice en la tabla de páginas y un desplazamiento
dentro de la página. La dirección lógica 002 0FF se descompone en dos campos: 002, que es el
número de página y 0FF que es el desplazamiento relativo dentro de esta página. De la tabla de
páginas se comprueba que a la pagina 002 le corresponde el marco de pagina 104, luego la
dirección física se construye sustituyendo, en el primer campo en el que se divide la dirección, el
numero de pagina por el marco de pagina correspondiente, resultando así la dirección física 104
0FF.

5.2.4.1. Tablas de páginas


Si las direcciones son de m bits y el tamaño de página es 2n, la tabla de páginas puede llegar a
contener 2m-n entradas. En una CPU moderna, m=32 (y ahora, incluso, 64), y n=12 o 13, lo que
significa que se requerirían cientos de miles de entradas: ya no es posible tener la tabla en
registros.
En la mayor parte de los esquemas de paginación, las tablas de páginas se mantienen en la
memoria, debido a su gran tamaño. Esto puede tener un impacto enorme sobre el rendimiento.
Consideremos, por ejemplo, una instrucción que copia un registro en otro. Si no hay paginación,
esta instrucción sólo hace referencia a la memoria una vez, para obtener la instrucción. Si hay
paginación, se requieren referencias adicionales a la memoria para acceder a la tabla de
páginas. Puesto que la velocidad de ejecución generalmente está limitada por la rapidez con que
la CPU puede obtener instrucciones y datos de la memoria, tener que referirse dos veces a la
tabla de páginas por cada referencia a la memoria reduce el rendimiento a una tercera parte. En
estas condiciones, nadie usaría paginación. Los diseñadores de computadoras han estado al
tanto de este problema desde hace años y han encontrado una solución. Su solución se basa en
la observación de que muchos programas tienden a efectuar un gran número de referencias a un
número pequeño de páginas, y no al revés. Así, sólo se lee mucho una fracción pequeña de las
entradas de la tabla de páginas; el resto apenas si se usa.

Ing. Jorge Orellana A. 60


Sistemas Operativos (2010017)

La solución típica es equipar las computadoras con un pequeño dispositivo de hardware para
transformar las direcciones logicas en físicas sin pasar por la tabla de páginas. El dispositivo,
llamado TLB (buffer de consulta para traducción, en inglés, Translation Lookaside Buffer) o
también memoria asociativa.
La memoria asociativa guarda pares (clave, valor), y cuando se le presenta la clave, busca
simultáneamente en todos sus registros, y retorna, en pocos nanosegundos, el valor
correspondiente. Obviamente, la memoria asociativa es cara; por eso, los TLBs rara vez
contienen más de 64 registros. El TLB forma parte de la unidad de administración de memoria
(MMU), y contiene los pares (página, marco) de las páginas más recientemente accesadas.
Cuando la CPU genera una dirección lógica a la página p, la MMU busca una entrada (p,f) en el
TLB. Si está, se usa marco f, sin acudir a la memoria. Sólo si no hay una entrada para p la MMU
debe accesar la tabla de páginas en memoria (e incorporar una entrada para p en el TLB,
posiblemente eliminando otra).
Por pequeño que sea el TLB, la probabilidad de que la página esté en el TLB (tasa de aciertos)
es alta, porque los programas suelen hacer muchas referencias a unas pocas páginas. Si la tasa
de aciertos es el 90% y un acceso al TLB cuesta 10ns, entonces, en promedio, cada acceso a
memoria costará 87ns. Es decir, sólo un 24% más que un acceso sin paginación. Una Intel
80486 tiene 32 entradas en el TLB; Intel asegura una tasa de aciertos del 98%. En cada cambio
de contexto, hay que limpiar el TLB, lo que puede ser barato, pero hay que considerar un costo
indirecto: si los cambios de contexto son muy frecuentes, la tasa de aciertos se puede reducir.

5.2.5. Segmentación
Cuando se usa paginación, el sistema divide la memoria para poder administrarla, no para
facilitarle la vida al programador. La vista lógica que el programador tiene de la memoria no tiene
nada que ver con la vista física que el sistema tiene de ella. El sistema ve un sólo gran arreglo
dividido en páginas, pero el programador piensa en términos de un conjunto de subrutinas y
estructuras de datos, a las cuales se refiere por su nombre: la función coseno, el stack, la tabla
de símbolos, sin importar la ubicación en memoria, y si acaso una está antes o después que la
otra.
La segmentación es una forma de administrar la memoria que permite que el usuario vea la
memoria como una colección de segmentos, cada uno de los cuales tiene un nombre y un
tamaño (que, además, puede variar dinámicamente). Las direcciones lógicas se especifican
como un par (segmento, desplazamiento).

Ing. Jorge Orellana A. 61


Sistemas Operativos (2010017)

Las direcciones especifican el nombre del segmento y el desplazamiento dentro del segmento;
por ello. El usuario debe especificar cada dirección mediante dos cantidades, el nombre del
segmento y un desplazamiento. Por ejemplo: una dirección del segmento de la tabla de símbolos
es:

[segmento s, elemento 4]

y una dirección del segmento de la función coseno es:

[segmento cos, sentencia 7]

Como se ve, este esquema es conceptualmente muy diferente al de la paginación, donde el


usuario especifica solo una única dirección, que mediante circuitería lógica se divide en un
número de página y un desplazamiento y todo ello sin intervención directa por parte del
programador.
Un segmento es un objeto lógico, conocido por el programador y que lo utiliza como tal. Un
segmento puede tener una determinada estructura de datos (pila, array, variables escalares,
etc.) o un procedimiento o un modulo, pero normalmente no va a contener una mezcla de varios.

5.2.5.1. Implementación
El programa de usuario se compila y el compilador construye automáticamente los segmentos de
acuerdo a la estructura del programa. Por ejemplo, un compilador de Pascal creará segmentos
separados para las variables globales, la pila de llamadas a los procedimientos (para almacenar
parámetros y direcciones de retorno), el código de cada uno de los procedimientos y funciones.
El cargador tomara todos los segmentos y le asignara números de segmento. Por propósito de
reubicación cada uno de los segmentos se compila comenzando por su propia dirección lógica 0.
Las direcciones lógicas, por tanto, constaran de un número de segmento y un desplazamiento
dentro de él.
Es este contexto, el espacio de direcciones es bidimensional, pero la memoria física mantiene su
organización lineal unidimensional, luego es necesario disponer de un mecanismo que convierta
las direcciones lógicas bidimensionales en su dirección física equivalente unidimensional. Esta
asignación se efectúa mediante una tabla de segmentos.

Ing. Jorge Orellana A. 62


Sistemas Operativos (2010017)

En la figura se puede observar el uso de la tabla de segmentos donde se mantienen registrados


los descriptores de cada segmento: la base, correspondiente a la dirección física en que
comienza el segmento (obtenida durante la creación de la partición) y el tamaño del mismo.
El número de segmento, primer parámetro de la dirección lógica, se emplea como un índice
dentro de la tabla de segmentos. La dirección física se obtiene añadiendo el desplazamiento a la
base del segmento. Este desplazamiento no debe sobrepasar el tamaño máximo del segmento,
si superara el tamaño máximo registrado en la tabla se producirá una error de direccionamiento.
La realización de las tablas de segmentos es parecida a las tablas de páginas, teniendo en
cuenta que los segmentos son de diferentes tamaños. Así, una tabla de segmentos que se
mantiene en registros especiales puede ser referenciada muy rápidamente; la suma a la base y
la comparación con el tamaño se puede hacer simultáneamente. Este método puede llevarse a
cabo mientras el numero de segmentos no sea grande, pero cuando este crece la tabla de
segmentos se deja en memoria; en este caso, un registro base de la tabla de segmentos apunta
a la tabla de segmentos; además, como el numero de segmentos usados por un programa
puede variar, también se emplea un registro de la longitud de la tabla de segmentos.
Para una dirección lógica se procede de la siguiente manera:
1) Se comprueba que el numero de segmento es correcto (es decir, que es menor que el
registro de la longitud de la tabla de segmentos)
2) Se suma el número de segmento al valor del registro base de la tabla de segmentos
para obtener la dirección de memoria para la entrada de la tabla de segmentos.
3) Esta entrada se lee de memoria y se procede como antes, es decir, se comprueba que el
desplazamiento no rebasa la longitud del segmento y se calcula la dirección física como
la suma del registro base de la tabla de segmentos y del desplazamiento. Pero al igual
que con la paginación, esto requiere dos referencias a memoria por dirección lógica,
provocando así una disminución en la velocidad del computador.
La solución a este problema es disponer también de una memoria asociativa para retener las
entradas de la tabla de segmentos usadas más recientemente (análogamente a como se hacía
en las páginas) y conseguir rendimientos tan solo un poco peor que si tuviera acceso directo. La
segmentación no produce fragmentación interna, pero si externa, la cual ocurre cuando todos los
bloques de memoria libre son muy pequeños para acomodar a un segmento. En esta situación,
el proceso solo puede esperar a disponer de más memoria, agujeros más grandes, o usar
estrategias de compactación para crear huecos más grandes.

Ing. Jorge Orellana A. 63


Sistemas Operativos (2010017)

Para el proceso de la figura, el espacio de direcciones lógicas está constituido por seis
segmentos situados en memoria según se indica. La tabla de segmentos tiene una entrada para
cada segmento, indicándose la dirección de comienzo del segmento en la memoria física (base)
y la longitud del mismo (tamaño). El segmento 3 empieza en la dirección 1048576 y tiene un
tamaño de 100 kb; si se hace una referencia a la dirección 115000 del segmento 3 se producirá
un error de direccionamiento, puesto que esta dirección supera el tamaño del segmento.

5.2.5.2. Protección y compartición


Una ventaja de la segmentación es la posibilidad de asociar la protección a los segmentos.
Puesto que los segmentos son porciones diferentes del programa definidas semánticamente
(unos segmentos contendrán solo instrucciones mientras que otros datos), todas las entradas al
segmento se usan de la misma manera. De esta forma, los segmentos de código se pueden
definir de solo lectura, mientras que un segmento de datos se puede definir de lectura/escritura.
La segmentación también facilita la compartición de código o datos entre varios procesos. Un
ejemplo usual es el de las librerías compartidas; así, en equipos con sistemas gráficos se suele
tener librerías graficas muy grandes que se compilan casi en todos los programas; en un sistema
segmentado, la librería grafica se puede situar en un segmento y compartirse entre varios
procesos, sin necesidad de tenerla en el espacio de direcciones de cada proceso.
Los segmentos son compartidos cuando la entrada a la tabla de segmentos de dos procesos
apunta a la misma posición física. De esta forma, cualquier información se puede compartir si se
define en un segmento.

5.2.6. Paginación - Segmentación


La paginación y la segmentación son esquemas diferentes de gestión de la memoria cuyas
principales características se pueden resumir en los siguientes puntos:
a) El programador no necesita saber que está utilizando la paginación; pero si se dispone
de segmentación si necesita saberlo
b) Mientras que la paginación sigue un esquema lineal, con un único espacio de
direcciones, la segmentación proporciona un espacio bidimensional con muchos
espacios de direcciones.
c) En la paginación no se distingue el código de los datos. En cambio, en la segmentación
si se distinguen (tienen segmentos diferentes), pudiéndose proteger de manera
independiente.
d) La segmentación facilita la compartición de código y datos, así como el uso de
estructuras de datos de tamaños fluctuantes.
e) Ambas tienen en común que el espacio total de direcciones puede exceder el tamaño de
la memoria física.

Ing. Jorge Orellana A. 64


Sistemas Operativos (2010017)

De forma absoluta no se puede decir que una técnica sea superior a otra, por ellos hay sistemas
que combinan los dos métodos para recoger lo mejor de ambos.
Una técnica muy extendida consiste en usar la segmentación desde el punto de vista del usuario
pero dividiendo cada segmento en páginas de tamaño fijo para su ubicación. Esta técnica es
adecuada en el caso de que los segmentos sean muy grandes y resuelve un grave
inconveniente, o sea casi imposible mantenerlos en su totalidad dentro de la memoria.

El principio de la combinación de la segmentación con la paginación se muestra en la figura, bajo


este esquema se elimina la fragmentación externa y se hace más fácil el problema de la
ubicación ya que cualquier marco desocupado se puede asignar a una página. Se puede ver en
la figura que la principal diferencia con la segmentación pura es que la entrada a la tabla de
segmentos no contiene la dirección base del segmento, sino la dirección base de la tabla de
páginas para este segmento. El desplazamiento del segmento se divide en un número de página
y un desplazamiento dentro de ella. El número de página se usa como índice de la tabla de
páginas para obtener el número del marco de página. Este último se combina con el
desplazamiento de página para obtener la dirección física.
Cada segmento tiene su propia tabla de páginas. Sin embargo, cada segmento tiene una
longitud, dada en la tabla de segmentos, de forma que el tamaño de la tabla de páginas no tiene
que ser el máximo, sino un tamaño en función de la longitud del segmento. De esta forma, en
promedio, se tiene una fragmentación interna de media página por segmento.
Consecuentemente, se ha eliminado la fragmentación externa, pero introduciendo fragmentación
interna y aumentando el espacio de tablas.

5.3. Memoria Virtual

Hace muchos años las personas enfrentaron por primera vez programas que eran demasiado
grandes para caber en la memoria disponible. La solución que normalmente se adoptaba era
dividir el programa en fragmentos, llamados superposiciones (overlay). Algunos sistemas de
superposición eran muy complejos, pues permitían varias superposiciones en la memoria a la
vez. Las superposiciones se mantenían en disco y el sistema operativo las intercambiaba con la
memoria dinámicamente, según fuera necesario.
Aunque el trabajo real de intercambiar las superposiciones corría por cuenta del sistema, la tarea
de dividir el programa en fragmentos tenía que ser efectuada por el programador. La división de
programas grandes en fragmentos modulares pequeños consumía tiempo y era tediosa. No pasó
mucho tiempo antes de que a alguien se le ocurriera una forma de dejar todo el trabajo a la
computadora.

Ing. Jorge Orellana A. 65


Sistemas Operativos (2010017)

El método que se inventó (Fotheringham, 1961) se conoce ahora como memoria virtual. La idea
en que se basa la memoria virtual es que el tamaño combinado del programa, los datos y la pila
puede exceder la cantidad de memoria física disponible para él. El sistema operativo mantiene
en la memoria principal las partes del programa que actualmente se están usando, y el resto en
el disco. Por ejemplo, un programa de 16M puede ejecutarse en una máquina de 4M si se
escogen con cuidado los 4M que se mantendrán en la memoria en cada instante, intercambiando
segmentos del programa entre el disco y la memoria según se necesite [Tanembaum].
La memoria virtual también puede funcionar en un sistema de multiprogramación, manteniendo
segmentos de muchos programas en la memoria a la vez. Mientras un programa está esperando
que se traiga a la memoria una de sus partes, está esperando E/S y no puede ejecutarse, así
que puede otorgarse la CPU a otro proceso, lo mismo que en cualquier otro sistema de
multiprogramación.
Mucho mejor sería poder extender la memoria de manera virtual, es decir, hacer que el proceso
tenga la ilusión de que la memoria es mucho más grande que la memoria física (o que el trozo
de memoria física que le corresponde, si tenemos multiprogramación). El sistema operativo se
encarga de mantener en memoria física los trozos (páginas) que el proceso está usando, y el
resto en disco. Ya que el disco es barato, podemos tener espacios de direccionamiento enormes.
Los métodos más comunes de implementación son mediante:
o Técnicas de paginación.
o Técnicas de segmentación.
o Una combinación de ambas técnicas.
Las direcciones generadas por los programas en su ejecución no son, necesariamente, aquellas
contenidas en el almacenamiento primario (memoria real), ya que las direcciones virtuales
(lógicas) suelen seleccionarse dentro de un número mucho mayor de direcciones que las
disponibles dentro del almacenamiento primario.
Las estrategias para la administración de sistemas de memoria virtual condicionan la conducta
de los sistemas de almacenamiento virtual que operan según esas estrategias. Se consideran
las siguientes estrategias:

o Estrategias de búsqueda: Tratan de los casos en que una página o segmento deben ser
traídos del almacenamiento secundario al primario.
§ Las estrategias de “búsqueda por demanda” esperan a que se haga referencia a una
página o segmento por un proceso antes de traerlos al almacenamiento primario.
§ Los esquemas de “búsqueda anticipada” intentan determinar por adelantado a qué
páginas o segmentos hará referencia un proceso para traerlos al almacenamiento
primario antes de ser explícitamente referenciados.
o Estrategias de colocación: Tratan del lugar del almacenamiento primario donde se colocará
una nueva página o segmento. Los sistemas toman las decisiones de colocación de una
forma trivial ya que una nueva página puede ser colocada dentro de cualquier marco de
página disponible.
o Estrategias de sustitución o reposición: Tratan de la decisión de cuál página o segmento
desplazar para hacer sitio a una nueva página o segmento cuando el almacenamiento
primario está completamente comprometido.

5.3.1. Estrategias de búsqueda (paginación)

5.3.1.1. Paginación por demanda. Las páginas son cargadas por demanda [Deitel]. No se
llevan páginas del almacenamiento secundario al primario hasta que son referenciadas
explícitamente por un proceso en ejecución.
La memoria virtual se implementa con un poco de ayuda del hardware. Se agrega un bit a la
tabla de páginas, que indica si la página en cuestión es válida o inválida. Válida significa que
está en memoria (en el marco que indica la tabla), e inválida significa que no está.

Ing. Jorge Orellana A. 66


Sistemas Operativos (2010017)

El acceso a las páginas válidas funciona igual que antes, pero si la CPU intenta accesar una
página inválida, se produce una falta de página (page fault) que genera un trap al sistema
operativo, quien debe encargarse de ir a buscar esa página al disco, ponerla en la memoria, y
reanudar el proceso. El sistema operativo debe:
o Bloquear al proceso.
o Ubicar un marco libre. Si no hay, tiene que liberar uno.
o Ordenar al controlador de disco que lea la página (de alguna manera, el sistema
operativo debe saber dónde la guardó).
o Cuando se completa la lectura, modificar la tabla de páginas para reflejar que ahora la
página si es válida.
o Pasar el proceso a la cola READY. Cuando le toque ejecutar, no notará ninguna
diferencia entre esta falta de página y una simple expropiación de la CPU.
Con este mecanismo, ya no se necesita superposiciones para ejecutar programas grandes, sólo
se carga en memoria las partes del programa que se usan, reduciendo costos de lectura desde
disco y ya que no es necesario tener el programa completo en memoria para poder ejecutarlo,
se puede aumentar el nivel de multiprogramación.

5.3.1.2. Paginación Anticipada. El S. O. intenta predecir las páginas que un proceso va a


necesitar y a continuación precarga estas páginas cuando hay espacio disponible [Deitel].
Mientras el proceso ejecuta sus páginas actuales, el sistema carga páginas nuevas que estarán
disponibles cuando el proceso las pida, debido a ello, el tiempo de ejecución de un proceso se
puede reducir.

5.3.1.3. Tablas de páginas. Ahora que la memoria lógica puede ser mucho más grande que la
física, se acentúa el problema del tamaño de la tabla de páginas. Con direcciones de 32 bits, la
memoria virtual puede alcanzar los 4GB. Con páginas de 4 KB, se necesita hasta 1M entradas.
Si cada entrada usa 32 bits, entonces la tabla podría pesar 4 MB. Además, cada proceso tiene
su tabla, y esta tabla debe estar siempre en memoria física mientras se ejecuta.
La solución es usar tablas de varios niveles. El 386 usa dos niveles: una dirección lógica de 32
bits se divide en (p1, p2, d), de 10, 10 y 12 bits respectivamente. p1 es un índice a una tabla de
tablas, metatabla, o tabla de primer nivel, que contiene 210=1024 entradas de 32 bits, o sea,
exactamente una página. La entrada correspondiente a p1 en esta tabla indica el marco donde
se encuentra otra tabla (de segundo nivel), donde hay que buscar indexando por p2, el marco
donde está la página que finalmente se necesita.

Ing. Jorge Orellana A. 67


Sistemas Operativos (2010017)

Es indispensable mantener en memoria la tabla de primer nivel. Las otras se tratan como
páginas ordinarias, es decir, pueden estar en disco. Así, al utilizar la propia memoria virtual para
estas páginas, se mantienen en memoria física sólo las más usadas. Sin embargo, se está
agregando un acceso más a la memoria para convertir cada dirección lógica a una física, pero
con un TLB con tasa de aciertos alta el impacto es mínimo.

5.3.1.4. Tablas de páginas invertidas. Las tablas de páginas tradicionales del tipo que hemos
descrito hasta ahora requieren una entradas por cada página virtual, ya que están indizadas por
número de página virtual. Si el espacio de direcciones consta de 232 bytes, con 4096 bytes por
página, se necesitará más de un millón de entradas de tabla. Como mínimo, la tabla de páginas
tendrá que ocupar 4 megabytes. En los sistemas grandes, este tamaño probablemente será
manejable.
Sin embargo, en computadoras de 64 bits, la situación cambia drásticamente. Si el espacio de
direcciones ahora tiene 264 bytes, con páginas de 4K, se necesitara más de 1015 bytes para la
tabla de páginas. Sacar de circulación un millón de gigabytes sólo para la tabla de páginas no es
razonable. Por tanto, se requiere una solución distinta para los espacios de direcciones virtuales
de 64 bits paginados.
Una solución es la tabla de páginas invertida. En este diseño, hay una entrada por marco de
página de la memoria real, no por cada página del espacio de direcciones virtual. Por ejemplo,
con direcciones virtuales de 64 bits, páginas de 4K y 32MB de RAM, una tabla de páginas
invertida sólo requiere 8192 entradas. La entrada indica cuál (proceso, página virtual) está en
ese marco de página.
Aunque las tablas de páginas invertidas ahorran enormes cantidades de espacio, al menos
cuando el espacio de direcciones virtual es mucho más grande que la memoria física, tienen una
desventaja importante: la traducción de virtual a física se vuelve mucho más difícil. Cuando el
proceso n hace referencia a la página virtual p, el hardware ya no puede encontrar la página
física usando p como índice de la tabla de páginas. En vez de ello, debe buscar en toda la tabla
de páginas invertida una entrada (n, p). Además, esta búsqueda debe efectuarse en cada
referencia a la memoria, no sólo cuando hay una falla de página. Examinar una tabla de 8K cada
vez que se hace referencia a la memoria no es la forma de hacer que una máquina sea
vertiginosamente rápida.
La solución a este dilema es usar el TLB. Si el TLB puede contener todas las páginas de uso
pesado, la traducción puede efectuarse con tanta rapidez como con las tablas de páginas
normales. Sin embargo, si ocurre una falla de TLB, habrá que examinar la tabla de páginas

Ing. Jorge Orellana A. 68


Sistemas Operativos (2010017)

invertida. Si se usa una tabla de dispersión como índice de la tabla de páginas invertida, esta
búsqueda puede hacerse razonablemente rápida.

5.3.2. Algoritmos de Sustitución (reposición) de Páginas


Cuando ocurre una falla de página (si un proceso accesa una página inválida), el sistema
operativo tiene que escoger la página que sacará de la memoria para que pueda entrar la nueva
página. Si la página que se eliminará fue modificada mientras estaba en la memoria, se debe
reescribir en el disco a fin de actualizar la copia del disco, pero si no fue así (si la página
contenía texto de programa), la copia en disco ya estará actualizada y no será necesario
reescribirla. La nueva página simplemente sobreescribe la que está siendo desalojada.
Si bien sería posible escoger una página al azar para ser desalojada cuando ocurre una falla de
página, el rendimiento del sistema es mucho mejor si se escoge una página que no se usa
mucho. Si se elimina una página de mucho uso, probablemente tendrá que traerse pronto a la
memoria otra vez, aumentando el gasto extra. Se ha trabajado mucho sobre el tema de los
algoritmos de reemplazo de páginas, tanto teórica como experimentalmente. A continuación
describimos algunos de los algoritmos más importantes.

5.3.2.1. Algoritmo de sustitución de páginas óptimo


El mejor algoritmo de reemplazo de páginas posible es fácil de describir pero imposible de
implementar.
En el momento en que ocurre una falla de páginas, algún conjunto de páginas está en la
memoria. A una de estas páginas se hará referencia en la siguiente instrucción (la página que
contiene esa instrucción). Otras páginas podrían no necesitarse sino hasta 10, 100 o tal vez
1000 instrucciones después. Cada página puede rotularse con el número de instrucciones que
se ejecutarán antes de que se haga referencia a esa página.
El algoritmo de reemplazo de páginas óptimo simplemente dice que se debe eliminar la página
que tenga el rótulo más alto. Si una página no se va a usar sino hasta después de 8 millones de
instrucciones y otra página no se usará sino hasta después de 6 millones de instrucciones, el
desalojo de la primera postergará la falla de página que la traerá de nuevo a la memoria lo más
lejos hacia el futuro que es posible. Las computadoras, al igual que las personas, tratan de
aplazar los sucesos desagradables el mayor tiempo que se puede. El único problema con este
algoritmo es que no se puede poner en práctica. En el momento en que ocurre la falla de página,
el sistema operativo no tiene manera de saber cuándo se hará referencia a cada una de las
páginas.

5.3.2.2. Algoritmo de sustitución de páginas de primera que entra, primera que sale (FIFO)
Consiste en reemplazar la página más vieja (la que lleva más tiempo en memoria física). Es
simple, pero no es bueno, ya que la página más antigua no necesariamente es la menos usada.
Además sufre de la Anomalía de FIFO (Belady, Nelson y Shedler descubrieron que con la
reposición FIFO, ciertos patrones de referencias de páginas causan más fallos de páginas
cuando se aumenta el número de marcos (celdas) de páginas asignados a un proceso).
Para ilustrar su funcionamiento, consideremos un supermercado que tiene suficientes anaqueles
para exhibir exactamente k productos distintos. Un día, alguna compañía introduce un nuevo
alimento: yogurt orgánico liofilizado instantáneo que puede reconstituirse en un homo de
microondas. Su éxito es inmediato, así que nuestro supermercado finito tiene que deshacerse de
un producto viejo para poder tener el nuevo en exhibición. Una posibilidad consiste en encontrar
el producto que el supermercado ha tenido en exhibición durante más tiempo (es decir, algo que
comenzó a vender hace 120 años) y deshacerse de él bajo el supuesto de que a nadie le
interesa ya. En efecto, el supermercado mantiene una lista enlazada de todos los productos que
vende en el orden en que se introdujeron. El nuevo se coloca al final de la lista; el que está a la
cabeza de la lista se elimina [Tanembaum].
Como algoritmo de reemplazo de páginas, puede aplicarse la misma idea. El sistema operativo
mantiene una lista de todas las páginas que están en la memoria, siendo la página que está a la
cabeza de la lista la más vieja, y la del final, la más reciente. Cuando hay una falla de página, se
elimina la página que está a la cabeza de la lista y se agrega la nueva página al final. Cuando

Ing. Jorge Orellana A. 69


Sistemas Operativos (2010017)

FIFO se aplica a una tienda, el producto eliminado podría ser cera para el bigote, pero también
podría ser harina, sal o mantequilla.
En la siguiente figura, se considera la secuencia de acceso a las paginas (8, 1, 2, 3, 1, 4, 1, 5, 3,
4, 1, 4). Tiene 3 marcos de página. Si inicialmente la memoria esta vacía, las primeras tres
transferencias causaran fallos de pagina, que provocaran que se traigan las tres paginas (8,1, 2)
a la memoria. Para la siguiente referencia no se tiene espacio y se tendrá que elegir una pagina
para sustituir, se elige la 8 que fue la primera que se cargo en memoria. Cuando se accede a la
1, esta ya esta en memoria y no hay que hacer nada, pero al referenciar la pagina 4, se sustituye
la 1 y asi sucesivamente.

5.3.2.3. Algoritmo de sustitución de páginas Menos – Recientemente – Usada (LRU)


Esta estrategia selecciona para ser reemplazada la página que no ha sido usada durante el
mayor período de tiempo. Se basa en la heurística de que el pasado reciente es un buen
indicador del futuro próximo.
Requiere que cada página reciba un sello de tiempo cada vez que se referencia:
Puede significar una sobrecarga adicional importante.
No se implementa frecuentemente.
La página seleccionada para reemplazo podría ser la próxima en ser requerida, por lo que habría
que paginarla de nuevo al almacenamiento principal casi de inmediato.

5.3.2.4. Algoritmo de sustitución de páginas Menos - Frecuentemente - Usada (LFU)


En este algoritmo interesa la intensidad de uso que haya tenido cada página. La página que será
reemplazada es aquella que ha sido usada con menos frecuencia o que ha sido referida con
menos intensidad. El inconveniente es que se puede seleccionar fácilmente para su reposición la
página equivocada; por ejemplo la página de uso menos frecuente puede ser la página de
entrada más reciente al almacenamiento principal, y por lo tanto existe una alta probabilidad de
que sea usada de inmediato.

5.3.2.5. Algoritmo de sustitución de páginas No Usada - Recientemente (NUR)


Presupone que las páginas que no han tenido uso reciente tienen poca probabilidad de ser
usadas en el futuro próximo y pueden ser reemplazadas por otras nuevas. Es deseable
reemplazar una página que no ha sido cambiada mientras estaba en el almacenamiento
primario. La estrategia NUR se implementa con la adición de dos bits de hardware por página:

Ing. Jorge Orellana A. 70


Sistemas Operativos (2010017)

Bit referenciado:
= 0 si la página no ha sido referenciada.
= 1 si la página ha sido referenciada.
Bit modificado (también llamado “bit sucio”):
= 0 si la página no ha sido modificada.
= 1 si la página ha sido modificada.
La selección de la página que será reemplazada comienza buscando una página que no ha sido
referenciada, pero si no la encuentra habrá que reemplazar una página que ha sido referenciada.
Si una página ha sido referenciada se comprueba si ha sido modificada o no:
Si no ha sido modificada se la reemplaza: Su reposición representa menos sobrecarga que la de
una página modificada, ya que debería grabarse de nuevo en el almacenamiento secundario.
Si no se encuentra una página que no ha sido modificada será reemplazada una página
modificada.
Con el transcurso del tiempo la mayoría de los bits referenciados serán activados:
Se pierde la capacidad para distinguir las páginas más deseables para ser reemplazadas.
Para evitarlo se ajustan periódicamente todos los bits referenciados a “0”:
Se logra un nuevo inicio.
Se vuelve vulnerable al reemplazo aún a las páginas activas, pero solo brevemente, mientras se
reajustan los bits.
Los “bits modificados” no se ajustan periódicamente según esta estrategia.

5.3.2.6. El algoritmo de sustitución de páginas de segunda oportunidad


Una modificación sencilla de FIFO que evita el problema de desalojar una página muy utilizada
consiste en inspeccionar el bit R de la página más vieja. Si es 0, sabremos que la página,
además de ser vieja, no ha sido utilizada recientemente, así que la reemplazamos de inmediato.
Si el bit R es 1, se apaga el bit, se coloca la página al final de la lista de páginas, y se actualiza
su tiempo de carga como si acabara de ser traída a la memoria. Luego continúa la búsqueda.

El funcionamiento de este algoritmo, llamado de segunda oportunidad, se muestra en la figura


donde se ve que se mantienen las páginas de la A a la H en una lista enlazada ordenadas según
el momento en que se trajeron a la memoria.
Supongamos que ocurre una falla de página en el tiempo 20. La página más antigua es A, que
llegó en el tiempo 0, cuando se inició el proceso. Si A tiene el bit R apagado, es desalojada de la
memoria, ya sea escribiéndose en el disco (si está sucia) o simplemente abandonándose (si está
limpia). En cambio, si el bit R está encendido, A se coloca al final de la lista y su "tiempo de
carga" se ajusta al tiempo actual (20).

También se apaga su bit R. La búsqueda de una página apropiada continúa con B. Lo que hace
el algoritmo de segunda oportunidad es buscar una página vieja a la que no se, haya hecho
referencia en el intervalo de reloj anterior. Si se ha hecho referencia a todas las páginas, este
algoritmo pasa a ser FIFO puro. Específicamente, imaginemos que todas las páginas de la
primera figura tienen su bit R encendido. Una por una, el sistema operativo pasará' páginas al
final de la lista, apagando el bit R cada vez que anexa una página al final de la lista. Tarde o
temprano, regresará a la página A, que ahora tiene su bit R apagado. En este punto, A será
desalojada. Así, el algoritmo siempre termina.

5.3.2.7. El algoritmo de sustitución de páginas por reloj


Aunque el algoritmo de segunda oportunidad es razonable, es innecesariamente eficiente porque
constantemente cambia de lugar páginas dentro de su lista. Un enfoque mejor consiste en
mantener todas las páginas en una lista circular con forma de reloj. Una manecilla apunta a la
página más vieja.

Ing. Jorge Orellana A. 71


Sistemas Operativos (2010017)

Cuando ocurre una falla de página, se inspecciona la página a la que apunta la manecilla. Si su
bit R es 0, se desaloja la página, se inserta la nueva página en el reloj en su lugar, y la manecilla
avanza una posición. Si R es 1, se pone en 0 y la manecilla se avanza a la siguiente página.
Este proceso se repite hasta encontrar una página con R = 0. No resulta sorprendente que este
algoritmo se llame de reloj. La única diferencia respecto al de segunda oportunidad es la
implementación.

Ing. Jorge Orellana A. 72


Sistemas Operativos (2010017)
 

Tema VI
Sistema de Archivos
Todas las aplicaciones de computadora necesitan almacenar y recuperar información [Dietel], y las
condiciones esenciales para el almacenamiento de información a largo plazo son:

• Debe ser posible almacenar una cantidad muy grande de información. Mientras un proceso se
está ejecutando, puede almacenar una cantidad de información limitada dentro de su propio espacio
de direcciones. Sin embargo, la capacidad de almacenamiento está restringida al tamaño del espacio
de direcciones virtual. En el caso de algunas aplicaciones, este tamaño es adecuado, pero en el de
otras, dicho tamaño resulta excesivamente pequeño.
• La información debe sobrevivir a la conclusión del proceso que la utiliza. Al mantener la
información dentro del espacio de direcciones de un proceso, sucede que cuando el proceso termina
la información se pierde. En muchas aplicaciones (como las bases de datos), la información debe
retenerse durante semanas, meses, o incluso eternamente. No es aceptable que la información
desaparezca cuando el proceso que la está usando termina. Además, la información no debe
perderse cuando una caída de la computadora termina el proceso.
• Debe ser posible que varios procesos tengan acceso concurrente a la información. En muchos
casos es necesario que múltiples procesos accedan a la información al mismo tiempo.

La solución es el almacenamiento de la información en discos y otros medios externos en unidades


llamadas archivos. Los archivos deben ser persistentes, es decir, que no deben verse afectados por la
creación o terminación de un proceso.

El Sistema de Archivos es la parte del sistema de administración del almacenamiento responsable,


principalmente, de la administración de los archivos del almacenamiento secundario. Es la parte del S.
O. responsable de permitir compartir controladamente la información de los archivos. Sus funciones son:
• Los usuarios deben poder crear, modificar y borrar archivos.
• Se deben poder compartir los archivos de una manera cuidadosamente controlada (acceso de
lectura, acceso de escritura, acceso de ejecución, etc.).
• Se debe poder estructurar los archivos de la manera más apropiada a cada aplicación.
• Los usuarios deben poder ordenar la transferencia de información entre archivos.
• Se deben proporcionar posibilidades de respaldo y recuperación para prevenir contra pérdida
accidental y destrucción maliciosa de información.
• Se debe poder referenciar a los archivos mediante Nombres Simbólicos, brindando
independencia de dispositivos.
• En ambientes sensibles, el sistema de archivos debe proporcionar posibilidades de cifrado y
Descifrado.
• Se debe brindar una interface favorable al usuario. Debe suministrar una visión lógica de los
datos y de las funciones que serán ejecutadas, en vez de una visión física. El usuario no debe
tener que preocuparse por:
o Los dispositivos particulares.
o Dónde serán almacenados los datos.
o El formato de los datos en los dispositivos.
o Los medios físicos de la transferencia de datos hacia y desde los dispositivos.
6.1. Archivos
Es una colección o conjunto de datos relacionados lógicamente. Se considerará el punto de vista del
usuario. Los archivos son un mecanismo de abstracción; proporcionan una forma de almacenar
información en el disco y leerla después. Esto debe hacerse de tal manera que el usuario no tenga que
ocuparse de los detalles de cómo y dónde se almacena la información, ni de cómo funcionan realmente
los discos.

6.1.1. Nombre de los Archivos


Cuando un proceso crea un archivo, le asigna un nombre y cuando termina, el archivo sigue existiendo y
otros procesos pueden acceder a él utilizando su nombre. Las reglas exactas para nombrar archivos
Ing. Jorge Orellana A.   73  
 
 
Sistemas Operativos (2010017)
 

varían un tanto de un sistema a otro, pero todos los sistemas operativos permiten cadenas de uno a
ocho caracteres como nombres de archivo válidos. En muchos casos se permiten también dígitos y
caracteres especiales. Muchos sistemas de archivos reconocen nombres de hasta 255 caracteres.
Algunos sistemas de archivos distinguen entre las letras mayúsculas y minúsculas, y otros no. UNIX
pertenece a la primera categoría; MS-DOS cae en la segunda. Muchos sistemas operativos reconocen
nombres de archivo de dos partes, las cuales se separan con un punto. La parte que sigue al punto se
denomina extensión de archivo y, por lo regular, indica algo acerca del archivo. En MS-DOS, por
ejemplo, los nombres de archivo tienen de 1 a 8 caracteres, más una extensión opcional de 1 a 3
caracteres. En UNIX, el tamaño de la extensión, si la hay, es decisión del usuario, y un archivo incluso
puede tener dos o más extensiones.
En algunos casos, las extensiones de archivo no son más que convenciones y su uso no es obligatorio.
Un archivo llamado archivo.txt con toda probabilidad es algún tipo de archivo de texto, pero ese nombre
sirve más como recordatorio para el propietario que para comunicar alguna información específica a la
computadora. Por otro lado, un compilador de C podría exigir que los archivos que va a compilar
terminen con .c, y podría negarse a compilarlos si no es así.

6.1.2. Estructura de un Archivo


Los archivos se pueden estructurar de varias maneras, las más comunes son [Tanenbaum]:

• Secuencia de bytes (a). El archivo es una serie no estructurada de bytes. El sistema operativo no
sabe (ni le importa) qué contiene el archivo; lo único que ve es bytes. Cualquier significado que tenga
deberán imponerlo los programas en el nivel de usuario. Tanto UNIX como MS-DOS adoptan este
enfoque. Windows usa básicamente el sistema de archivos de MS-DOS, agregándole un poco de
azúcar sintáctica (nombres de archivo largos), así que casi todo lo que se diga acerca de MS-DOS
también aplica a WINDOWS.

• Secuencia de registros (b). Un archivo es una secuencia de registros de longitud fija, cada uno con
cierta estructura interna. La idea de que un archivo es una secuencia de registros se apoya en el
concepto de que la operación de lectura devuelve un registro y que la operación de escritura
sobreescribe o anexa un registro. En el pasado, cuando existía la tarjeta perforada de 80 columnas,
muchos sistemas operativos tenían archivos compuestos por registros de 80 caracteres, que eran
imágenes de tarjetas. Estos sistemas también daban soporte a archivos con registros de 132
caracteres, pensados para la impresora de líneas. Los programas leían las entradas en unidades de
80 caracteres y las escribían en unidades de 132 caracteres Un sistema (antiguo) que consideraba
los archivos como secuencias de registros de longitud fija era CP/M. Éste utilizaba registros de 128
caracteres. Hoy día, la idea de un archivo como una secuencia de registros de longitud fija es
prácticamente cosa del pasado.

• Árbol (c). Un archivo consiste en un árbol de registros, no necesariamente todos de la misma


longitud, cada uno de los cuales contiene un campo de llave en una posición fija dentro del registro.
El árbol está ordenado según el campo de llave, a fin de poder buscar rápidamente una llave en
particular. La operación básica aquí no es obtener el siguiente registro, aunque eso también es
posible, sino obtener el registro que tiene una llave dada. En el caso del archivo de zoológico de la
figura (c), podríamos pedirle al sistema que obtenga el registro cuya llave es pony, por ejemplo, sin
tener que preocuparnos por su posición exacta en el archivo. Además, es posible agregar nuevos

Ing. Jorge Orellana A.   74  


 
 
Sistemas Operativos (2010017)
 

registros al archivo dejando que el sistema operativo, y no el usuario, decida dónde deben colocarse.
Este tipo de archivo obviamente es muy distinto de los flujos de bytes no estructurados que se
emplean en UNIX y MS-DOS, pero se utiliza ampliamente en las macrocomputadoras que todavía se
usan en algunas aplicaciones comerciales de procesamiento de datos.

6.1.3. Tipos de Archivos


Muchos sistemas operativos reconocen varios tipos de archivos. UNIX y MS-DOS, por ejemplo, tienen
archivos normales y directorios (folders). UNIX también tiene archivos especiales por caracteres y por
bloques. Los archivos regulares son los que contienen información del usuario. Los directorios son
archivos de sistema que sirven para mantener la estructura del sistema de archivos. Los archivos
especiales por caracteres están relacionados con entrada/salida y sirven para modelar dispositivos E/S
en serie como las terminales, impresoras y redes. Los archivos especiales por bloques sirven para
modelar discos.
Los archivos normales generalmente son archivos ASCII o bien archivos binarios. Los archivos ASCII
consisten en líneas de texto. En algunos sistemas cada línea termina con un carácter de retorno de
carro; en otros se emplea el carácter de salto de línea. Ocasionalmente se requieren ambos. Las líneas
no tienen que tener todas la misma longitud. La gran ventaja de los archivos ASCII es que pueden
exhibirse e imprimirse tal como están, y se pueden editar con un editor de textos normal. Además, si una
gran cantidad de programas usan archivos ASCII como entradas y salidas, es fácil conectar la salida de
un programa a la entrada del otro, como en tuberías (pipes) de shell. Otros archivos son binarios, lo que
simplemente significa que no son archivos ASCII. Si listamos estos archivos en una impresora,
obtendremos un listado incomprensible lleno de lo que parece ser basura. Por lo regular, estos archivos
tienen alguna estructura interna.

6.1.4. Atributos de un archivo


Todo archivo tiene un nombre y ciertos datos. Además, todos los sistemas operativos asocian
información adicional a cada archivo, por ejemplo, la fecha y hora de creación del archivo, y su tamaño.
Llamamos a estos datos adicionales atributos del archivo. La lista de atributos varía considerablemente
de un sistema a otro. Los atributos de un archivo son: el nombre, el tipo, la localización (donde se ubica),
derechos de acceso, tiempo de creación/acceso/modificación, UID del creador, etc.

6.1.5. Operaciones con archivos


Los archivos existen para almacenar información que posteriormente se pueda recuperar. Los diferentes
sistemas ofrecen distintas operaciones de almacenamiento y recuperación. Podemos pensar en un
archivo como en un tipo abstracto de datos, al que se pueden aplicar operaciones como: Abrir, Cerrar,
Crear, Destruir, Copiar, Renombrar, Listar. A los ítems que forman el archivo (bytes, registros, etc.) se
Ing. Jorge Orellana A.   75  
 
 
Sistemas Operativos (2010017)
 

pueden aplicar operaciones como: Leer, Modificar, Agregar (al final), Insertar, Borrar. Estas operaciones
están implementadas a través de llamadas al sistema (systems calls). Las llamadas al sistema más
comunes relacionadas con archivos son:
CRÉATE. El archivo se crea sin datos. El propósito de la llamada es anunciar que va a haber un archivo
y establecer algunos de los atributos.
DELETE. Cuando el archivo ya no se necesita, es preciso eliminarlo para desocupar el espacio en disco.
Siempre hay una llamada al sistema para este fin.
OPEN. Antes de usar un archivo, un proceso debe abrirlo. El propósito de la llamada OPEN es permitir al
sistema que obtenga los atributos y la lista de direcciones de disco y los coloque en la memoria principal
a fin de agilizar el acceso en llamadas posteriores.
CLOSE. Una vez concluidos todos los accesos, los atributos y las direcciones de disco ya no son
necesarios, por lo que se debe cerrar el archivo para liberar el espacio correspondiente en las tablas
internas.
READ. Se leen datos del archivo. Por lo regular, los bytes provienen de la posición actual. El invocador
debe especificar cuántos datos se necesitan y también debe suministrar un buffer para colocarlos.
WRITE. Se escriben datos en el archivo, también, por lo regular, en la posición actual. Si dicha posición
es el final del archivo, el tamaño del archivo aumenta. Si la posición actual está a la mitad del archivo, se
sobreescribe en los datos existentes, que se pierden irremediablemente.
APPEND. Esta llamada es una forma restringida de WRITE que sólo puede agregar datos al final del
archivo. Los sistemas que ofrecen un juego mínimo de llamadas al sistema generalmente no cuentan
con APPEND, pero muchos sistemas ofrecen varias formas de hacer una misma cosa, y a veces
incluyen APPEND.
SEEK. En el caso de archivos de acceso aleatorio, se requiere un método para especificar el lugar del
que deben tomarse los datos. Un enfoque común es tener una llamada al sistema, SEEK, que ajuste el
apuntador a la posición actual haciéndolo que apunte a un lugar específico del archivo. Una vez
efectuada esta llamada, se pueden leer datos de esa posición o escribirlos en ella.
GET ATTRIBUTES. Es frecuente que los procesos necesiten leer los atributos de un archivo para
realizar su trabajo.
SET ATTRIBUTES. Algunos de los atributos pueden ser establecidos por el usuario y modificarse
después de que se creó el archivo.
RENAME. Es común que un usuario necesite cambiar el nombre de un archivo existente.

6.2. Directorios
Los sistemas de archivos casi siempre tienen directorios para organizar los archivos, En muchos
sistemas, son también archivos.

6.2.1. Sistemas de directorio jerárquicos


Un directorio contiene varias entradas (un conjunto de datos) por cada archivo. Por ejemplo cada entrada
puede contener el nombre del archivo, los atributos del archivo, y la dirección de disco donde están
almacenados los datos. Otra posibilidad es que cada entrada de directorio contenga el nombre del
archivo y un apuntador a otra estructura de datos en la que pueden encontrarse los atributos y las
direcciones en disco. Ambos sistemas son de uso generalizado.
Cuando se abre un archivo, el sistema operativo examina su directorio hasta encontrar el nombre del
archivo por abrir, y luego extrae los atributos y las direcciones en disco, ya sea directamente de la
entrada de directorio o de la estructura de datos a la que ésta apunta, y los coloca en una tabla en la
memoria principal. Todas las referencias subsecuentes al archivo utilizan la información que está en la
memoria principal.
El número de direcciones varía de un sistema a otro.
• Directorio Único. El diseño más sencillo es aquel en el que el sistema mantiene un solo directorio
que contiene todos los archivos de todos los usuarios. Si hay muchos usuarios, y éstos escogen los
mismos nombres de archivo (correo y juegos), los conflictos y la confusión resultante pronto harán
inmanejable el sistema. Este modelo se utilizó en los primeros sistemas operativos de
microcomputadoras, pero ya casi nunca se encuentra.

Ing. Jorge Orellana A.   76  


 
 
Sistemas Operativos (2010017)
 

• Un directorio por usuario. Este diseño elimina los conflictos de nombres entre usuarios pero no es
satisfactorio para quienes tienen un gran número de archivos. Es muy común que los usuarios
quieran agrupar sus archivos atendiendo a patrones lógicos. Un profesor, por ejemplo, podría tener
una colección de archivos que juntos constituyan un libro que está escribiendo para un curso, una
segunda colección de archivos que contenga programas que los estudiantes de otro curso hayan
presentado, un tercer grupo de archivos que contenga el código de un sistema avanzado de escritura
de compiladores que él esté construyendo, un cuarto grupo de archivos que contenga propuestas
para obtener subvenciones, así como otros archivos para correo electrónico, minutas de reuniones,
artículos que esté escribiendo, juegos, etc.

Se requiere algún método para agrupar estos archivos en formas flexibles elegidas por el usuario.
• Árbol de directorios. Cada usuario puede tener tantos directorios como necesite para poder
agrupar sus archivo) según patrones naturales. Los directorios contenidos en el directorio raíz,
pertenecen cada uno a un usuario distinto.

6.2.2. Nombres de ruta de Accesos


Cuando el sistema de archivos se organiza en forma de árbol de directorios, se necesita un método para
determinar los nombres de los archivos. Los dos métodos principales son:

Ing. Jorge Orellana A.   77  


 
 
Sistemas Operativos (2010017)
 

• Nombre de Ruta de Acceso Absoluta:


Cada archivo tiene una ruta de acceso absoluta. Consta de la ruta de acceso desde el directorio raíz
hasta el archivo. Los componentes de la ruta de acceso se separan mediante algún caracter llamado
separador. Por ejemplo, la ruta /datos/joa/mailbox indica que el directorio raíz contiene un subdirectorio
“datos”, que a su vez contiene un subdirectorio “joa”, el cual contiene el archivo “mailbox”. En UNIX los
componentes de la ruta se separan con “/”. En MS-DOS y Windows el separador es “\”. Sea cual sea el
carácter empleado, si el primer carácter del nombre de rutas es el separador, la ruta es absoluta.

• Nombre de Ruta de Acceso Relativa:


Se utiliza junto con el concepto de directorio de trabajo o directorio activo (directorio actual). Todos los
nombres que no comiencen en el directorio raíz se toman en relación con el directorio de trabajo. El
nombre absoluto de la ruta de acceso siempre funciona, sin importar cual sea el directorio de trabajo. Un
usuario puede definir un directorio como directorio de trabajo actual, en cuyo caso todos los nombres de
archivo que comiencen en el directorio raíz se tomarán en relación con el directorio con el trabajo. Por
ejemplo si el directorio de trabajo actual es /datos/joa, entonces se podrá hacer referencia al archivo
cuya ruta absoluta es /datos/joa/mailbox simplemente como mailbox. En otras palabras, el comando de
UNIX

cp /datos/joa/mailbox /datos/joa/mailbox.bak
y el comando
cp mailbox mailbox.bak

harán exactamente lo mismo si el directorio de trabajo es /datos/joa. La forma relativa suele ser más
cómoda, pero hace lo mismo que la forma absoluta.
Algunos programas necesitan acceder a un archivo específico sin considerar el directorio de trabajo. En
tal caso, siempre deben usar nombres de ruta absolutos. Por ejemplo, un revisor ortográfico podría
necesitar leer /usr/lib/dictionary para efectuar su trabajo. En este caso, el programa deberá usar el
nombre de ruta absoluto completo porque no sabe cuál será el directorio de trabajo cuando sea
invocado. El nombre de ruta absoluta siempre funciona, sea cual sea el directorio de trabajo.
La mayor parte de los sistemas operativos que manejan un sistema de directorios jerárquico tiene dos
entradas especiales en cada directorio, "." y "..", generalmente pronunciados "punto" y "punto punto".
Punto se refiere al directorio actual; punto punto se refiere a su padre.

6.2.3. Operaciones con directorios


Las llamadas al sistema permitidas para administrar directorios muestran variaciones de un sistema a
otro. A fin de dar una idea de la naturaleza modo de operar de esas llamadas, presentaremos una
muestra (tomada de UNIX).

• CRÉATE. Se crea un directorio, que está vacío con la excepción de punto y punto, punto, que el
sistema (o, en unos pocos casos, el programa mkdir) coloca ahí automáticamente.
• DELETE. Se elimina un directorio. Sólo puede eliminarse un directorio vacío. Un directorio que sólo
contiene punto y punto punto se considera vacío, ya que éstos normalmente no pueden eliminarse.
• OPENDIR. Los directorios pueden leerse. Por ejemplo, para listar todos los archivos de un directorio,
un programa para emitir listados abre el directorio y lee los nombres de los archivos que contiene.
Antes de poder leer un directorio, es preciso abrirlo, de forma análoga a como se abren y leen los
archivos.
• CLOSEDIR. Una vez que se ha leído un directorio, debe cerrarse para liberar espacio de tablas
internas.
• READDIR. Esta llamada devuelve la siguiente entrada de un directorio abierto. Antes, era posible
leer directorios empleando la llamada al sistema READ normal, pero ese enfoque tiene la desventaja
de obligar al programador a conocer y manejar la estructura interna de los directorios. En contraste,
READDIR siempre devuelve una entrada en un formato estándar, sin importar cuál de las posibles
estructuras de directorio se esté usando.
• RENAME. En muchos sentidos, los directorios son iguales que los archivos y podemos cambiar su
nombre tal como hacemos con los archivos.

Ing. Jorge Orellana A.   78  


 
 
Sistemas Operativos (2010017)
 

• LINK. El enlace (linking) es una técnica que permite a un archivo aparecer en más de un directorio.
Esta llamada al sistema especifica un archivo existente y un nombre de ruta, y crea un enlace del
archivo existente al nombre especificado por la ruta. De este modo, el mismo archivo puede aparecer
en múltiples directorios.
• UNLINK. Se elimina una entrada de directorio. Si el archivo que está siendo desvinculado sólo está
presente en un directorio (el caso normal), se eliminará del sistema de archivos. Si el archivo está
presente en varios directorios, sólo se eliminará el nombre de ruta especificado; los demás seguirán
existiendo. En UNIX, la llamada al sistema para eliminar archivos (que ya vimos antes) es, de hecho,
UNLINK.

6.3. Implementación de Sistemas de Archivos


A los usuarios les interesa la forma de nombrar los archivos, las operaciones que pueden efectuarse con
ellos, el aspecto que tiene el árbol de directorios y cuestiones de interfaz por el estilo. A los
implementadores les interesa cómo están almacenados los archivos y directorios, cómo se administra el
espacio en disco y cómo puede hacerse que todo funcione de forma eficiente y confiable. Se deben tener
presentes problemas tales como la fragmentación creciente del espacio en disco lo que ocasiona
problemas de performance al hacer que los archivos se dispersen a través de bloques muy dispersos.
Las técnicas para aliviar el problema de la fragmentación consiste en realizar periódicamente
condensación (defragmentacion) donde se reorganizan los archivos expresamente o automáticamente
según algún criterio predefinido y la recolección de basura o residuos, donde se puede hacer fuera de
línea o en línea, con el sistema activo, según la implementación.

6.3.1. Implementación de archivos


El aspecto clave de la implantación del almacenamiento de archivos es el registro de los bloques
asociados a cada archivo [Deitel]. Podemos modelar el disco duro como una secuencia de bloques, de 0
a N-1, de tamaño fijo (usualmente de 512 bytes), a los cuales tenemos acceso directo, pero, por el
movimiento del brazo es más caro leer dos bloques separados que dos bloques contiguos.
El primer problema consiste en determinar cómo a almacenar los archivos en el disco. Algunos de los
métodos utilizados son los siguientes:

• Asignación contigua o adyacente. El esquema de asignación más sencillo es almacenar cada


archivo como un bloque contiguo de datos en el disco. Así, en un disco con bloques de 1K, a un
archivo de 50K se le asignarían 50 bloques consecutivos. Las principales ventajas son que la
implementación es sencilla porque para saber dónde están los bloques de un archivo basta con
recordar un número( la dirección en disco del primer bloque) y que el rendimiento es excelente
porque es posible leer todo el archivo del disco en una sola operación. Las desventajas son que se
debe conocer el tamaño máximo del archivo al crearlo y que produce una gran fragmentación de los
discos.

• Asignación por lista enlazada. Consiste en guardar cada archivo como una lista enlazada de
bloques de disco. La primera palabra de cada bloque se emplea como apuntador al siguiente. El
Ing. Jorge Orellana A.   79  
 
 
Sistemas Operativos (2010017)
 

resto del bloque se destina a datos. Las ventajas son que es posible utilizar todos los bloques (No se
pierde espacio por fragmentación del disco, excepto por fragmentación interna en el último bloque) y
que el directorio sólo debe registrar el primer bloque de cada archivo (No es necesario declarar el
máximo tamaño que puede llegar a tener un archivo, puesto que puede crecer sin problemas,
mientras queden bloques libres). Las desventajas son que, aunque la lectura secuencial de un
archivo es sencilla, el acceso aleatorio es extremadamente lento y que la cantidad de
almacenamiento de datos en un bloque ya no es una potencia de dos porque el apuntador ocupa
unos cuantos bytes.

• Asignación por lista enlazada empleando un índice. Para eliminar las desventajas del método
anterior, en vez de tener esparcidos los punteros en cada bloque, se pueden agrupar y poner todos
juntos en una tabla o FAT (Tabla de asignación de archivos). En la siguiente figura se muestra un
ejemplo de la tabla con dos archivos. El archivo A usa los bloques de disco 4,7,2,10 y 12, en ese
orden, y el archivo B usa los bloques de disco 6, 3, 11 y 14, en ese orden. Se puede comenzar en el
bloque 4 y seguir la cadena hasta el final. Lo mismo puede hacerse comenzando en el bloque 6. Si
se emplea esta organización, todo el bloque está disponible para datos. Además, el acceso directo
es mucho más fácil. Aunque todavía hay que seguir la cadena para encontrar una distancia dada
dentro de un archivo, la cadena está por completo en la memoria, y puede seguirse sin tener que
consultar el disco. Al igual que con el método anterior, basta con guardar un solo entero (el número
del bloque inicial) en la entrada de directorio para poder localizar todos los bloques, por más grande
que sea el archivo. MS-DOS emplea este método para la asignación en disco. La desventaja es que
toda la tabla debe estar en la memoria todo el tiempo para que funcione. En el caso de un disco
grande con, digamos, 500 000 bloques de 1K (500M), la tabla tendrá 500 000 entradas, cada una de
las cuales tendrá que tener un mínimo de 3 bytes. Si se desea acelerar las búsquedas, se
necesitarán 4 bytes. Así, la tabla ocupará de 1.5 a 2 megabytes todo el tiempo, dependiendo de si el
sistema se optimiza en cuanto al espacio o en cuanto al tiempo. Aunque MS-DOS emplea este
mecanismo, evita manejar tablas muy grandes empleando bloques grandes (de hasta 32K) en los
discos de gran tamaño.

Ing. Jorge Orellana A.   80  


 
 
Sistemas Operativos (2010017)
 

• Nodos-I. Consiste en mantener juntos los punteros de cada archivo, en una tabla llamada nodo-
i (nodo-índice), asociada al archivo, y que se guarda en un bloque como cualquiera (esta tabla
es análoga a la tabla de páginas). Además, en ese bloque se puede guardar información de los
otros atributos del archivo. El directorio sólo contiene un puntero al bloque que contiene la tabla.

Las primeras pocas direcciones de disco se almacenan en el nodo-i mismo, así que en el caso
de archivos pequeños toda la información está contenida en el nodo-i, que se trae del disco a la
memoria principal cuando se abre el archivo. En el caso de archivos más grandes, una de las
direcciones del nodo-i es la dirección de un bloque de disco llamado bloque de indirección
sencilla. Este bloque contiene direcciones de disco adicionales. Si esto todavía no es suficiente,
otra dirección del nodo-i, llamada bloque de indirección doble, contiene la dirección de un bloque
que contiene una lista de bloques de indirección sencilla. Cada uno de estos bloques de
indirección sencilla apunta a unos cuantos cientos de bloques de datos. Si ni siquiera con esto
basta, se puede usar también un bloque de dirección triple. UNIX emplea este esquema. Las
ventajas son que lo único que hay que registrar en el directorio es un puntero al nodo-i, además
que el acceso aleatorio es rápido para archivos pequeños y que el sobrecosto fijo es bajo (no hay
FAT, entradas en directorios pequeñas). Las desventajas es que es relativamente complejo,
además hay mayor sobrecosto variable (cada archivo, por pequeño que sea, necesita al menos
dos bloques) y que el acceso aleatorio es más complicado para archivos grandes (en todo caso,
los bloques con índices pueden guardarse en memoria para agilizar el acceso).

6.3.2. Implementación de directorios


Para leer un archivo, hay que abrirlo. Cuando se abre un archivo, el sistema operativo usa el nombre de
ruta proporcionado por el usuario para localizar la entrada de directorio. Esta entrada proporciona la
Ing. Jorge Orellana A.   81  
 
 
Sistemas Operativos (2010017)
 

información necesaria para encontrar los bloques de disco. Dependiendo del sistema, esta información
puede ser la dirección en disco de todo el archivo (asignación contigua), el número del primer bloque
(ambos esquemas de lista enlazada) o el número del nodo-i.
La función principal del sistema de directorios es transformar el nombre ASCII del archivo en la
información necesaria para localizar los datos.
Un aspecto íntimamente ligado con esto es la posición de almacenamiento de los atributos. Una
posibilidad obvia es almacenarlos directamente en la entrada de directorio. Muchos sistemas hacen
precisamente esto. En el caso de sistemas que usan nodos-i, otra posibilidad es almacenar los atributos
en el nodo-i, en lugar de en la entrada de directorio. Como veremos más adelante, este método tiene
ciertas ventajas respecto a la colocación de los atributos en la entrada de directorio.

Directorios en MS-DOS
La figura muestra una entrada de directorio de MS-DOS, la cual tiene 32 bytes de longitud y contiene el
nombre de archivo, los atributos y el número del primer bloque de disco. El primer número de bloque se
emplea como índice de una tabla del tipo FAT. Siguiendo la cadena, se pueden encontrar todos los
bloques.

En MS-DOS, los directorios pueden contener otros directorios, dando lugar a un sistema de archivos
jerárquico. En este sistema operativo es común que los diferentes programas de aplicación comiencen
por crear un directorio en el directorio raíz y pongan ahí todos sus archivos, con objeto de que no haya
conflictos entre las aplicaciones.

Directorios en UNIX
En la estructura de directorios UNIX, cada entrada contiene sólo un nombre de archivo y su número de
nodo-i. Toda la información acerca del tipo, tamaño, tiempos, propietario y bloques de disco está
contenida en el nodo-i.
Algunos sistemas UNIX tienen una organización distinta, pero en todos los casos una entrada de
directorio contiene en última instancia sólo una cadena ASCII y un número de nodo-i.
Cuando se abre un archivo, el sistema de archivos debe tomar el nombre que se le proporciona y
localizar sus bloques de disco. Consideremos cómo se busca el nombre de ruta /usr/ast/mbox. Lo
primero que hace el sistema de archivos es localizar el directorio raíz. En UNIX su nodo-i está situado en
un lugar fijo del disco. A continuación, el sistema de archivos busca el primer componente de la ruta,
“usr”, en el directorio raíz para encontrar el número de nodo-i del archivo /usr. La localización de un
nodo-i, una vez que se tiene su número es directa, ya que cada uno tiene una posición fija en el disco.
Con la ayuda de este nodo-i, el sistema localiza el directorio correspondiente a /usr y busca el siguiente
componente, “ast”, dentro de él. Una vez que el sistema encuentra la entrada para “ast”, obtiene el nodo-
i del directorio /usr/ast. Con este nodo, el sistema puede encontrar el directorio mismo y buscar el archivo
mbox.

Luego se trae a la memoria el nodo-i de este archivo y se mantiene ahí hasta que se cierra el archivo. El
proceso de búsqueda se ilustra en la siguiente figura:

Ing. Jorge Orellana A.   82  


 
 
Sistemas Operativos (2010017)
 

Los nombres de ruta relativos se buscan de la misma forma que los absolutos, sólo que el punto de
partida es el directorio de trabajo en lugar del directorio raíz. Cada directorio tiene entradas para “.” y “..”,
que se colocan ahí cuando se crea el directorio. La entrada “.” tiene el número de nodo-i del directorio
actual, y la entrada “..” tiene el número de nodo-i del directorio padre. Así, un procedimiento que busque
../dick/prog.c simplemente buscará “..” en el directorio de trabajo, encontrará el número de nodo-i de su
directorio padre y buscará dick en ese directorio. No se requiere ningún mecanismo especial para
manejar estos nombres. En lo que al sistema de directorios concierne, se trata de cadenas ASCII
ordinarias, lo mismo que los demás nombres.

6.3.3. Administración del espacio en disco


Los archivos normalmente se almacenan en disco, así que la administración del espacio en disco es de
interés primordial para los diseñadores de sistemas de archivos. Hay dos posibles estrategias para
almacenar un archivo de n bytes: asignar n bytes consecutivos de espacio en disco, o dividir el archivo
en varios bloques (no necesariamente) contiguos. Un trueque similar (entre segmentación pura y
paginación) está presente en los sistemas de administración de memoria. El almacenamiento de un
archivo como secuencia contigua de bytes tiene el problema obvio de que, si el archivo crece,
probablemente tendrá que pasarse a otro lugar del disco. El mismo problema ocurre con los segmentos
en la memoria, excepto que el traslado de un segmento en la memoria es una operación relativamente
rápida comparada con el traslado de un archivo de una posición en el disco a otra. Por esta razón, casi
todos los sistemas de archivos dividen los archivos en bloques de tamaño fijo que no necesitan estar
adyacentes.

Tamaño de bloque
Una vez que se ha decidido almacenar archivos en bloques de tamaño fijo, surge la pregunta de qué
tamaño deben tener los bloques. Dada la forma como están organizados los discos, el sector, la pista y
el cilindro son candidatos obvios para utilizarse como unidad de asignación. En un sistema con
paginación, el tamaño de página también es un contendiente importante.
Tener una unidad de asignación grande, como un cilindro, implica que cada archivo, incluso un archivo
de un byte, ocupará todo un cilindro. Estudios realizados (Mullender y Tanenbaum, 1984) han
demostrado que la mediana del tamaño de los archivos en los entornos UNIX es de alrededor de 1K, así
que asignar un cilindro de 32K a cada archivo implicaría un desperdicio de 31/32 = 97% del espacio en
disco total. Por otro lado, el empleo de una unidad de asignación pequeña implica que cada archivo
consistirá en muchos bloques. La lectura de cada bloque normalmente requiere una búsqueda y un
retardo rotacional, así que la lectura de un archivo que consta de muchos bloques pequeños será lenta.
Por ejemplo, consideremos un disco con 32 768 bytes por pista, tiempo de rotación de 16.67 ms y tiempo
de búsqueda medio de 30 ms. El tiempo en milisegundos requerido para leer un bloque de k bytes es la
suma de los tiempos de búsqueda, retardo rotacional y transferencia:
30 + 8.3 + (k/32768) x 16.67
La curva continua de la siguiente figura muestra la tasa de datos para un disco con estas características
en función del tamaño de bloque. Si utilizamos el supuesto burdo de que todos los bloques son de 1K (la
mediana de tamaño medida), la curva de guiones de la siguiente figura indicará la eficiencia de espacio

Ing. Jorge Orellana A.   83  


 
 
Sistemas Operativos (2010017)
 

del disco. La mala noticia es que una buena utilización del espacio (tamaño de bloque < 2K) implica
tasas de datos bajas y viceversa. La eficiencia de tiempo y la eficiencia de espacio están inherentemente
en conflicto. El término medio usual es escoger un tamaño de bloque de 512, 1K o 2K bytes. Si se
escoge un tamaño de bloque de 1K en un disco cuyos sectores son de 512 bytes, el sistema de archivos
siempre leerá o escribirá dos sectores consecutivos y los tratará como una sola unidad, indivisible. Sea
cual sea la decisión que se tome, probablemente convendrá reevaluarla periódicamente, ya que, al igual
que sucede con todos los aspectos de la tecnología de computadoras, los usuarios aprovechan los
recursos más abundantes exigiendo todavía más.

Administración de bloques libres


Una vez que se ha escogido el tamaño de bloque, el siguiente problema es cómo seguir la pista a los
bloques libres. Se utilizan ampliamente dos métodos, mismos que se ilustran en la siguiente figura. El
primero (a) consiste en usar una lista enlazada de bloques de disco, en la que cada bloque contiene
tantos números de bloques de disco libres como quepan en él. Con bloques de 1K y números de bloque
de 32 bits, cada bloque de la lista libre contiene los números de 255 bloques libres. (Se requiere una
ranura para el apuntador al siguiente bloque.) Un disco de 200M necesita una lista libre de, como
máximo, 804 bloques para contener los 200K números de bloque. Es común que se usen bloques libres
para contener la lista libre.

La otra técnica de administración del espacio libre es (b) el mapa de bits. Un disco con n bloques
requiere un mapa de bits con n bits. Los bloques libres se representan con unos en el mapa, y los
bloques asignados con ceros (o viceversa). Un disco de 200M requiere 200K bits para el mapa, mismos
que ocupan sólo 25 bloques. No es sorprendente que el mapa de bits requiera menos espacio, ya que
sólo usa un bit por bloque, en vez de 32 como en el modelo de lista enlazada. Sólo si el disco está casi
lleno el esquema de lista enlazada requerirá menos bloques que el mapa de bits. Si hay suficiente
memoria principal para contener el mapa de bits, este método generalmente es preferible. En cambio, si
sólo se puede dedicar un bloque de memoria para seguir la pista a los bloques de disco libres, y el disco
está casi lleno, la lista enlazada podría ser mejor. Con sólo un bloque del mapa de bits en la memoria,
podría ser imposible encontrar bloques libres en él, causando accesos a disco para leer el resto del
mapa de bits. Cuando un bloque nuevo de la lista enlazada se carga en la memoria, es posible asignar
255 bloques de disco antes de tener que traer del disco el siguiente bloque de la lista.

Ing. Jorge Orellana A.   84  


 
 
Sistemas Operativos (2010017)
 

6.3.4. Confiabilidad del sistema de archivos


Es necesario proteger la información alojada en el sistema de archivos, efectuando los resguardos
correspondientes [Tanenbaum]. De esta manera se evitan las consecuencias generalmente catastróficas
de la pérdida de los sistemas de archivos. La destrucción de un sistema de archivos suele ser un
desastre mucho peor que la destrucción una computadora. Las pérdidas se pueden deber a problemas
de hardware, software, hechos externos, etc.
Los discos pueden tener bloques defectuosos. Los discos flexibles normalmente son perfectos cuando
salen de la fábrica, pero pueden adquirir defectos durante el uso.
Para manejar bloques defectuosos se utilizan soluciones por hardware y por software.
• La solución en hardware consiste en dedicar un sector del disco a la lista de bloques
defectuosos. Al inicializar el controlador de hardware por primera vez, este lee la “lista de
bloques defectuosos” y elige un bloque (o pista) de repuesto para reemplazar los defectuosos,
registrando la correspondencia en la lista de bloques defectuosos. A partir de entonces, todas las
solicitudes que pidan el bloque defectuoso usarán el de repuesto. Cada vez que se descubren
nuevos errores se actualiza esta lista como parte de un formato de bajo nivel.
• La solución en software requiere que el usuario o el sistema de archivos construyan un archivo
con todos los bloques defectuosos. Se los elimina de la lista de bloques libres y se crea un
“archivo de bloques defectuosos que está constituido por los bloques defectuosos que no debe
ser leído ni escrito y no se debe intentar obtener copias de respaldo de este archivo.

Respaldos (copias de seguridad o de back-up)


Es muy importante respaldar los archivos con frecuencia incluso teniendo una estrategia ingeniosa para
manejar los bloques defectuosos. Los respaldos pueden consistir en efectuar copias completas del
contenido de los discos flexibles o rígidos.
Una estrategia de respaldo consiste en dividir los discos en áreas de datos y áreas de respaldo,
utilizándolas de a pares, pero se desperdicia la mitad del almacenamiento de datos en disco para
respaldo, donde cada noche (o en el momento que se establezca), la parte de datos de la unidad 0 se
copia a la parte de respaldo de la unidad 1 y viceversa.

Otra estrategia es el vaciado por incrementos o respaldo incremental, donde se obtiene una copia de
respaldo periódicamente (una vez por mes o por semana), llamada copia total. Se obtiene una copia
diaria solo de aquellos archivos modificados desde la última copia total; en estrategias mejoradas, se
copian solo aquellos archivos modificados desde la última vez que dichos archivos fueron copiados. Se
debe mantener en el disco información de control como una lista de los tiempos de copiado de cada
archivo, la que debe ser actualizada cada vez que se obtienen copias de los archivos y cada vez que los
archivos son modificados. Puede requerir una gran cantidad de cintas de respaldo dedicadas a los
respaldos diarios entre respaldos completos.
También se puede usar métodos automáticos que emplean múltiples discos, como la creación de
espejos que emplea dos discos. Las escrituras se efectúan en ambos discos, y las lecturas provienen de
uno solo. La escritura en el disco espejo se retrasa un poco, efectuándose cuando el sistema está
ocioso. Un sistema así puede seguir funcionando en "modo degradado" si un disco falla, y esto permite
reemplazar el disco que falló y recuperar los datos sin tener que parar el sistema.

Consistencia del sistema de archivos


Muchos sistemas de archivos leen bloques, los modifican y escriben en ellos después. Si el sistema falla
antes de escribir en los bloques modificados, el sistema de archivos puede quedar en un estado
inconsistente. La inconsistencia es particularmente crítica si alguno de los bloques afectados son:
• Bloques de nodos-i.
Ing. Jorge Orellana A.   85  
 
 
Sistemas Operativos (2010017)
 

• Bloques de directorios.
• Bloques de la lista de bloques libres.
La mayoría de los sistemas dispone de un programa utilitario que verifica la consistencia del sistema de
archivos. Se pueden ejecutar al arrancar el sistema o a pedido, pueden actuar sobre todos o algunos de
los discos y pueden efectuar verificaciones a nivel de bloques y a nivel de archivos. La consistencia del
sistema de archivos no asegura la consistencia interna de cada archivo, respecto de su contenido.
Generalmente pueden verificar también el sistema de directorios y / o de bibliotecas.

Se pueden realizar dos tipos de verificaciones de consistencia: de bloques y de archivos. Para


comprobar la consistencia de los bloques, el programa construye dos tablas, cada una de las cuales
contiene un contador para cada bloque, que inicialmente vale 0. Los contadores de la primera tabla
llevan la cuenta de cuántas veces un bloque está presente en un archivo; los contadores de la segunda
tabla registran cuántas veces está presente cada bloque en la lista libre (o en el mapa de bits de bloques
libres).
A continuación, el programa lee todos los nodos-i. Partiendo de un nodo-i, es posible construir una lista
de todos los números de bloque empleados en el archivo correspondiente. Al leerse cada número de
bloque, su contador en la primera tabla se incrementa. Después, el programa examina la lista o mapa de
bits de bloques libres, para encontrar todos los bloques que no están en uso. Cada ocurrencia de un
bloque en la lista libre hace que su contador en la segunda tabla se incremente. Si el sistema de archivos
es consistente, cada bloque tendrá un 1 ya sea en la primera tabla o en la segunda, como se ilustra en la
figura (a). Sin embargo, después de una caída las tablas podrían tener el aspecto de la figura (b), en la
que el bloque 2 no ocurre en ninguna de las dos tablas. Este bloque se informará como bloque fallante.
Aunque los bloques fallantes no representan un daño real, desperdician espacio y, por tanto, reducen la
capacidad del disco. La solución en el caso de haber bloques fallantes es directa: el verificador del
sistema de archivos simplemente los agrega a la lista libre.

Otra situación que podría ocurrir es la de la figura (c). Aquí vemos un bloque, el número 4, que ocurre
dos veces en la lista libre. (Sólo puede haber duplicados si la lista libre es realmente una lista; si es un
mapa de bits esto es imposible.) La solución en este caso también es simple: reconstruir la lista libre. Lo
peor que puede suceder es que el mismo bloque de datos esté presente en dos o más archivos, como se
muestra en la figura (d) con el bloque 5. Si cualquiera de estos archivos se elimina, el bloque 5 se
colocará en la lista libre, dando lugar a una situación en la que el mismo bloque está en uso y libre al
mismo tiempo. Si se eliminan ambos archivos, el bloque se colocará en la lista libre dos veces. La acción
apropiada para el verificador del sistema de archivos es asignar un bloque libre, copiar en él el contenido
del bloque 5, e insertar la copia en uno de los archivos. De este modo, el contenido de información de los
archivos no cambiará (aunque casi con toda seguridad estará revuelto), y la estructura del sistema de
archivos al menos será consistente. Se deberá informar del error, para que el usuario pueda
inspeccionar los daños.
Además de verificar que cada bloque esté donde debe estar, el verificador del sistema de archivos
también revisa el sistema de directorios. En este caso también se usa una tabla de contadores, pero
ahora uno por cada archivo, no por cada bloque. El programa parte del directorio raíz y desciende
recursivamente el árbol, inspeccionando cada directorio del sistema de archivo; Para cada archivo de
cada directorio, el verificador incrementa el contador correspondiente í nodo-i de ese archivo. Una vez
que termina la revisión, el verificador tiene una lista, indizada por número de nodo que indica cuántos
directorios apuntan a ese nodo-i. Luego, el programa compara estos números con los conteos de enlace
almacenados en los nodos-i mismos. En un sistema de archivos consistente, ambos conteos coinciden.

Ing. Jorge Orellana A.   86  


 
 
Sistemas Operativos (2010017)
 

Sin embargo, pueden ocurrir dos tipos de errores: el conteo de enlaces del nodo-i puede ser demasiado
alto o demasiado bajo.
Si el conteo de enlaces es mayor que el número de entradas de directorio, entonces aunque si borraran
todos los archivos de todos los directorios el conteo seguiría siendo mayor que cero y el nodo-i no se
eliminaría. Este error no es grave, pero desperdicia espacio en el disco con archivo: que no están en
ningún directorio, así que debe corregirse asignando el valor correcto al conteo de enlaces del nodo-i. El
otro error puede ser catastrófico. Si dos entradas de directorio están enlazadas a un archivo, pero el
nodo-i dice que sólo hay una, cuando se elimine cualquiera de las entradas de directorio, el conteo del
nodo-i se convertirá en cero. Cuando esto suceda, el sistema de archivos le marcará como desocupado
y liberará todos sus bloques. El resultado de esta acción es que uno de los directorios ahora apunta a un
nodo-i desocupado, cuyos bloques pronto pueden ser asignados a otros archivos. Una vez más, la
solución es hacer que el conteo de enlaces del nodo-i sea igual al número real de entradas de directorio.
Estas dos operaciones, verificar bloques y verificar directorios, a menudo se integran por razones de
eficiencia (una sola pasada por los nodos-i). También pueden realizarse otras verificaciones heurísticas.
Por ejemplo, los directorios tienen un formato definido, con números de nodo-i y nombres ASCII. Si un
número de nodo-i es mayor que el número de nodos-i que hay en el disco, es señal de que el directorio
ha sido dañado.
Además, cada nodo-i tiene un modo, y algunos de estos modos son permitidos pero extraños, como el
0007, que no permite que el propietario ni su grupo tengan acceso, pero sí permite a terceros leer,
escribir y ejecutar el archivo. Podría ser útil al menos informar de la existencia de archivos que dan más
derechos a terceros que al propietario. Los directorios que tienen más de 1000 entradas también son
sospechosos. Los archivos situados en directorios de usuarios, pero que son propiedad del superusuario
y tienen habilitado el bit SETUID, implican posibles problemas de seguridad.

6.3.5. Desempeño del sistema de archivos


El acceso a un disco es mucho más lento que el acceso a la memoria. La lectura de un byte de memoria
por lo regular toma decenas de nanosegundos. La lectura de una palabra de un disco duro puede tardar
50 microsegundos, lo que implica que es cuatro veces más lenta por palabra de 32 bits, pero a esto
deben sumarse de 10 a 20 milisegundos para mover la cabeza de lectura a la pista correcta y luego
esperar a que el sector deseado se coloque debajo de la cabeza. Si sólo se necesita una palabra, el
acceso a la memoria será del orden de 100 000 veces más rápido que al disco. En vista de esta
diferencia en el tiempo de acceso, muchos sistemas de archivos se han diseñado pensando en reducir el
número de accesos a disco requeridos.
La técnica más común empleada para reducir los accesos a disco es el caché de bloques o el caché de
buffer. En este contexto, un caché es una colección de bloques que lógicamente pertenecen al disco
pero que se están manteniendo en la memoria por razones de rendimiento.
Uno de los algoritmos más comunes para la administración del caché es el siguiente:
• Verificar todas las solicitudes de lectura para saber si el bloque solicitado se encuentra en el caché.
• En caso afirmativo, se satisface la solicitud sin un acceso a disco.
• En caso negativo, se lee para que ingrese al caché y luego se copia al lugar donde se necesite.
• Cuando hay que cargar un bloque en un caché totalmente ocupado:
— Hay que eliminar algún bloque y volverlo a escribir en el disco en caso de que haya sido
modificado luego de haberlo traído del disco.
— Se plantea una situación muy parecida a la paginación y se resuelve con algoritmos similares.
Se debe considerar la posibilidad de una falla total del sistema y su impacto en la consistencia del
sistema de archivos, si un bloque crítico, como un bloque de un nodo-i, se lee en el caché y se modifica,
sin volverse a escribir en el disco, una falla total del sistema dejará al sistema de archivos en un estado
inconsistente.
Se deben tener en cuenta los siguientes factores:
• Los bloques que se vayan a utilizar muy pronto, como un bloque parcialmente ocupado que se está
escribiendo, deberían permanecer un largo tiempo.
• Es esencial el bloque para la consistencia del sistema de archivos y si se ha modificado, debe
escribirse en el disco de inmediato, con lo que se reduce la probabilidad de que una falla total del
sistema haga naufragar al sistema de archivos eligiendo con cuidado el orden de escritura de los
bloques críticos.

Ing. Jorge Orellana A.   87  


 
 
Sistemas Operativos (2010017)
 

• No es recomendable mantener los bloques de datos en el caché durante mucho tiempo antes de
reescribirlos.
La solución de algunos S. O. consiste en tener una llamada al sistema que fuerza una actualización
general a intervalos regulares de algunos segundos. Una técnica importante para aumentar el
rendimiento de un sistema de archivos es la reducción de la cantidad de movimientos del brazo del disco
(mecanismo de acceso), se deben colocar los bloques que probablemente tengan un acceso secuencial,
próximos entre sí, preferentemente en el mismo cilindro. Los nodos-i deben estar a mitad del disco y no
al principio, reduciendo a la mitad el tiempo promedio de búsqueda entre el nodo-i y el primer bloque del
archivo.

7.4. Ejemplos de Sistemas de Archivos

7.4.1. Sistemas de archivos Windows

7.4.1.1. FAT (File Allocation Table)

El sistema de archivos FAT es uno de los sistemas más simples que se implementan por los sistemas
operativos. Esta sencillez viene dada por el reducido número de estructuras que lo conforman. FAT nació
como una solución a la gestión de archivos y directorios para los sistemas DOS. Posteriormente su uso
fue extendido a los sistemas operativos Windows en sus diferentes versiones, así como para sistemas
UNIX.
El nombre de FAT viene dado porque su principal estructura es precisamente una tabla de asignación de
archivos (FAT, File Allocation Table). Dependiendo del tamaño de las entradas de esta tabla
distinguiremos tres variantes de este sistema. Si las entradas son de 12 bits nos estaremos refiriendo a
FAT12. Si las entradas son de 16 bits hablaremos de FAT16. Finalmente, si las entradas son de 32 bits
(realmente se utilizan 28) nos dirigiremos a FAT32.
La tabla de asignación de archivos (FAT), reside en la parte más "superior" del volumen. Para proteger el
volumen, se guardan dos copias de la FAT por si una resultara dañada. Además, las tablas FAT y el
directorio raíz deben almacenarse en una ubicación fija para que los archivos de arranque del sistema se
puedan ubicar correctamente.

Las funciones que tienen las diferentes zonas son:


• Sector de arranque: ésta zona contiene información acerca del código de arranque del sistema,
así como de características físicas y lógicas del sistema de archivos.
• FAT: ésta zona no es más que la tabla que ya se ha citado anteriormente. Su función es
mantener la relación de los diferentes conjuntos de sectores o clústeres asignados a los
archivos.
• Directorio raíz: estructura con entradas que mantienen la información de todos los archivos del
sistema.
• Zona de datos: formada por clústeres que guardan la información contenida en los archivos.
Un disco con formato FAT se asigna en clústeres, cuyo tamaño viene determinado por el tamaño del
volumen. Cuando se crea un archivo, se crea una entrada en el directorio y se establece el primer
número de clúster que contiene datos. Esta entrada de la tabla FAT indica que este es el último clúster
del archivo o bien señala al clúster siguiente.

Ing. Jorge Orellana A.   88  


 
 
Sistemas Operativos (2010017)
 

FUNCIONAMIENTO DEL SISTEMA DE ARCHIVOS


Para la grabación de un archivo o directorio, el sistema primero se fija cual es el primer cluster disponible
para usar, luego genera una entrada en el directorio (lugar donde se inserta el nombre de archivo o
directorio), luego determina el tamaño para ocupar un cluster o varios, si ocupa un solo cluster escribirá
en el numero de cluster asignado la sentencia EOF indicando que el archivo termina en ese cluster,

Si el archivo fuese mas grande que un cluster se informa sobre la ubicación del cluster inicial y luego se
busca otro cluster (en lo posible consecutivo) para seguir almacenando el dato, colocando este numero
en la posición inicial para indicar donde continuar la búsqueda al momento de recuperar el archivo y
repitiendo este proceso hasta llegar al fin del archivo donde se colocara la sentencia EOF.

En el área de directorio no solo se guardan el nombre de archivos sino también:


• Nombre del archivo (8 bytes con el formato 8.3)
• Extensión (3 bytes)
• Identificación (Si es archivo, directorio o entrada de nombre de archivo largo NFL)
• Fecha y hora de creación (5 bytes)
• Cluster de inicio (2 bytes)
• Tamaño de archivo (4 bytes)
• Atributos de archivo (A, R, S, H, D)

Ing. Jorge Orellana A.   89  


 
 
Sistemas Operativos (2010017)
 

Convención de nomenclatura de FAT


FAT utiliza la convención de nomenclatura tradicional 8.3 y todos los nombres de archivo deben crearse
con el conjunto de caracteres ASCII. El nombre de un archivo o directorio puede tener ocho caracteres
de longitud, después un separador de punto (.) y una extensión de hasta tres caracteres. El nombre debe
empezar con una letra o un número y puede contener cualquier carácter excepto los siguientes:

. " / \ [ ] : ; | = ,

Los nombres siguientes están reservados:


CON, AUX, COM1, COM2, COM3, COM4, LPT1, LPT2, LPT3, PRN, NUL

Todos los caracteres se convertirán a mayúsculas.

Ventajas de FAT
No es posible realizar una recuperación de archivos eliminados en Windows NT en ninguno de los
sistemas de archivos compatibles. Las utilidades de recuperación de archivos eliminados intentan tener
acceso directamente al hardware, lo que no se puede hacer en Windows NT. Sin embargo, si el archivo
estuviera en una partición FAT y se reiniciara el sistema en MS-DOS, se podría recuperar el archivo. El
sistema de archivos FAT es el más adecuado para las unidades y/o particiones de menos de 200 MB
aproximadamente, ya que FAT se inicia con muy poca sobrecarga.

Desventajas de FAT
Cuando se utilicen unidades o particiones de más de 200 MB, es preferible no utilizar el sistema de
archivos FAT. El motivo es que a medida que aumente el tamaño del volumen, el rendimiento con FAT
disminuirá rápidamente. No es posible establecer permisos en archivos que estén en particiones FAT.
Las particiones FAT tienen un tamaño limitado a un máximo de 4 Gigabytes (GB) en Windows NT y 2 GB
en MS-DOS.

7.4.1.2. HPFS (sistemas de archivos de alto rendimiento)

El sistema de archivos HPFS se presentó por primera vez con OS/2 1.2 para permitir un mejor acceso a
los discos duros de mayor tamaño que estaban apareciendo en el mercado. Además, era necesario que
un nuevo sistema de archivos ampliara el sistema de nomenclatura, la organización y la seguridad para
las crecientes demandas del mercado de servidores de red. HPFS mantiene la organización de directorio
de FAT, pero agrega la ordenación automática del directorio basada en nombres de archivo. Los
nombres de archivo se amplían hasta 254 caracteres de doble byte. HPFS también permite crear un
archivo de "datos" y atributos especiales para permitir una mayor flexibilidad en términos de
compatibilidad con otras convenciones de nomenclatura y seguridad. Además, la unidad de asignación
cambia de clústeres a sectores físicos (512 bytes), lo que reduce el espacio perdido en el disco.

En HPFS, las entradas de directorio contienen más información que en FAT. Además del archivo de
atributos, esto incluye información sobre la fecha y la hora de modificación, de creación y de acceso. En
lugar de señalar al primer clúster del archivo, en HPFS las entradas del directorio señalan a FNODE.
FNODE puede contener los datos del archivo, o bien punteros que pueden señalar a datos del archivo o
a otras estructuras que, a su vez, señalarán a datos del archivo.

HPFS intenta asignar, en la medida de lo posible, la mayor cantidad de datos de un archivo en sectores
contiguos. Esto se hace con el fin de aumentar la velocidad al realizar el procesamiento secuencial de un
archivo.
 
HPFS organiza una unidad en una serie de bandas de 8 MB y, siempre que sea posible, un archivo
estará contenido dentro de una de estas bandas. Entre cada una de estas bandas hay mapas de bits de
asignación de 2 KB, que hacen un seguimiento de los sectores dentro de una banda que se han
asignado y que no se han asignado. La creación de bandas aumenta el rendimiento porque el cabezal de

Ing. Jorge Orellana A.   90  


 
 
Sistemas Operativos (2010017)
 

la unidad no tiene que volver a la parte superior lógica (normalmente el cilindro 0) del disco, sino al mapa
de bits de asignación de banda más cercano, para determinar dónde se almacenará un archivo.

Superbloque El superbloque se encuentra en el sector lógico 16 y contiene un puntero al FNODE del


directorio raíz. Uno de los mayores peligros de utilizar HPFS es que si el superbloque se pierde o resulta
dañado debido a un sector defectuoso, lo mismo ocurrirá con el contenido de la partición, incluso aunque
el resto de la unidad esté bien. Sería posible recuperar los datos de la unidad copiando todo a otra
unidad con un sector 16 en buen estado y volviendo a generar el superbloque. Sin embargo, es una
tarea muy compleja.
Bloque de reserva El bloque de reserva se encuentra en el sector lógico 17, y contiene una tabla de
"revisiones" y el bloque de directorio de reserva. En HPFS, cuando se detecta un sector defectuoso, la
entrada de las "revisiones" se utiliza para señalar lógicamente a un sector en buen estado existente en
lugar de al sector defectuoso. Esta técnica para el tratamiento de errores de escritura se conoce como
revisión.
La revisión es una técnica en la que si se produce un error debido a un sector defectuoso, el sistema de
archivos mueve la información a otro sector diferente y marca el sector original como no válido. Todo ello
se realiza de forma transparente para cualquier aplicación que esté realizando operaciones de E/S de
disco (es decir, la aplicación nunca sabe que hubo problemas con el disco duro). Al utilizar un sistema de
archivos que admite revisiones, se eliminarán mensajes de error como el de FAT "¿Desea interrumpir,
reintentar o cancelar?" que aparece cuando se encuentra un sector defectuoso. La versión de HPFS
incluida con Windows NT no admite revisiones.

Ventajas de HPFS
• HPFS es la mejor opción para las unidades comprendidas entre 200 y 400 MB.
• Permite nombres de archivos de hasta 256 caracteres.
• El volumen está estructurado en bandas.
• La estructura de directorios es una estructura de árbol binario cuyos nodos se denominan
Fondees. Estas estructuras contienen un nombre de archivo, su longitud, atributos, ACL (Access
Control List) y situación de los datos del archivo, es decir, el número de banda en el que se
encuentran.

Desventajas de HPFS

• Debido a la sobrecarga que implica HPFS, no es una opción muy eficaz para un volumen de
menos de unos 200 MB. Además, con volúmenes mayores de unos 400 MB, habrá una ligera
degradación del rendimiento. No se puede establecer la seguridad en HPFS con Windows NT.

Ing. Jorge Orellana A.   91  


 
 
Sistemas Operativos (2010017)
 

• HPFS solo es compatible con las versiones 3.1, 3.5 y 3.51 de Windows NT. Windows NT 4.0 no
puede tener acceso a particiones HPFS.
• Fragmentación externa. Depende del tamaño del archivo de los usuarios y de su disposición en
las bandas.

7.4.1.3. NTFS (New Technology File System)

Está basado en el sistema de archivos HPFS de IBM/Microsoft usado en el sistema operativo OS/2, y
también tiene ciertas influencias del formato de archivos HFS diseñado por Apple.

Desde el punto de vista de un usuario, NTFS sigue organizando los archivos en directorios que, al igual
que ocurre en HPFS, se ordenan. Sin embargo, a diferencia de FAT o de HPFS, no hay ningún objeto
"especial" en el disco y no hay ninguna dependencia del hardware subyacente, como los sectores de 512
bytes. Además, no hay ninguna ubicación especial en el disco, como las tablas de FAT o los
superbloques de HPFS.

Los objetivos de NTFS son proporcionar lo siguiente:


• Confiabilidad, que es especialmente deseable para los sistemas avanzados y los servidores de
archivos
• Una plataforma para tener mayor funcionalidad
• Compatibilidad con los requisitos de POSIX
• Eliminación de las limitaciones de los sistemas de archivos FAT y HPFS

Confiabilidad Para garantizar la confiabilidad de NTFS, se trataron tres áreas principales: posibilidad de
recuperación, eliminación de errores graves de un único sector y revisiones.
NTFS es un sistema de archivos recuperable porque hace un seguimiento de las transacciones con el
sistema de archivos. Cuando se ejecuta un comando CHKDSK en FAT o HPFS, se comprueba la
coherencia de los punteros dentro del directorio, la asignación y las tablas de archivos. En NTFS se
mantiene un registro de transacciones con estos componentes de forma que CHKDSK solo tenga que
deshacer las transacciones hasta el último punto de confirmación para recuperar la coherencia dentro del
sistema de archivos.
En FAT o en HPFS, si se produce un error en un sector que es la ubicación de uno de los objetos
especiales del sistema de archivos, se producirá un error de un único sector. NTFS evita esto de dos
maneras: en primer lugar, no utilizando objetos especiales en el disco, efectuando el seguimiento de
todos los objetos del disco y protegiéndolos. En segundo lugar, en NTFS se mantienen varias copias (el
número depende del tamaño del volumen) de la tabla maestra de archivos. De manera similar a las
versiones OS/2 de HPFS, NTFS admite revisiones.
Mayor funcionalidad Uno de los principales objetivos de diseño de Windows NT en cada nivel es
proporcionar una plataforma a la que se pueda agregar e integrar funciones, y NTFS no es ninguna
excepción. NTFS proporciona una plataforma enriquecida y flexible que pueden utilizar otros sistemas de
archivos. Además, NTFS es totalmente compatible con el modelo de seguridad de Windows NT y admite
varias secuencias de datos. Ya no es un archivo de datos en una única secuencia de datos. Por último,
en NTFS un usuario puede agregar a un archivo sus propios atributos definidos por él mismo.
Compatibilidad con POSIX NTFS es el sistema de archivos compatible que mejor se adhiere a POSIX,
ya que cumple los requisitos siguientes de POSIX.:

• Nomenclatura con distinción entre mayúsculas y minúsculas (En POSIX, LÉAME.TXT, Léame.txt
y léame.txt son todos archivos diferentes.)
• Marca de tiempo adicional (La marca de tiempo adicional proporciona la hora a la que se tuvo
acceso al archivo por última vez)
• Vínculos físicos (Un vínculo físico se produce cuando dos nombres de archivo diferentes, que
pueden estar en directorios diferentes, señalan a los mismos datos.)

Ing. Jorge Orellana A.   92  


 
 
Sistemas Operativos (2010017)
 

Eliminación de limitaciones En primer lugar, NTFS ha aumentado considerablemente el tamaño de los


archivos y los volúmenes, de forma que ahora pueden tener hasta 2^64 bytes (16 exabytes o
18.446.744.073.709.551.616 bytes). NTFS también ha vuelto al concepto de clústeres de FAT para evitar
el problema de HPFS de un tamaño de sector fijo. Esto se hizo porque Windows NT es un sistema
operativo portátil y es probable que se encuentre tecnología de disco diferente en algún lugar. Por tanto,
se consideró que quizás 512 bytes por sector no fuera siempre un valor adecuado para la asignación.
Para lograrlo, se permitió definir el clúster como múltiplos del tamaño de asignación natural del hardware.
Por último, en NTFS todos los nombres de archivo se basan en Unicode, y los nombres de archivo 8.3 se
conservan junto con los nombres de archivo largos.

Es un sistema adecuado para las particiones de gran tamaño requeridas en estaciones de trabajo de alto
rendimiento y servidores puede manejar volúmenes de, teóricamente, hasta 264–1 clústeres. En la
práctica, el máximo volumen NTFS soportado es de 232–1 clústeres (aproximadamente 16 TiB usando
clústeres de 4 KiB).

Hay tres versiones de NTFS: v1.2 en NT 3.51, NT 4, v3.0 en Windows 2000 y v3.1 en Windows
XP, Windows 2003 Server, Windows Vista y v5.1 en Windows 2008. Estas versiones reciben en
ocasiones las denominaciones v4.0, v5.0, v5.1, v 5.2, y v 6.0 en relación con la versión de Windows en la
que fueron incluidas

Una partición formateada con NTFS tiene la siguiente estructura:

• Sector de arranque. Esta zona no ocupa generalmente un único sector sino varios. Contiene
información sobre la disposición del volumen así como de las diferentes estructuras que forman el
sistema de archivos. También contiene información para el arranque del sistema.
• Master File Table (MFT). Se trata de una tabla, tal y como su nombre indica, que contiene
información de todos los archivos y directorios del sistema.
• Archivos de sistema. Los archivos del sistema contienen estructuras útiles para la gestión del
sistema de archivos, así como para la recuperación del mismo en caso de fallida.
• Zona de datos. Zona para la ubicación de los diferentes archivos de datos.

La estructura de directorios que implementa NTFS es la estructura de árbol. Para mejorar el rendimiento
del sistema utiliza un árbol B, el cuál permite balanceo de carga

Master File Table (MFT) La tabla MFT está organizada en registros que contienen información de todos
los archivos del volumen. El tamaño de un registro es de 1 KB. La tabla MFT se basa en el concepto de

Ing. Jorge Orellana A.   93  


 
 
Sistemas Operativos (2010017)
 

tabla de una base de datos relacional. Cada registro contiene información de un archivo, incluyendo la
tabla MFT que es tratada como un archivo más. De este modo se consigue que la tabla sea de tamaño
variable.

Ventajas  de  NTFS  


• NTFS  es  la  mejor  opción  para  volúmenes  de  unos  400  MB  o  más.  El  motivo  es  que  el  
rendimiento  no  se  degrada  en  NTFS,  como  ocurre  en  FAT,  con  tamaños  de  volumen  mayores.    
• La  posibilidad  de  recuperación  está  diseñada  en  NTFS  de  manera  que  un  usuario  nunca  tenga  
que  ejecutar  ningún  tipo  de  utilidad  de  reparación  de  disco  en  una  partición  NTFS.    
Desventajas  de  NTFS  
• No  se  recomienda  utilizar  NTFS  en  un  volumen  de  menos  de  unos  400  MB,  debido  a  la  
sobrecarga  de  espacio  que  implica.  Esta  sobrecarga  de  espacio  se  refiere  a  los  archivos  de  
sistema  de  NTFS  que  normalmente  utilizan  al  menos  4  MB  de  espacio  de  unidad  en  una  
partición  de  100  MB.    
 
Convenciones  de  nomenclatura  de  NTFS  
Los  nombres  de  archivo  y  de  directorio  pueden  tener  hasta  255  caracteres  de  longitud,  incluyendo  
cualquier  extensión.  Los  nombres  conservan  el  modelo  de  mayúsculas  y  minúsculas,  pero  no  distinguen  
mayúsculas  de  minúsculas.  NTFS  no  realiza  ninguna  distinción  de  los  nombres  de  archivo  basándose  en  
el  modelo  de  mayúsculas  y  minúsculas.  Los  nombres  pueden  contener  cualquier  carácter  excepto  los  
siguientes:    

? " / \ < > * | :

En la actualidad, desde la línea de comandos solo se pueden crear nombres de archivo de un máximo
de 253 caracteres.

Ing. Jorge Orellana A.   94  


 
 
Sistemas Operativos (2010017)
 

Límites

Máxima dimensión 16 TiB con la actual implementación


de archivo
(16 EiB según su arquitectura)

32
Máximo número de 4.294.967.295 (2 –1)
archivos

Tamaño máximo del 255 caracteres


nombre de archivo

Tamaño máximo del 256 TiB con la actual implementación (16 EiBsegún su arquitectura)
volumen

1
Caracteres Cualquier carácter excepto '\0' (NULO) y '/'
permitidos en
nombres de archivo Windows también excluye el uso de \: * ? " < > |

Funcionamiento

Todo lo que tiene que ver con los archivos se almacena en forma de metadatos. Esto permitió una fácil
ampliación de características durante el desarrollo de Windows NT. Un ejemplo lo hallamos en la
inclusión de campos de indizado añadidos para posibilitar el funcionamiento de Active Directory.
Estructuras

Contenido del Árbol-B+


directorio

Localización de Mapa de bits/Extents


archivo

Bloques malos Mapa de bits/Extents

Los nombres de archivo son almacenados en Unicode (UTF-16), y la estructura de archivos en árboles-
B, una estructura de datos compleja que acelera el acceso a los archivos y reduce la fragmentación, que
era lo más criticado del sistema FAT.

Se emplea un registro transaccional (journal) para garantizar la integridad del sistema de archivos (pero
no la de cada archivo). Los sistemas que emplean NTFS han demostrado tener una estabilidad
mejorada, que resultaba un requisito ineludible considerando la naturaleza inestable de las versiones
más antiguas de Windows NT. Sin embargo, a pesar de lo descrito anteriormente, este sistema de
archivos posee un funcionamiento prácticamente secreto, ya que Microsoft no ha liberado
su código como hizo con FAT.

Ing. Jorge Orellana A.   95  


 
 
Sistemas Operativos (2010017)
 

Gracias a la ingeniería inversa, aplicada sobre el sistema de archivos, se desarrollaron controladores


como el NTFS-3G que actualmente proveen a sistemas operativos GNU/Linux, Solaris, MacOS X o BSD,
entre otros, de soporte completo de lectura y escritura en particiones NTFS.

El procedimiento que lleva a cabo NTFS cuando se quiere almacenar un archivo o un directorio es el
siguiente. Se pueden dar dos casos.
• Si se quiere almacenar un archivo en el que sus atributos son de poco tamaño, se coloca
automáticamente en un registro MFT. Es importante recordar que los archivos tienen como atributo a
su contenido. Los atributos que son guardados directamente en un registro MFT son denominados
atributos residentes.
• Si, por el contrario, el tamaño de los atributos de un archivo supera la capacidad de un registro, los
atributos son almacenados en clústeres de datos contiguos de la zona de datos. En este caso, el
registro MFT contiene referencias a dichos clústeres. El registro también contiene un identificador
denominado número de clúster virtual. Este identificador da información sobre el primer clúster que
ocupa el archivo así como el número de clústeres que ocupa. Dado que los clústeres que están
contiguos no tendremos que realizar consultas de la ubicación del siguiente clúster del archivo.
Se podría dar la posibilidad de que el número de referencias a los clústeres de un archivo también fuera
elevado. Éstas serían almacenadas también en la zona de datos, y se mantendría en el registro MFT una
referencia al bloque que las contiene. En general, los atributos que son guardados en la zona de datos
se denominan atributos no residentes.

7.4.1.4. ReFS (Sistema de archivos resistente a errores)

ReFS (Resilient File System, originalmente con nombre en código «Protogon») es un nuevo sistema de
archivos en Windows Server 2012 inicialmente previsto para servidores de archivos que mejora en
NTFS. El sistema presenta limitaciones frente a su predecesor, como se detalla más adelante, pero
también novedades en varios campos.

Los clientes de Windows desean disponer de una plataforma rentable que maximice la disponibilidad de
los datos, que escale de manera eficiente a conjuntos de datos de gran tamaño en diversas cargas de
trabajo y que garantice la integridad de los datos por medio de la resistencia a los daños
(independientemente de los errores de software o hardware). ReFS es un nuevo sistema de archivos que
está diseñado para satisfacer estas necesidades de los clientes y, al mismo tiempo, proporcionar una
base para importantes innovaciones en el futuro. Mediante una pila de almacenamiento integrada que
incluye ReFS y la nueva característica de Espacios de almacenamiento, los clientes ahora pueden
implementar la plataforma más rentable para obtener un acceso a los datos escalable y disponible
utilizando almacenamiento.

La característica Espacios de almacenamiento protege los datos contra errores de disco parciales y
totales mediante la conservación de copias en varios discos. ReFS establece una interfaz con Espacios
de almacenamiento para reparar los daños de manera automática.

Las siguientes son algunas de las características clave de ReFS:

• Mantiene los más altos niveles posibles de disponibilidad y confiabilidad del sistema, suponiendo que
el almacenamiento subyacente puede ser, de forma inherente, poco confiable.
• Ofrece una arquitectura resistente a errores completa cuando se usa junto con Espacios de
almacenamiento para que estas dos características amplíen las funcionalidades y la confiabilidad de
la otra cuando se usan juntas.
• Mantiene la compatibilidad con las características de NTFS que se adoptan ampliamente y con
resultados óptimos, además de reemplazar las características que aportan un valor limitado.

Ing. Jorge Orellana A.   96  


 
 
Sistemas Operativos (2010017)
 

Además, existe una nueva herramienta de línea de comandos Integrity.exe para administrar la integridad
y las directivas de limpieza de discos.

Aplicaciones prácticas
ReFS ofrece funcionalidades que ayudan a los clientes a almacenar y proteger los datos,
independientemente de la confiabilidad de la pila subyacente de hardware y software. Esto minimiza el
costo del almacenamiento y reduce los gastos de capital para las empresas. Las siguientes son algunas
de las aplicaciones prácticas:
• Servidor de archivos de uso general. El cliente implementa un servidor de archivos conectado a
una configuración de almacenamiento JBOD con unidades serie ATA (SATA) o SCSI conectado
en serie (SAS).
• Almacenamiento remoto consolidado de datos de aplicaciones. El cliente implementa un clúster
de servidor de archivos de dos nodos y escalabilidad horizontal con Espacios de
almacenamiento, en el cual el clúster usa una configuración de almacenamiento JBOD con
unidades SATA o SAS.

Funcionalidad importante
La funcionalidad importante que se incluye con ReFS se describe a continuación:
• Integridad: ReFS almacena los datos de modo que los protege contra muchos de los errores
comunes que normalmente ocasionan pérdida de datos. Cuando ReFS se usa junto con un
Espacio de almacenamiento reflejado, los daños detectados (tanto en los metadatos como en los
datos de usuario, cuando están habilitadas las secuencias de integridad) se pueden reparar de
forma automática mediante la copia alternativa proporcionada por los Espacios de
almacenamiento. En caso de que se produzca un error del sistema, ReFS se recupera
rápidamente del error sin que haya pérdida de datos de usuario.
• Disponibilidad. ReFS prioriza la disponibilidad de los datos. Históricamente, los sistemas de
archivos a menudo son susceptibles a daños en los datos que requieren que el sistema se
desconecte para la reparación. Con ReFS, si se producen daños, el proceso de reparación se
localiza en el área del daño y se ejecuta en línea, por lo que no se requiere tiempo de inactividad
de los volúmenes. Pese a que no es algo habitual, si un volumen se daña, o si el usuario decide
no usar Espacios de almacenamiento reflejados, ReFS implementa un rescate, una
característica que elimina los datos dañados del espacio de nombres en un volumen activo sin
que los datos en estado óptimo se vean afectados por los datos dañados que no se pueden
reparar. Además, no hay chkdsk con ReFS.
• Escalabilidad. Dado que la cantidad y el tamaño de los datos que se almacenan en los equipos
sigue aumentando con rapidez, ReFS está diseñado para funcionar correctamente con conjuntos
de datos extremadamente grandes, petabytes y volúmenes aún mayores, sin afectar al
rendimiento. No obstante, las preocupaciones prácticas en torno a las configuraciones del
sistema (como la cantidad de memoria), los límites que imponen diversos componentes del
sistema y el tiempo que se necesita para rellenar los conjuntos de datos o las horas de copia de
seguridad pueden definir limitaciones prácticas. El formato en disco de ReFS está diseñado para
admitir tamaños de volúmenes de hasta 2^78 bytes usando tamaños de clúster de 16 KB,
mientras que el direccionamiento de la pila de Windows permite 2^64 bytes. Este formato
también admite tamaños de archivo de 2^64-1 bytes, 2^64 archivos en un directorio y la misma
cantidad de directorios en un volumen.
• Identificación proactiva de errores. Las funcionalidades de integridad de ReFS las aprovecha
un escáner de integridad, cuyo proceso se conoce como limpieza. La utilidad de limpieza
examina de forma periódica el volumen e intenta identificar los daños latentes y luego
desencadena automáticamente una reparación de los datos dañados.

Principales novedades
• Mejora de la fiabilidad de las estructuras en disco. ReFS utiliza árboles B+ para todas las
estructuras en disco incluyendo metadatos y los datos de los archivos. El tamaño de archivo, el
tamaño total de volumen, el número de archivos en un directorio y el número de directorios en un
volumen están limitados a números de 64 bits, lo que se traduce en un tamaño máximo de
Ing. Jorge Orellana A.   97  
 
 
Sistemas Operativos (2010017)
 

archivo de 16 exbibytes, un tamaño máximo de volumen de 1 yobibyte (con clústeres de 64 KiB),


que permite gran escalabilidad prácticamente sin límites en el tamaño de archivos y directorios
(las restricciones de hardware siguen aplicando). Los metadatos y los archivos son organizados
en tablas, de manera similar a una base de datos relacional. El espacio libre se cuenta mediante
un asignador jerárquico que comprende tres tablas separadas para trozos grandes, medianos y
pequeños. Los nombres de archivo y las rutas de acceso de archivo están limitados a una
cadena de texto Unicode de 32 KiB.
• Capacidad de resiliencia incorporada. ReFS emplea estrategia de actualización de metadatos
de asignación en escritura, que asigna los nuevos bloques para transacción de actualización y
utiliza lotes grandes de entrada y salida (IO). Todos los metadatos de ReFS tienen sumas de
verificación de 64 bits incorporadas, que son almacendas de forma independiente. Los datos de
los archivos opcionalmente pueden tener una suma de verificación en una «corriente de
integridad» separada, en cuyo caso la estrategia de actualización de archivo también implementa
asignación en escritura; esto es controlado por un nuevo atributo «integridad» aplicable a
archivos y directorios. Si los datos de archivo o los metadatos resultaran dañados, el archivo
puede ser eliminado sin tener que desmontar el volumen por mantenimiento, y así restaurarlos
desde una copia de seguridad. Con la resiliencia incorporada, los administradores no necesitan
ejecutar periódicamente herramientas de comprobación de errores en el sistema de archivos
(como CHKDSK) en los volúmenes con sistemas de archivos ReFS.
• Compatibilidad con las APIs y tecnologías existentes. ReFS no requiere de nuevas APIs de
sistema y la mayoría de los filtros de sistema de archivos continuarán trabajando con volúmenes
ReFS. ReFS soporta muchas características existentes de Windows y NTFS, como el cifrado
BitLocker, Listas de Control de Acceso, diario USN, notificaciones de cambio, enlaces
simbólicos, puntos de unión, puntos de montaje, puntos de reanálisis, instantáneas de volumen,
IDs de archivo y oplock. ReFS se integra adecuadamente con los «espacios de
almacenamiento», una capa de virtualización de almacenamiento que permite la realización de
espejos de datos (mirroring), así como compartir las agrupaciones de almacenamiento entre
máquinas. Las características de resiliencia de ReFS mejora la función de duplicación (mirroring)
provista por los espacios de almacenamiento, y puede detectar si las copias espejo de los
archivos llegan a corromperse usando un proceso de depuración de datos en segundo plano,
que periódicamente lee todas las copias espejos y verifica sus sumas de verificación, luego
remplaza las copias dañadas por copias en buen estado de los archivos implicados.

Limitaciones de ReFS frente a NTFS


Algunas características de NTFS no son compatibles por ReFS, como los flujos de datos alternativos,
identificadores de objetos, nombres cortos «8.3», compresión de archivos, cifrado a nivel de archivos,
transacciones de datos de usuario, archivos dispersos, enlaces duros, atributos extendidos y cuotas de
disco. ReFS no ofrece por sí mismo deduplicación de datos. Son remplazados los discos dinámicos con
volúmenes espejos o en bandas, con agrupaciones de almacenamiento con bandas o espejos, provistas
por espacios de almacenamiento. Sin embargo, en Windows Server 2012 solo es soportada la corrección
automatizada de errores en los espacios reflejados, y tampoco es soportado el arranque desde un
volumen con formato ReFS.

7.4.2. Sistemas de archivos UNIX

7.4.2.1. MINIX

Minix fue el primer sistema de archivos de Linux. Se origino con el sistema operativo Minix de ahí su
nombre. Fue escrito desde 0 por Andrew S. Tanenbaum en la década de los 80s, como un sistema
operativo tipo UNIX, donde su código fuente puede ser usado libremente para la educación. El sistema
de archivos MINIX fue diseñado para usarse en MINIX; copia la estructura básica del Sistema de
archivos UNIX (UFS), pero evita características complejas para mantener el código fuente limpio, claro y
simple, donde su objetivo total es ser de Minix una ayuda útil de enseñanza.
Cuando Linus Torvalds comenzó a escribir el núcleo Linux en 1991, él trabajaba en una máquina
corriendo MINIX, así el lanzamiento inicial estuvo basado en las funcionalides de éste último sistema
operativo. Hasta que en abril de 1992 se realiza la introducción del Extended file system (sistema de
Ing. Jorge Orellana A.   98  
 
 
Sistemas Operativos (2010017)
 

archivos extendido, conocido como ext). Este sistema de ficheros es aún usado por algunas  
distribuciones  GNU/Linux  para  arrancar  discos  y  en  otras  situaciones,  donde  se  necesite  un  sistema  de  
archivos  simple  y  compacto.  

Un sistema de archivos MINIX tiene seis componentes:


• El Bloque de arranque, el cual es el que se almacena en el primer bloque de un disco duro. Contiene
el cargador de Arranque, que carga y corre un sistema operativo dentro del sistema conocido como
sistema de inicio.
• El segundo bloque es el Superblock, el cual almacena datos relacionados al sistema de archivos,
que permite al sistema operativo localizar y entender otras arquitecturas de sistemas de archivos.
Por ejemplo, el número de inodos y zonas, el tamaño de los dos bitmaps y el bloque de inicio de una
área de datos.
• El inode bitmap, es el mapa simple de uno de los inodos que rastrea cuales están en uso y cuales
están libres, representadolos con un 1 (para los en uso) y con un 0 (para los libres).
• El zone bitmap, trabaja de similar forma al inode bitmap, excepto que rastrea las zonas.
• El área de inodos. Cada archivo o directorio es representado como un inodo, el cual graba
metadatos incluyendo los tipos (archivo, directorio, bloque, carácter, pipe), identidad (is) para el
usuario y el grupo, tres registros de fecha y el tiempo de último acceso, la última modificación y el
último cambio de estado. Un inodo también contiene una lista de las direcciones que indican las
zonas en el àrea de datos donde el archivo o los datos de directorio están actualmente almacenados.
• El data area (área de datos), es el componente más largo de un sistema de archivos, que usa la
mayor parte del espacio. Es donde los archivos y directorios de datos están almacenados.

7.4.2.2. Extended file system

El sistema de archivos extendido más comúnmente conocido como ext o EXTFS. El sistema de archivos
extendido (extended file system o ext), fue el primer sistema de archivos creado específicamente para el
sistema operativo Linux. Fue diseñado para vencer las limitaciones del sistema de archivos MINIX. Fue
reemplazado tanto por ext2 como xiafs, entre los cuales había una competencia, que finalmente ganó
ext2, debido a su viabilidad a largo plazo.

ext

Nombre completo Extended file system

Introducción Abril de 1992 (Linux)

Estructuras

Localización de archivo bitmap (espacio libre), table (metadatos)

Bloques malos Tabla

7.4.2. 3. ext2 (second extended filesystem)

ext2 "segundo sistema de archivos extendido" es un sistema de archivos para el kernel Linux.. La
principal desventaja de ext2 es que no implementa el registro por diario (en inglés Journaling) que sí
Ing. Jorge Orellana A.   99  
 
 
Sistemas Operativos (2010017)
 

poseen sus posteriores versiones ext3 y ext4. ext2 fue el sistema de ficheros por defecto de las
distribuciones de Linux Red Hat, Fedora y Debian.

El sistema de archivos tiene una tabla donde se almacenan los i-nodos. Un i-nodo almacena información
del archivo (ruta o path, tamaño, ubicación física). En cuanto a la ubicación, es una referencia a un
sector del disco donde están todas y cada una de las referencias a los bloques del archivo fragmentado.
Estos bloques son de tamaño especificable cuando se crea el sistema de archivos, desde los 512 bytes
hasta los 4 KiB, lo cual asegura un buen aprovechamiento del espacio libre con archivos pequeños. Los
límites son un máximo de 2 terabytes de archivo, y de 4 para la partición.

ext2

Nombre completo Second extended file system

Sistemas operativos
Linux, BSD, Windows (mediante IFS), Mac OS X
compatibles

Introducción Enero de 1993 (Linux)

Estructuras

Localización de archivo I-nodos

Límites

Máxima dimensión de archivo 2 TB

18
Máximo número de archivos 10

Tamaño máximo del nombre de


255 caracteres
archivo

Tamaño máximo del volumen 16 TB

Caracteres permitidos en
Cualquiera excepto NULL y '/'
nombres de archivo

Ing. Jorge Orellana A.   100  


 
 
Sistemas Operativos (2010017)
 

7.4.2.4. ext3 (third extended filesystem)

ext3 "tercer sistema de archivos extendido" es un sistema de archivos con registro por diario (journaling).
Es el sistema de archivo más usado en distribuciones Linux. Es una extensión del sistema de ficheros
ext2. Este sistema de archivos está incluido en los kernels 2.4.x.

La principal diferencia con ext2 es el registro por diario. Un sistema de archivos ext3 puede ser montado
y usado como un sistema de archivos ext2. Otra diferencia importante es que ext3 utiliza un árbol binario
balanceado (árbol AVL) e incorpora el asignador de bloques de disco Orlov.

Ventajas

Aunque su velocidad y escalabilidad es menor que sus competidores, como JFS, ReiserFS o XFS, tiene
la ventaja de permitir actualizar de ext2 a ext3 sin perder los datos almacenados ni tener que formatear
el disco. Tiene un menor consumo de CPU y está considerado más seguro que otros sistemas de
ficheros en Linux dada su relativa sencillez y su mayor tiempo de prueba.

El sistema de archivo ext3 agrega a ext2 lo siguiente:

• Registro por diario.


• Índices en árbol para directorios que ocupan múltiples bloques.
• Crecimiento en línea.

Límites de tamaño

Ext3 tiene dos límites de tamaño distintos. Uno para archivos y otro para el tamaño del sistema de
32
archivos entero. El límite del tamaño del sistema de archivos es de 2 bloques

Tamaño del bloque Tamaño máximo de los archivos Tamaño máximo del sistema de ficheros

1 KiB 16 GiB 2 TiB

2 KiB 256 GiB 8 TiB

4 KiB 2 TiB 16 TiB

8 KiB 2 TiB 32 TiB

Niveles del Journaling

Hay tres niveles posibles de Journaling (registro por diario)

Diario (riesgo bajo)


Los metadatos y los ficheros de contenido son copiados al diario antes de ser llevados al sistema
de archivos principal. Como el diario está en el disco continuamente puede mejorar el
rendimiento en ciertas ocasiones. En otras ocasiones el rendimiento es peor porque los datos
deben ser escritos dos veces, una al diario y otra a la parte principal del sistema de archivos.
Pedido (riesgo medio)
Solo  los  metadatos  son  registrados  en  el  diario,  los  contenidos  no,  pero  está  asegurado  que  el  
contenido  del  archivo  es  escrito  en  el  disco  antes  de  que  el  metadato  asociado  se  marque  como  

Ing. Jorge Orellana A.   101  


 
 
Sistemas Operativos (2010017)
 

transcrito  en  el  diario.  Es  el  sistema  por  defecto  en  la  mayoría  de  las  distribuciones  de  Linux.  Si  
hay  un  bajón  de  tensión  o  kernel Panic  cuando  el  fichero  se  está  escribiendo  o  está  empezando,  
el  diario  indicará  que  el  nuevo  archivo  o  el  intento  no  ha  sido  pasado,  por  lo  que  será  purgado  
por  el  proceso  de  limpiado  
Reescritura  (riesgo  alto)  
Solo  los  metadatos  son  registrados  en  el  diario,  el  contenido  de  los  archivos  no.  Los  contenidos  
pueden  estar  escritos  antes  o  después  de  que  el  diario  se  actualice.  Como  resultado,  los  archivos  
modificados  correctamente  antes  de  una  ruptura  pueden  volverse  corruptos.  Por  ejemplo,  un  
archivo  pendiente  de  ser  marcado  en  el  diario  como  mayor  de  lo  que  actualmente  es,  
convirtiendo  en  basura  al  final  de  la  comprobación.  Las  versiones  antiguas  de  los  archivos  
pueden  aparecer  inesperadamente  después  de  una  recuperación  de  diario.  La  carencia  de  
sincronización  entre  los  datos  y  el  diario  es  rápidamente  subsanada  en  muchos  casos.  JFS  usa  
este  nivel  de  journaling,  pero  se  asegura  de  que  cualquier  basura  es  borrada  al  reiniciar  

Desventajas

Funcionalidad
Como ext3 está hecho para ser compatible con ext2, la mayoría de las estructuras del archivación son
similares a las del ext2. Por ello, ext3 carece de muchas características de los diseños más recientes
como las extensiones, la localización dinámica de los inodos, y la sublocalización de los bloques. Hay un
límite de 31998 subdirectorios por cada directorio, que se derivan de su límite de 32000 links por inodo.
Ext3, como la mayoría de los sistemas de archivos actuales de Linux, no puede ser chequeado por el
fsck mientras el sistema de archivos está montado para la escritura. Si se intenta chequear un sistema
de ficheros que está montado puede detectar falsos errores donde los datos no han sido volcados al
disco todavía, y corromper el sistema de archivos al intentar arreglar esos errores.

Fragmentación
No hay herramienta de desfragmentación online para ext3 que funcione en nivel del sistema de archivos.
Existe un desfragmentador offline para ext2, e2defrag, pero requiere que el sistema de archivos ext3 sea
reconvertido a ext2 antes de iniciarse. Además, dependiendo de los bits encendidos en el sistema,
e2defrag puede destruir datos.

Compresión
El soporte para la compresión está disponible como un parche no oficial para ext3. Este parche es un
aporte directo de e2compr pero necesita un mayor desarrollo ya que todavía no implementa el journaling.

No hay comprobación en el diario


Ext3 no hace la suma de verificación cuando está escribiendo en el diario.

ext3

Nombre completo Third extended file system

Sistemas operativos
Linux, BSD, Windows (a través de IFS)
compatibles

Ing. Jorge Orellana A.   102  


 
 
Sistemas Operativos (2010017)
 

Introducción Noviembre de 2001 (Linux 2.4.15)

Estructuras

Contenido del directorio Tabla, Árbol

Localización de archivo bitmap (espacio libre), tabla (metadatos)

Bloques malos Tabla

Límites

Máxima dimensión de
2 TB
archivo

Máximo número de archivos 1018

Tamaño máximo del nombre


255 caracteres
de archivo

Tamaño máximo del


32 TB
volumen

7.4.2.5. ext4 (fourth extended filesystem)

ext4 “cuarto sistema de archivos extendido” es un sistema de archivos transaccional (en inglés
journaling). El 25 de diciembre de 2008 se publicó el kernel Linux 2.6.28, que elimina ya la etiqueta de
"experimental" de código de ext4.

Las principales mejoras son:


• Soporte de volúmenes de hasta 1024 PiB.
• Soporte añadido de extent.
• Menor uso del CPU.
• Mejoras en la velocidad de lectura y escritura.

Mejoras

Sistema de archivos de gran tamaño


El sistema de archivos ext4 es capaz de trabajar con volúmenes de gran tamaño, hasta 1 exbibyte y
archivos de tamaño de hasta 16 TiB.

Extents

Ing. Jorge Orellana A.   103  


 
 
Sistemas Operativos (2010017)
 

Los extents han sido introducidos para reemplazar al tradicional esquema de bloques usado por los
sistemas de archivos ext2/3. Un extent es un conjunto de bloques físicos contiguos, mejorando el
rendimiento al trabajar con ficheros de gran tamaño y reduciendo la fragmentación. Un extent simple en
ext4 es capaz de mapear hasta 128 MiB de espacio contiguo con un tamaño de bloque igual a 4 KiB.

Compatibilidad hacia adelante y hacia atrás


Cualquier sistema ext3 existente puede ser montado como ext4 sin necesidad de cambios en el formato
del disco. También es posible actualizar un sistema de archivos ext3 para conseguir las ventajas del ext4
ejecutando un par de comandos. Esto significa que se puede mejorar el rendimiento, los límites de
almacenamiento y las características de sistemas de archivos ext3 sin reformatear y/o reinstalar el
sistema operativo. Si se requiere de las ventajas de ext4 en un sistema de producción, se puede
actualizar el sistema de archivos.

Asignación persistente de espacio en el disco


El sistema de archivos ext4 permite la reserva de espacio en disco para un archivo. Hasta ahora la
metodología consistía en rellenar el fichero en el disco con ceros en el momento de su creación. Esta
técnica no es ya necesaria con ext4, ya que una nueva llamada del sistema "preallocate()" ha sido
añadida al kernel Linux para uso de los sistemas de archivos que permitan esta función. El espacio
reservado para estos ficheros quedará garantizado y con mucha probabilidad será contiguo. Esta función
tiene útiles aplicaciones en streaming y bases de datos.

Asignación retrasada de espacio en el disco


Ext4 hace uso de una técnica de mejora de rendimiento llamada Allocate-on-flush, también conocida
como reserva de memoria retrasada. Consiste en retrasar la reserva de bloques de memoria hasta que
la información esté a punto de ser escrita en el disco, a diferencia de otros sistemas de archivos, los
cuales reservan los bloques necesarios antes de ese paso. Esto mejora el rendimiento y reduce la
fragmentación al mejorar las decisiones de reserva de memoria basada en el tamaño real del fichero.

Límite de 32000 subdirectorios superado


En ext3 el nivel de profundidad en subdirectorios permitido estaba limitado a 32000. Este límite ha sido
aumentado a 64000 en ext4.

Journal checksumming
ext4 usa checksums en el registro para mejorar la fiabilidad, puesto que el journal es uno de los ficheros
más utilizados en el disco. Esta función tiene un efecto colateral beneficioso: permite de forma segura
evitar una lectura/escritura de disco durante el proceso de registro en el journal, mejorando el
rendimiento ligeramente. La técnica del journal checksumming está inspirada en la investigación de la
Universidad de Wisconsin en sistemas de archivos IRON (Sección 6, bajo el nombre "checksums de
transacciones").

Desfragmentación online
Incluso haciendo uso de diversas técnicas para evitar la fragmentación, un sistema de larga duración
tiende a fragmentarse con el tiempo. Ext4 dispondrá de una herramienta que permite desfragmentar
ficheros individuales o sistemas de ficheros enteros sin desmontar el disco.
En ext4, los grupos de bloques no asignados y secciones de la tabla de inodos están marcados como
tales. Esto permite a e2fsck saltárselos completamente en los chequeos y en gran medida reduce el
tiempo requerido para chequear un sistema de archivos del tamaño para el que ext4 está preparado.
Esta función está implementada desde la versión 2.6.24 del kernel Linux.

Asignador multibloque
Ext4 asigna múltiples bloques para un fichero en una sola operación, lo cual reduce la fragmentación al
intentar elegir bloques contiguos en el disco. El asignador multibloque está activo cuando se usa
0_DIRECT o si la asignación retrasada está activa. Esto permite al fichero tener diversos bloques
"sucios" solicitados para escritura al mismo tiempo, a diferencia del actual mecanismo del kernel de
solicitud de envío de cada bloque al sistema de archivos de manera separada para su asignación.

Ing. Jorge Orellana A.   104  


 
 
Sistemas Operativos (2010017)
 

Timestamps mejorados
Puesto que los ordenadores se tornan en general cada vez más rápidos y que Linux está pasando a ser
cada vez más usado en aplicaciones críticas, la granularidad de los timestamps basados en segundos se
está volviendo insuficiente. Para resolver esto, ext4 tendrá timestamps medidos en nanosegundos. Ésta
función está actualmente implementada en la versión 2.6.23 del kernel. Adicionalmente se han añadido 2
bits del timestamp extendido a los bits más significativos del campo de segundos de los timestamps para
retrasar casi 500 años el problema del año 2038.

ext4

Nombre completo Fourth extended file system

Sistemas operativos
Linux
compatibles

Introducción 10 de octubre de 2006 (Linux 2.6.19)

Estructuras

Contenido del directorio Tabla, Árbol

Localización de archivo bitmap (espacio libre), table (metadatos)

Bloques malos Tabla

Límites

Máxima dimensión de
16 TiB (usando bloques de 4k )
archivo

4 mil millones (4x10⁹) (especificado en el tiempo de


Máximo número de archivos
creación del sistema de archivos)

Tamaño máximo del nombre


256 bytes
de archivo

Tamaño máximo del volumen 1024 PiB = 1 EiB

Caracteres permitidos en
Todos los bytes excepto NULL y '/'
nombres de archivo

Ing. Jorge Orellana A.   105  


 
 
Sistemas Operativos (2010017)
 

7.4.3. Otros sistemas de archivos

• ReiserFS: Este sistema de ficheros es un diseño completamente nuevo, que fue introducido en el
kernel de Linux 2.4.1. Se ha ganado una reputación de estabilidad y uso eficiente de espacio en
disco, sobre todo cuando una partición tiene muchos archivos pequeños.
• XFS: Es de Silicon Graphics (SGI) del sistema de archivos de diario. Ha sido utilizado en la SGI de
las estaciones de trabajo Unix y servidores, lo liberaron bajo la licencia GPL. Este sistema de
ficheros es compatible con los tamaños máximos de hasta 16.384 peta bytes (1Pb es 1024Tb), y
máximo de archivo de hasta 8192Pb. XFS no está integrado en el kernel de Linux, desde la versión
2.4.18.
• JFS: Se desarrollo originalmente para el sistema operativo AIX, sin embargo, IBM ha lanzado su
sistema de archivos avanzado bajo la GPL y está apoyando activamente la migración a Linux.
Soporta sistema de archivos de un tamaño máximo de 32Pb. Aun no se ha incluido en el kernel
estándar, a partir de la versión 2.4.18.
• Apple - MFS: Fue utilizado por los 1ros. Macintosh. Este se utiliza en disquetes de 400Kb que son
extremadamente raros en la actualidad. Linux no incluye compatibilidad con MFS.
• HFS: El sistema de archivos jerárquico (HFS) fue el reemplazo del MFS. Utilizado en más de 800Kb
y los disquetes y discos duros de Macintosh hasta 1998; es muy común en el mundo Macintosh.
Macintosh CD-ROM suelen utilizar HFS en lugar de la norma ISO-9660.
• HFS+: El seguimiento a HFS toma muchas características del estilo de sistemas de archivos Unix,
pero no llega a la adición de un diario. Los nuevos Macintosh siempre vienen con discos con formato
HFS+, pero este sistema de ficheros no se utiliza mucho en medios extraíbles. El soporte de Linux
para HFS+ de encuentra en la fase alfa y todavía no se ha integrado en el kernel.
• FFS: MacOSX ofrece la opción de utilizar el sistema de archivos de Unix rápido (FFS).
• BeFS: Utiliza un sistema de archivos de diario propio, conocido como BeFS. Este es un sistema
operativo mono usuarios, apoya archivo de propiedad y los permisos similares a los utilizados en
Linux. Carece de soporte para acceso a archivos de marcas de tiempo, lo que puede dificultar su
capacidad como sistema de archivos naticos de Linux.
• FFS/UFS: Sistema de ficheros rápido (FFS) o también conocido como sistema de archivos Unix
(UFS) se desarrollo temprano en la historia de Unix. Es utilizado por muchos sistemas Unix y
derivados, incluyendo FreeBSD y Solaris. El soporte de Linux de escritura para este sistema de
archivos todavía se considera peligroso.
• NFS: Es el método preferido para el intercambio de archivos para redes de ordenadores Unix o
Linux.
• Coda: Se trata de un sistema de archivos de red avanzada que soporta las características omitidas
de NFS. Estas características incluyen una mayor seguridad (incluido el cifrado) y el almacenamiento
en cache mejorada.
• SMB/CIFS El Server Message Block (SMB), es el medio habitual de intercambio de archivos en red
entre los sistemas operativos de Microsoft. El kernel de Linux incluye SMB/CIFS cliente, para que
pueda montar SMB/CIFS acciones.
• NCP El NetWare Core Protocol (NCP) es un protocolo de intercambio de archivos NetWare. Al igual
que con SMB/CIFS, Linux incluye soporte básico de cliente NCP en el núcleo, y se puede añadir
paquetes de servidor distintos para convertir Linux en un servidor NCP.

Ing. Jorge Orellana A.   106  


 
 
Sistemas Operativos (2010017)

Tema VIII
Sistemas de Entrada/Salida
Las aplicaciones utilizan los dispositivos (devices) para realizar la E/S (entrada-salida). Estos dispositivos son
variados y trabajan de manera diferente: secuencialmente, random; transfieren datos asincrónicamente o
sincrónicamente; pueden ser de sólo lectura ( read only) o lectura-escritura ( read-write), etc. El sistema operativo
debe permitir que las aplicaciones puedan utilizar esos dispositivos, proveyendo una interfaz que los presente de la
manera mas simple posible. Los dispositivos son una de las partes mas lentas de un sistema de computo. Por lo
tanto, el SO, debe manejar la situación como para salvar esa diferencia de velocidad.
Una de las funciones principales de un S. O. es el control de todos los dispositivos de E /S de la computadora
[Tanenbaum].
Las principales funciones relacionadas son:
• Enviar comandos a los dispositivos.
• Detectar las interrupciones.
• Controlar los errores.
• Proporcionar una interfaz entre los dispositivos y el resto del sistema.

8.1. Principios del Hardware de E / S


El enfoque que se considerará tiene que ver con la interfaz que desde el hardware se presenta al software:
[Tanenbaum]
• Comandos que acepta el hardware.
• Funciones que realiza.
• Errores que puede informar.

8.1.1. Dispositivos de E/S


Los dispositivos de E/S se pueden clasificar en dispositivos de almacenamiento, dispositivos de comunicación y
dispositivos de interfaz con humanos. Ortogonalmente, se pueden clasificar como dispositivos de bloques o de
caracteres.
• Dispositivos por bloques. Almacena información en bloques de tamaño fijo, cada uno con su propia dirección.
Los tamaños de bloque comunes van desde 512 bytes hasta 32768 bytes. La propiedad esencial de un
dispositivo por bloques es que es posible leer o escribir cada bloque con independencia de los demás. Los
discos son los dispositivos por bloques más comunes.
• Dispositivos por caracteres. Suministra o acepta una corriente de caracteres, sin contemplar ninguna estructura
de bloques; no es direccionable y no tiene una operación de búsqueda. Las impresoras, interfaces de red, ratones
y casi todos los demás dispositivos que no se parecen a los discos pueden considerarse como dispositivos por
caracteres.
Algunos dispositivos no se ajustan a este esquema de clasificación, por ejemplo los relojes, que no tienen
direcciones por medio de bloques y no generan o aceptan flujos de caracteres. El sistema de archivos solo trabaja
con dispositivos de bloque abstractos, por lo que encarga la parte dependiente del dispositivo a un software de
menor nivel, el software manejador del dispositivo.

8.1.2. Controladores de dispositivos


Las unidades de E/S consisten típicamente de una parte mecánica y una parte electrónica, conocida como
controlador de dispositivo. El sistema operativo sólo se comunica con el controlador. La parte del sistema operativo
encargada de ello es el manejador o driver del dispositivo. La interfaz entre el controlador y la parte mecánica es de
muy bajo nivel.
La tarjeta controladora casi siempre tiene un conector en el que puede insertarse un cable que conduce al dispositivo.
Muchos controladores pueden manejar dos, cuatro o incluso ocho dispositivos idénticos. Si la interfaz entre el
controlador y el dispositivo es de un tipo estándar, ya sea una norma oficial como ANSI, IEEE o ISO, o una norma
de facto, las compañías pueden fabricar controladores y dispositivos que se ajustan a esa interfaz. Por ejemplo,
muchas compañías producen unidades de disco que se ajustan a las interfaces de controlador de disco IDE
(Integrated Drive Electronics) o SCSI (Small Computer System Interface).
Los modelos más frecuentes de comunicación entre la cpu y los controladores son para la mayoría de las micro y
mini computadoras el modelo de bus del sistema y para la mayoría de los mainframes el modelo de varios buses y
computadoras especializadas en E/S llamadas canales de E/S.

Ing. Jorge Orellana A.


82
Sistemas Operativos (2010017)

La interfaz entre el controlador y el dispositivo suele ser de nivel muy bajo. Un disco, por ejemplo, podría
formatearse con 16 sectores de 512 bytes en cada pista. Sin embargo, lo que realmente sale de la unidad es un flujo
de bits en serie que comienza con un preámbulo seguido de los 4096 bits del sector y por último una suma de
verificación, llamada también código para corrección de errores (ECC: error-correcting code). El preámbulo se
escribe cuando se da formato al disco y contiene el número de cilindro y de sector, el tamaño de sector y datos
similares, así como información de sincronización.
La función del controlador consiste en convertir un flujo de bits a un bloque de bytes y realizar las acciones de
corrección de errores necesarias. Generalmente, primero se arma el bloque de bytes, bit por bit, en un buffer dentro
del controlador. Una vez que se ha cotejado su suma de verificación y se le declara libre de errores, el bloque puede
copiarse en la memoria principal.
Cada controlador tiene unos cuantos registros que sirven para comunicarse con la CPU. En algunas computadoras
estos registros forman parte del espacio de direcciones de la memoria normal. Este esquema se denomina E/S
mapeada en memoria.
Además de los puertos de E/S, muchos controladores usan interrupciones para indicarle a la CPU cuándo están listos
para que sus registros sean leídos o escritos. Una interrupción es, en primera instancia, un suceso eléctrico. Una
línea de petición de interrupción (IRQ) de hardware es una entrada física del chip controlador de interrupciones. El
número de tales entradas es limitado; las PC de tipo Pentium sólo tienen 15 entradas disponibles para dispositivos de
E/S. Algunos controladores están alambrados físicamente en la tarjeta madre del sistema, como, por ejemplo, el
controlador del teclado en una PC. En el caso de un controlador que se enchufa en el plano posterior, a veces pueden
usarse interruptores o puentes de alambre en el controlador de dispositivo para seleccionar la IRQ que usará el
dispositivo, a fin de evitar conflictos (aunque en algunas tarjetas, como Plug „n Play, las IRQ pueden establecerse en
software). El chip controlador de interrupciones establece una correspondencia entre cada entrada IRQ y un vector
de interrupción, que localiza la rutina de servicio de interrupción correspondiente.

8.1.3. Acceso directo a memoria (DMA)


Muchos controladores, sobre todo los de dispositivos por bloques, manejan el acceso directo a memoria o DMA.
Analicemos cómo ocurren las lecturas de disco cuando no se usa DMA. Primero el controlador lee el bloque (uno o
más sectores) de la unidad en serie, bit por bit, hasta que todo el bloque está en el buffer interno del controlador. A
continuación, el controlador calcula la suma de verificación para comprobar que no ocurrieron errores de lectura, y
luego causa una interrupción. Cuando el sistema operativo comienza a ejecutarse, puede leer el bloque del disco del
buffer del controlador byte por byte o palabra por palabra, ejecutando un ciclo, leyéndose en cada iteración un byte o
una palabra de un registro del controlador y almacenándose en la memoria. Un ciclo de la CPU programado para
leer los bytes del controlador uno por uno desperdicia tiempo de CPU. Se inventó el DMA para liberar a la CPU de
este trabajo de bajo nivel.
Cuando se usa DMA, la CPU proporciona al controlador dos elementos de información, además de la dirección en
disco del bloque: la dirección de memoria donde debe colocarse el bloque y el número de bytes que deben
transferirse.

Una vez que el controlador ha leído todo el bloque del dispositivo, lo ha colocado en su buffer y ha calculado la
suma de verificación, copia el primer byte o palabra en la memoria principal en la dirección especificada por la

Ing. Jorge Orellana A.


83
Sistemas Operativos (2010017)

dirección de memoria de DMA. Luego, el controlador incrementa la dirección de DMA y decrementa la cuenta de
DMA en el número de bytes que se acaban de transferir. Este proceso se repite hasta que la cuenta de DMA es cero,
y en ese momento el controlador causa una interrupción. Cuando el sistema operativo inicia, no tiene que copiar el
bloque en la memoria; ya está ahí.
El proceso con almacenamiento intermedio de dos pasos tiene implicaciones importantes para el rendimiento de E/S.
Mientras los datos están siendo transferidos del controlador a la memoria, sea por la CPU o por el controlador, el
siguiente sector estará pasando bajo la cabeza del disco y los bits estarán llega al controlador. Los controladores
sencillos simplemente no pueden efectuar entrada y salida al mismo tiempo, de modo que mientras se está
realizando una transferencia a la memoria el sector/que pasa bajo la cabeza del disco se pierde.
En consecuencia, el controlador sólo puede leer bloques de manera intercalada, es decir, uno sí y uno no, de modo
que la lectura de toda una pista requiere dos rotaciones completas, una para los bloques pares y otra para los
impares. Si el tiempo que toma transferir un bloque del controlador a la memoria por el bus es más largo que el que
toma leer un bloque del disco, puede ser necesario leer un bloque y luego saltarse dos (o más) bloques. Saltarse
bloques para dar al controlador tiempo de transferir los datos a la memoria se denomina intercalación. Cuando se da
formato al disco, los bloques se numeran teniendo en cuenta el factor de intercalación. En la figura (a) hay un disco
con ocho bloques por pista y cero intercalación. En (b) el mismo disco con intercalación sencilla. En (c) la
intercalación doble.

El objetivo de numerar los bloques de este modo es permitir que el sistema operativo lea N bloques numerados
consecutivamente y aun así logre la máxima velocidad de que el hardware es capaz. Si los bloques se numeraran
como en (a) pero el controlador sólo pudiera leer bloques alternados, un sistema operativo que se encargara de
distribuir un archivo de ocho bloques en bloques de disco consecutivos requeriría ocho rotaciones de disco para leer
los bloques 0 al 7 en orden.

8.2. Principios del software de E/S


La idea básica es organizar el software como una serie de capas donde [Tanenbaum]:
• Las capas inferiores se encarguen de ocultar las peculiaridades del hardware a las capas superiores.
• Las capas superiores deben presentar una interfaz agradable, limpia y regular a los usuarios.

8.2.1. Objetivos del software de E/S


Un concepto clave del diseño de software de E/S se conoce como independencia del dispositivo, es decir, debe ser
posible escribir programas que puedan leer archivos de un disquete, un disco duro o un CD-ROM sin tener que
modificar los programas para cada tipo de dispositivo distinto. Es responsabilidad del sistema operativo resolver los
problemas causados por el hecho de que estos dispositivos en realidad son distintos y requieren controladores en
software muy distintos para escribir los datos en el dispositivo de salida.

Ing. Jorge Orellana A.


84
Sistemas Operativos (2010017)

El nombre de un archivo o un dispositivo debe ser simplemente una cadena y un entero. El objetivo de lograr
nombres uniformes está muy relacionado con el de independencia del dispositivo. Todos los archivos y dispositivos
adquieren direcciones de la misma forma, es decir mediante el nombre de su ruta de acceso.
Otro aspecto importante del software de E/S es el manejo de errores. En general, los errores deben manejarse tan
cerca del hardware como sea posible. Si el controlador descubre un error de lectura, debe tratar de corregirlo él
mismo, si puede. Si no puede, el controlador en software debería manejarlo, tal vez tratando simplemente de leer el
bloque otra vez. Muchos errores son transitorios, como los errores de lectura causados por partículas de polvo en la
cabeza de lectura, y desaparecen si la operación se repite.
Sólo si las capas inferiores son incapaces de resolver el problema se deberá informar de él a las capas superiores. En
muchos casos, la recuperación de errores puede efectuarse de manera transparente en un nivel bajo sin que los
niveles superiores se enteren siquiera de que ocurrió un error.
Otro aspecto clave es si las transferencias son síncronas (por bloqueo) o asíncronas (controladas por interrupciones).
En general, la E/S física es asíncrona: la CPU inicia la transferencia y se dedica a otra cosa hasta que llega la
interrupción. Los programas de usuario son mucho más fáciles de escribir si las operaciones de E/S provocan
bloqueos. Le corresponde al sistema operativo hacer que las operaciones que en realidad son controladas por
interrupciones parezcan controladas por bloqueo a los programas de usuario.
El último concepto es el de dispositivos de uso exclusivo y no exclusivo. Algunos dispositivos de E/S, como los
discos, pueden ser utilizados por muchos usuarios al mismo tiempo. No hay problemas si varios usuarios tienen
archivos abiertos en el mismo disco al mismo tiempo. Otros dispositivos, como las unidades de cinta, tienen que
estar dedicados a un solo usuario hasta que éste haya terminado. Luego, otro usuario puede disponer de la unidad de
cinta. Si dos o más usuarios escriben bloques entremezclados al azar en la misma cinta, se generará el caos. La
introducción de dispositivos de uso exclusivo (no compartidos) da lugar a diversos problemas. Una vez más, el
sistema operativo debe poder manejar dispositivos tanto compartidos como de uso exclusivo de manera tal que se
eviten problemas.
Estos objetivos se pueden lograr de una forma lógica y eficiente estructurando el software de E/S en cuatro capas,
como se ve en el grafico anterior:
1. Manejadores de interrupciones (capa inferior)
2. Controladores de dispositivos en software.
3. Software del sistema operativo independiente del software.
4. Software de usuario (capa superior).
En la figura se resume el sistema de E/S, con todas las capas y las funciones principales de cada capa. Comenzando
por abajo, las capas son el hardware, los manejadores de interrupciones, los controladores de dispositivos, el
software independiente del dispositivo y por último los procesos de usuario. Las flechas indican el flujo de control.
Por ejemplo, cuando un proceso de usuario trata de leer un bloque de un archivo, se invoca el sistema operativo para
que ejecute la llamada. El software independiente del dispositivo busca, por ejemplo, en el caché de bloques. Si el
bloque que se necesita no está ahí, ese software invoca el controlador de dispositivo para que emita la petición al
hardware. A continuación el proceso se bloquea hasta que se lleva a cabo la operación de disco. Cuando el disco
termina, el hardware genera una interrupción. Se ejecuta el manejador de interrupciones para descubrir qué ha
sucedido, es decir, cuál dispositivo debe atenderse en este momento. El manejador extrae entonces el estado del
dispositivo y despierta al proceso dormido para que finalice la petición de E/S y permita al proceso de usuario
continuar.

8.2.2. Manejadores de interrupciones


Las interrupciones son desagradables pero inevitables, y deben ocultarse en las profundidades del sistema operativo,
con el fin de reducir al mínimo las partes del sistema que tienen conocimiento de ellas:
• Cada proceso que inicie una operación de E/S se bloquea hasta que termina la E/S y ocurra la interrupción.
• El procedimiento de interrupción realiza lo necesario para desbloquear el proceso que lo inicio.

Ing. Jorge Orellana A.


85
Sistemas Operativos (2010017)

8.2.3. Controladores de dispositivos


Todo el código dependiente del dispositivo se coloca en los controladores de dispositivo. Cada controlador maneja
un tipo de dispositivo o, cuando más, una clase de dispositivos similares. Cada controlador tiene uno o más registros
de dispositivo que sirven para aceptar comandos. Los controladores en software emiten estos comandos y verifican
que se ejecuten correctamente. Así, el controlador de disco en software es la única parte del sistema operativo que
sabe cuántos registros tiene ese controlador de disco en hardware y para qué sirven. Sólo él sabe algo de sectores,
pistas, cilindros, cabezas, movimiento del brazo, factores de intercalación, motores, tiempos de asentamiento de la
cabeza y todos los demás aspectos mecánicos del funcionamiento correcto del disco.
La tarea de un controlador de dispositivo en software es aceptar peticiones abstractas del software independiente del
dispositivo que está arriba de él y ver que dichas peticiones sean atendidas.
El primer paso para atender realmente una petición de E/S, digamos para un disco, es traducirla de términos
abstractos a concretos. En el caso de un controlador de disco, esto implica calcular en qué parte del disco está
realmente el bloque solicitado, verificar si el motor de la unidad está funcionando, determinar si el brazo está
colocado en el cilindro apropiado, etc. En pocas palabras, el controlador en software debe decidir qué operaciones
del controlador son necesarias en el hardware y en qué orden. Una vez que el controlador en software ha decidido
qué comandos debe emitir al controlador en hardware, comenzará a emitirlos escribiendo en los registros de
dispositivo de este último. Algunos controladores en hardware sólo pueden manejar un comando a la vez; otros
están dispuestos a aceptar una lista enlazada de comandos, que luego ejecutan por su cuenta sin más ayuda del
sistema operativo.
Una vez que se ha emitido el comando o comandos, se presentará una de dos situaciones. En muchos casos el
controlador en software debe esperar hasta que el controlador en hardware realice cierto trabajo, así que se bloquea
hasta que llega la interrupción para desbloquearlo. En otros casos, sin embargo, la operación se lleva a cabo sin
dilación, y el controlador no necesita bloquearse. En el primer caso, el controlador bloqueado será despertado por la
interrupción; en el segundo, nunca se dormirá. De cualquier manera, una vez que se ha efectuado la operación el
controlador debe determinar si no ocurrieron errores. Si todo está bien, el controlador puede tener datos que pasar al
software independiente del dispositivo (el bloque que acaba de leerse). Por último, el controlador devuelve
información de estado para informar de errores a su invocador. Si hay otras peticiones en cola, puede seleccionarse e
inicia una de ellas. Si no hay nada en la cola, el controlador se bloquea esperando la siguiente petición.

8.2.4. Software de E/S independiente del dispositivo


Aunque una parte del software de E/S es específica para cada dispositivo, una fracción considerable es
independiente de él. La frontera exacta entre los controladores de dispositivos y el software independiente del
dispositivo depende del sistema, porque algunas funciones que podrían realizarse de forma independiente del
dispositivo podrían efectuarse realmente en los controladores en software, por razones de eficiencia o de otro tipo.
Las funciones generalmente realizadas por el software independiente del dispositivo son:
• Interfaz uniforme para los manejadores de dispositivos.
• Nombres de los dispositivos.
• Protección del dispositivo.
• Proporcionar un tamaño de bloque independiente del dispositivo.
• Uso de buffers.
• Asignación de espacio en los dispositivos por bloques.
• Asignación y liberación de los dispositivos de uso exclusivo.
• Informe de errores.
La función básica del software independiente del dispositivo es realizar las funciones de E/S comunes a todos los
dispositivos y presentar una interfaz uniforme al software de nivel de usuario.
Un aspecto importante de cualquier sistema operativo es cómo se nombran los objetos tales como archivos y
dispositivos de E/S. El software independiente del dispositivo se encarga de establecer la correspondencia entre los
nombres simbólicos de dispositivo y el controlador apropiado. En UNIX un nombre de dispositivo, como /dev/tty00,
especifica de manera única el nodo-i de un archivo especial, y este nodo-i contiene el número principal del
dispositivo, que sirve para localizar el controlador apropiado.
El nodo-i también contiene el número secundario del dispositivo, que se pasa como parámetro al controlador a fin de
especificar la unidad que se leerá o escribirá.
El software independiente del dispositivo debe ocultar a los niveles superiores los diferentes tamaños de sector de
los distintos discos y proporcionar un tamaño uniforme de los bloques, por ej.: considerar varios sectores físicos
como un solo bloque lógico.

Ing. Jorge Orellana A.


86
Sistemas Operativos (2010017)

El almacenamiento intermedio también es una cuestión importante, tanto para los dispositivos por bloques como
para los de caracteres. En el primer caso, el hardware generalmente insiste en leer y escribir bloques completos a la
vez, pero los procesos de usuario están en libertad de leer y escribir en unidades arbitrarias. Si un proceso de usuario
escribe medio bloque, el sistema operativo normalmente guardará los datos internamente hasta que se escriba el
resto de los datos, y en ese momento se podrá grabar el bloque en el disco. En el caso de los dispositivos por
caracteres, los usuarios podrían escribir los datos al sistema con mayor velocidad que aquella a la que pueden salir,
lo que requeriría almacenamiento intermedio (buffers). De igual manera, la entrada del teclado que llega antes de
que sea necesaria también requiere buffers.
Cuando se crea un archivo y se llena con datos, es preciso asignar nuevos bloques de disco al archivo. Para poder
realizar esta asignación, el sistema operativo necesita una lista o un mapa de bits de los bloques libres del disco, pero
el algoritmo para localizar un bloque libre es independiente del dispositivo y se puede efectuar en un nivel más alto
que el del manejador.
El manejo de errores generalmente lo realizan los controladores. La mayor parte de los errores son altamente
dependientes del dispositivo, de modo que sólo el controlador sabe qué debe hacerse (p. ej., reintentar, ignorar,
situación de pánico). Un error típico es el causado por un bloque de disco que se dañó y ya no puede leerse. Después
de que el controlador ha tratado de leer el bloque cierto número de veces, se da por vencido e informa de ello al
software independiente del dispositivo. La forma como el error se trata de aquí en adelante es independiente del
dispositivo. Si el error ocurrió mientras se estaba leyendo un archivo de usuario, puede ser suficiente informar del
error al invocador. Sin embargo, si el error ocurrió durante la lectura de una estructura de datos crítica del sistema,
como el bloque que contiene el mapa de bits que indica cuáles bloques están libres, puede ser que el sistema
operativo no tenga más opción que imprimir un mensaje de error y terminar.

8.2.5. Software de E/S de espacio de usuario


Aunque la mayor parte del software de E/S está dentro del sistema operativo, una pequeña parte de él consiste en
bibliotecas enlazadas a los programas de usuario, e incluso en programas completos que se ejecutan fuera del kernel.
Si bien estos procedimientos no hacen mucho más que colocar sus parámetros en el lugar apropiado para la llamada
al sistema, hay otros procedimientos de E/S que sí realizan trabajo de verdad. En particular, el formateo de entradas
y salidas se realiza mediante procedimientos de biblioteca. Un ejemplo de C es printf, que toma una cadena de
formato y posiblemente algunas variables como entrada, construye una cadena ASCII y luego invoca WRITE para
enviar la cadena a la salida. Otro ejemplo para la entrada lo constituye scanf que lee entradas y las almacena en
variables descritas en una cadena de formato que tiene la misma sintaxis que prinlf. La biblioteca de E/S estándar
contiene varios procedimientos que implican E/S, y todos se ejecutan como parte de programas de usuario.
No todo el software de E/S de nivel de usuario consiste en procedimientos de biblioteca. Otra categoría importante
es el sistema de spool. El uso de spool es una forma de manejar los dispositivos de E/S de uso exclusivo en un
sistema multiprogramado. Consideremos un dispositivo spool típico: una impresora. Aunque desde el punto de vista
técnico sería fácil dejar que cualquier proceso de usuario abriera el archivo especial por caracteres de la impresora,
podría suceder que un proceso lo abriera y luego pasara horas sin hacer nada. Ningún otro proceso podría imprimir
nada. En vez de ello, lo que se hace es crear un proceso especial, llamado genéricamente demonio, y un directorio
especial, llamado directorio de spool. Si un proceso quiere imprimir un archivo, primero genera el archivo completo
que va a imprimir y lo coloca en el directorio de spool. Corresponde al demonio, que es el único proceso que tiene
permiso de usar el archivo especial de la impresora, escribir los archivos en el directorio. Al proteger el archivo
especial contra el uso directo por parte de los usuarios, se elimina el problema de que alguien lo mantenga abierto
durante un tiempo innecesariamente largo.
El spool no se usa sólo para impresoras; también se usa en otras situaciones. Por ejemplo, es común usar un
demonio de red para transferir archivos por una red. Si un usuario desea enviar un archivo a algún lado, lo coloca en
un directorio de spool de red. Más adelante, el demonio de red lo toma de ahí y lo transmite. Una aplicación especial
de la transmisión de archivos por spool es el sistema de correo electrónico de Internet. Esta red consiste en millones
de máquinas en todo el mundo que se comunican empleando muchas redes de computadoras. Si se desea enviar
correo a alguien, se invoca un programa como send, que acepta la carta que se desea enviar y la deposita en un
directorio de spool para ser transmitida posteriormente. Todo el sistema de correo se ejecuta fuera del sistema
operativo.

8.3 Discos
Las siguientes son las principales ventajas con respecto del uso de la memoria principal como almacenamiento
[Tanenbaum]:
• Mucha mayor capacidad de espacio de almacenamiento.

Ing. Jorge Orellana A.


87
Sistemas Operativos (2010017)

• Menor precio por bit.


• La información no se pierde al apagar la computadora.
Un uso inapropiado de los discos puede generar ineficiencia, en especial en sistemas con multiprogramación.

8.3.1. Hardware de discos


Todos los discos reales están organizados en cilindros, cada uno de los cuales contiene tantas pistas como cabezas
hay apiladas verticalmente. Las pistas se dividen en sectores, de los cuales por lo regular hay entre 8 y 32 por pista
en los discos flexibles, llegando a varios cientos en algunos discos duros. Los diseños más sencillos tienen el mismo
número de sectores en todas las pistas. Todos los sectores tienen el mismo número de bytes aun cuando los sectores
cercanos al borde exterior del disco son físicamente más largos que los cercanos al centro. No obstante, el tiempo
que toma leer o escribir un sector es el mismo. La densidad de los datos obviamente es más alta en los cilindros más
interiores, y algunos diseños de disco requieren modificar la corriente de alimentación a las cabezas de lectura-
escritura para las pistas interiores. El controlador en hardware del disco se encarga de esto sin que lo vea el usuario
(o el implementador del sistema operativo). La diferencia en la densidad de los datos entre las pistas interiores y
exteriores implica un sacrificio de la capacidad, y existen sistemas más complejos. Se han probado diseños de disco
flexible que giran con mayor rapidez cuando las cabezas están sobre las pistas exteriores. Esto permite tener más
sectores en esas pistas, incrementando la capacidad del disco. Los discos duros modernos de gran capacidad también
tienen más sectores por pista en las pistas exteriores que en las interiores. Éstas son las unidades IDE (Integrated
Drive Electronics), y el complejo procesamiento que los circuitos incorporados en la unidad realizan enmascara los
detalles. Para el sistema operativo, estas unidades aparentan tener una geometría sencilla con un número constante
de sectores por pista. Los circuitos de la unidad y del controlador en hardware son tan importantes como el equipo
mecánico. El principal elemento de la tarjeta controladora que se instala en el plano posterior de la computadora es
un circuito integrado especializado, en realidad una microcomputadora pequeña. En el caso de un disco duro puede
ser que los circuitos de la tarjeta controladora sean más sencillos que para un disco flexible, pero esto es así porque
la unidad de disco duro en sí tiene un potente controlador electrónico incorporado. Una característica del dispositivo
que tiene implicaciones importantes para el controlador del disco en software es la posibilidad de que el controlador
en hardware realice búsquedas de sectores en dos o más unidades al mismo tiempo. Esto se conoce como búsquedas
traslapadas. Mientras el controlador en hardware y en software está esperando que se lleve a cabo una búsqueda en
una unidad, el controlador en hardware puede iniciar una búsqueda en otra unidad. También, muchos controladores
en hardware pueden leer o escribir en una unidad mientras buscan en otra u otras, pero un controlador de disco
flexible no puede leer o escribir en dos unidades al mismo tiempo. (La lectura o escritura requiere que el controlador
transfiera bits en una escala de tiempo de microsegundos, por lo que una transferencia ocupa casi toda su potencia
de cómputo.) La situación es diferente en el caso de los discos duros con controladores integrados en hardware, y en
un sistema provisto de más de uno de estos discos duros los controladores pueden operar simultáneamente, al menos
hasta el grado de transferir datos entre el disco y la memoria intermedia del controlador. Sin embargo, sólo es
posible una transferencia a la vez entre el controlador y la memoria del sistema. La capacidad para realizar dos o
más operaciones al mismo tiempo puede reducir considerablemente el tiempo de acceso medio.

8.3.1.1. Operación de Almacenamiento de Disco de Cabeza Móvil


Los datos se graban en una serie de discos magnéticos o platos [Deitel]. El eje común de los discos gira a una
velocidad del orden de las 7000 o más revoluciones por minuto. Se lee o escribe mediante una serie de cabezas de
lectura – escritura. Se dispone de una por cada superficie de disco y solo puede acceder a datos inmediatamente
adyacentes a ella. La parte de la superficie del disco de donde se leerá (o sobre la que se grabará) debe rotar hasta
situarse inmediatamente debajo (o arriba) de la cabeza de lectura - escritura. El tiempo de rotación desde la posición
actual hasta la adyacente al cabezal se llama tiempo de latencia. Todas las cabezas de lectura - escritura están

Ing. Jorge Orellana A.


88
Sistemas Operativos (2010017)

montadas sobre una barra o conjunto de brazo móvil. Puede moverse hacia adentro o hacia afuera, en lo que se
denomina operación de búsqueda. Para una posición dada, la serie de pistas accesibles forman un cilindro vertical.
A los tiempos de búsqueda y de latencia se debe agregar el tiempo de transmisión propiamente dicha. El tiempo total
de acceso a un registro particular involucra movimientos mecánicos y generalmente es del orden de centésimas de
segundo, aunque el tiempo de latencia sea de algunas milésimas de segundo (7 a 12 aproximadamente).

8.3.2. Software de discos


El tiempo requerido para leer o escribir un bloque de disco está determinado por tres factores:
1. El tiempo de búsqueda (el tiempo que toma mover el brazo al cilindro correcto).
2. El retraso rotacional (el tiempo que tarda el sector correcto en girar hasta quedar bajo la cabeza).
3. El tiempo real de transferencia de datos.
En casi todos los discos, el tiempo de búsqueda es mucho mayor que los otros dos, así que si reducimos el tiempo de
búsqueda mejoraremos sustancialmente el rendimiento del sistema.
Los dispositivos de disco son propensos a errores. Siempre se graba junto con los datos de cada sector de un disco
algún tipo de verificación de errores, una suma de verificación o una verificación de redundancia cíclica. Incluso las
direcciones registradas cuando se formatea un disco cuentan con datos de verificación. El controlador en hardware
de un disco flexible puede informar cuando se detecta un error, pero el software debe decidir qué se hará al respecto.
Los controladores en hardware de los discos duros con frecuencia asumen gran parte de esta carga. Sobre todo en el
caso de los discos duros, el tiempo de transferencia de sectores consecutivos dentro de la misma pista puede ser muy
rápido. Por ello, la lectura de más datos de los que se solicitan y su almacenamiento en un caché de la memoria
puede ser una estrategia muy eficaz para acelerar el acceso a disco.

8.3.2.1. Planificación de Discos


En los sistemas de multiprogramación muchos procesos pueden estar generando peticiones de E/S sobre discos
[Deitel]. La generación de peticiones puede ser mucho más rápida que la atención de las mismas, por lo que se
construyen líneas de espera o colas para cada dispositivo. Para reducir el tiempo de búsqueda de registros se ordena
la cola de peticiones: esto se denomina planificación de disco.
La planificación de disco implica:
• Un examen cuidadoso de las peticiones pendientes para determinar la forma más eficiente de servirlas.
• Un análisis de las relaciones posicionales entre las peticiones en espera.
• Un reordenamiento de la cola de peticiones para servirlas minimizando los movimientos mecánicos.
Los tipos más comunes de planificación son:
• Optimización de la búsqueda.
• Optimización rotacional (latencia).
Generalmente los tiempos de búsqueda superan a los de latencia, aunque la diferencia disminuye. Muchos
algoritmos de planificación se concentran en la reducción de los tiempos de búsqueda para un conjunto de
peticiones. La reducción de la latencia recién tiene efectos bajo cargas de trabajo muy pesadas.
Bajo condiciones de carga ligera (promedio bajo de longitud de la cola), es aceptable el desempeño del método
FCFS (primero en llegar, primero en ser servido). Bajo condiciones de carga media o pesada, es recomendable un
algoritmo de planificación de las colas de requerimientos.
Los principales criterios de categorización de las políticas de planificación son [Deitel]:
• Capacidad de ejecución.
• Media del tiempo de respuesta.
• Varianza de los tiempos de respuesta (predecibilidad).
Una política de planificación debe intentar maximizar la capacidad de ejecución, maximizando el número de
peticiones servidas por unidad de tiempo, minimizando la media del tiempo de respuesta y mejorando el rendimiento
global, quizás a costa de las peticiones individuales.
La planificación suele mejorar la imagen total al tiempo que reduce los niveles de servicio de ciertas peticiones. Se
mide utilizando la varianza de los tiempos de respuesta. La varianza es un término estadístico que indica hasta qué
punto tienden a desviarse del promedio de todos los elementos los elementos individuales. A menor varianza mayor
predecibilidad. Se desea una política de planificación que minimice la varianza, es decir que maximice la
predecibilidad. No debe haber peticiones que puedan experimentar niveles de servicio erráticos.

8.3.2.1.1. Optimización de la Búsqueda en Discos


En la mayoría de los discos, el tiempo de búsqueda supera al de retraso rotacional y al de transferencia, debido a
ello, la reducción del tiempo promedio de búsqueda puede mejorar en gran medida el rendimiento del sistema.

Ing. Jorge Orellana A.


89
Sistemas Operativos (2010017)

Las estrategias más comunes de optimización de la búsqueda son las siguientes [Deitel]:
o FCFS.
o SSF.
o SCAN.
o SCAN de N - Pasos.
o C - SCAN.
o Esquema Eschenbach.

Planificación FCFS First come - first served (Primero en Llegar, Primero en Ser Servido)
Si el controlador en software del disco acepta peticiones una por una y las atiende en ese orden, es decir, el primero
que llega, el primero que se atiende, poco puede hacerse por optimar el tiempo de búsqueda. Sin embargo, cuando el
disco está sometido a una carga pesada puede usarse otra estrategia. Es probable que, mientras el brazo está
realizando una búsqueda para atender una petición, otros procesos generen otras peticiones de disco. Muchos
controladores de disco mantienen una tabla, indizada por número de cilindro, con todas las peticiones pendientes
para cada cilindro encadenadas en listas enlazadas cuyas cabezas son las entradas de la tabla. Dado este tipo de
estructura de datos, podemos utilizar un mejor algoritmo que el del primero que llega, primero que se atiende. Para
entender este algoritmo, consideremos un disco de 40 cilindros. Llega una petición para leer un bloque que está en el
cilindro 11. Mientras se está realizando la búsqueda del cilindro 11, llegan peticiones nuevas para los cilindros 1, 36,
16, 34, 9 y 12, en ese orden, y se introducen en la tabla de peticiones pendientes, con una lista enlazada individual
para cada cilindro.

Cuando se termina de atender la petición en curso (para el cilindro 11), el controlador de disco puede escoger la
petición que atenderá a continuación. Si el controlador usara FCFS, iría después al cilindro 1, luego al 36, y así
sucesivamente. Este algoritmo requeriría movimientos del brazo de 10, 35, 20, 18, 25 y 3, respectivamente, para un
total de 111 cilindros.

Planificación SSF shortest seekfirst (Menor Tiempo de Búsqueda Primero)


En este algoritmo el controlador podría atender a continuación la petición más cercana, a fin de minimizar el tiempo
de búsqueda. Dadas las peticiones anteriores, la secuencia es 12, 9, 16, 1, 34 y 36, como indica la línea punteada en
la parte inferior de la figura. Con esta secuencia, los movimientos del brazo son 1, 3, 7, 15, 33 y 2 para un total de 61
cilindros. Este algoritmo reduce el movimiento total del brazo casi a la mitad en comparación con FCFS. SSF tiene
un problema, si siguen llegando más peticiones mientras se están procesando las peticiones anteriores. Por ejemplo,
si, después de ir al cilindro 16 está presente una nueva petición para el cilindro 8, ésta tendrá prioridad sobre el
cilindro 1. Si luego llega una petición para el cilindro 13, el brazo irá después a ese cilindro, en lugar de al 1. Si la
carga del disco es elevada, el brazo tenderá a permanecer en la parte media del disco la mayor parte del tiempo, y las
peticiones en los extremos tendrán que esperar hasta que una fluctuación estadística en la carga haga que no se
presenten peticiones cerca de la parte media. Las peticiones alejadas de la parte media podrían recibir un servicio
deficiente. Los objetivos de tiempo de respuesta mínimo y equitatividad están en conflicto aquí.

Planificación SCAN
Los edificios altos también tienen que enfrentar el trueque del algoritmo anterior. El problema de planificar un
elevador de un edificio alto es similar al de planificar un brazo de disco. Continuamente llegan llamadas que
reclaman al elevador que se dirija a otros pisos al azar. El microprocesador que controla el elevador fácilmente
podría usar FCFS para seguir la pista a la secuencia en que los clientes oprimen el botón de llamada y atenderlos;
también podría usar SSF. Sin embargo, la mayor parte de los elevadores usan un algoritmo distinto para conciliar los
objetivos en conflicto de eficiencia y equitatividad: continúan desplazándose en la misma dirección hasta que no hay
más peticiones pendientes en esa dirección, y luego se mueven en la dirección opuesta.

Ing. Jorge Orellana A.


90
Sistemas Operativos (2010017)

El algoritmo, conocido como algoritmo del elevador, requiere que el software mantenga un bit: el bit de dirección
actual, UP o DOWN. Cuando termina de atenderse una petición el controlador del disco o del elevador examina el
bit. Si éste es UP, el brazo o el elevador se mueve a la petición pendiente más cercana hacia arriba. Si no hay
peticiones pendientes en posiciones más altas, se invierte el bit de dirección. Si el bit está en DOWN, el movimiento
es hacia la siguiente posición solicitada hacia abajo, si la hay. La siguiente figura muestra el algoritmo del elevador
usando las mismas siete peticiones anteriores, suponiendo que el bit de dirección inicialmente estaba en UP. El
orden en que se atienden los cilindros es 12, 16, 34, 36, 9 y 1, que produce movimientos del brazo de 1, 4, 18, 2, 27
y 8, para un total de 60 cilindros. En este caso el algoritmo del elevador es un poco mejor que SSF, aunque
usualmente es peor.

Una propiedad agradable del algoritmo del elevador es que, dada cualquier colección de peticiones, el límite
superior del movimiento total está fijo: no puede ser mayor que dos veces el número de cilindros.

Planificación SCAN de N - Pasos


La estrategia de movimiento del brazo es como en SCAN; solo da servicio a las peticiones que se encuentran en
espera cuando comienza un recorrido particular. Las peticiones que llegan durante un recorrido son agrupadas y
ordenadas y serán atendidas durante el recorrido de regreso. Posee menor varianza de los tiempos de respuesta si se
compara con las planificaciones SSTF y SCAN convencionales.

Planificación C - SCAN (Búsqueda Circular)


El brazo se mueve del cilindro exterior al interior, sirviendo a las peticiones sobre una base de búsqueda más corta.
Finalizado el recorrido hacia el interior, salta a la petición más cercana al cilindro exterior y reanuda su
desplazamiento hacia el interior. No discrimina a los cilindros exterior e interior. La varianza de los tiempos de
respuesta es muy pequeña.

Esquema Eschenbach
El brazo del disco se mueve como en C - SCAN, pero las peticiones se reordenan para ser servidas dentro de un
cilindro para tomar ventaja de la posición rotacional y si dos peticiones trasladan posiciones de sectores dentro de un
cilindro, solo se sirve una en el movimiento actual del brazo del disco. Esta estrategia tiene en cuenta el retraso
rotacional.

Mediante trabajos de simulación y de laboratorio se demostró que la estrategia SCAN es la mejor con carga baja, la
estrategia C - SCAN es la mejor con cargas medias y pesadas y que la estrategia C - SCAN con optimización
rotacional es la mejor para cargas muy pesadas (mejor que la estrategia Eschenbach inclusive).
Generalmente, las mejoras tecnológicas de los discos, acortan los tiempos de búsqueda (seek), pero no acortan los
tiempos de demora rotacional (search). En algunos discos, el tiempo promedio de búsqueda ya es menor que el
retraso rotacional. El factor dominante será el retraso rotacional, por lo tanto, los algoritmos que optimizan los
tiempos de búsqueda (como el algoritmo del elevador) perderán importancia frente a los algoritmos que optimicen el
retraso rotacional. Una tecnología importante es la que permite el trabajo conjunto de varios discos. Una
configuración interesante es la de treinta y ocho (38) unidades ejecutándose en paralelo. Cuando se realiza una
operación de lectura, ingresan a la cpu 38 bit a la vez, uno por cada unidad. Los 38 bits conforman una palabra de 32
bits junto con 6 bits para verificación. Los bits 1, 2, 4, 8, 16 y 32 se utilizan como bits de paridad. La palabra de 38
bits se puede codificar mediante el código Hamming, que es un código corrector de errores. Si una unidad sale de
servicio, se pierde un bit de cada palabra y el sistema puede continuar trabajando; se debe a que los códigos
Hamming se pueden recuperar de un bit perdido. Este diseño se conoce como RAID; siglas en inglés de “arreglo
redundante de discos no costosos”.

Ing. Jorge Orellana A.


91
Sistemas Operativos (2010017)

8.3.2.1.2. Optimización Rotacional en Discos


En condiciones de carga pesada, las probabilidades de que ocurran referencias al mismo cilindro aumentan, por ello
resulta útil considerar la optimización rotacional además de la optimización de búsqueda [Deitel]. La optimización
rotacional es de uso común en dispositivos de cabezas fijas. La estrategia utilizada es la SLTF (tiempo de latencia
más corto primero). Situado el brazo del disco en un cilindro, examina todas las peticiones sobre el cilindro y sirve
primero a la que tiene el retraso rotacional más corto.

8.3.2.2. Manejo de Errores en Discos


Algunos de los errores más comunes en discos son [Tanenbaum]:
• Error de programación. (Ej.: Solicitar un sector no existente)
• Error temporal en la suma de verificación. (Ej.: Provocado por polvo en la cabeza).
• Error permanente en la suma de verificación. (Ej.: Un bloque del disco dañado físicamente).
• Error de búsqueda. (Ej.: El brazo se envía al cilindro 6 pero va al 7).
• Error del controlador. (Ej.: El controlador no acepta los comandos).
El manejador del disco debe controlar los errores de la mejor manera posible. La mayoría de los controladores,
verifican los parámetros que se les proporcionan e informan si no son válidos.
Respecto de los errores temporales en la suma de verificación, generalmente se eliminan al repetir la operación. Si
persisten, el bloque debe ser marcado como un bloque defectuoso, para que el software lo evite.
Otra posibilidad es que controladores “inteligentes” reserven cierta cantidad de pistas que serán asignadas en
reemplazo de pistas defectuosas. Una tabla asocia las pistas defectuosas con las pistas de repuesto. Está alojada en la
memoria interna del controlador y en el disco. La sustitución es transparente para el manejador. Puede afectarse el
desempeño de los algoritmos de búsqueda, como el del elevador, ya que el controlador utiliza pistas físicamente
distintas de las solicitadas.

Ing. Jorge Orellana A.


92

También podría gustarte