• No se han encontrado resultados

Evolución: primera, segunda, tercera y cuarta generación

N/A
N/A
Protected

Academic year: 2022

Share "Evolución: primera, segunda, tercera y cuarta generación"

Copied!
21
0
0

Texto completo

(1)

1 Introducción ... 2

1.1 Breve reseña histórica... 2

1.2 Evolución: primera, segunda, tercera y cuarta generación... 2

2 Estructura ... 4

2.1 Sistemas monolíticos ... 4

2.2 Sistemas por capas ... 5

3 Interfaces de Usuario... 5

3.1 Interfaz de línea de comandos ... 6

3.2 Interfaz gráfica... 6

4 Modos de procesamiento ... 6

4.1 Sistemas Multitareas y Monotareas ... 6

4.2 Sistemas Multiusuario y Monousuario ... 7

4.3 Sistemas Por lotes, Interactivos y de Tiempo compartido ... 7

5 Procesos ... 8

5.1 El modelo de procesos... 8

5.2 Comunicación entre procesos... 11

5.3 Concurrencia ... 11

5.4 Planificación (scheduling) de procesos... 12

6 Administración de la Memoria ... 15

6.1 Monoprogramación vs. Multiprogramación ... 15

6.2 Utilización de la memoria ... 16

6.3 Administración de Memoria Real ... 16

6.4 Administración de Memoria Virtual ... 18

7 Optimización ... 21

7.1 Fragmentación de archivos ... 21

7.2 Compresión de datos ... 21

7.3 Administración de memoria... 21

(2)

Introducción

Breve reseña histórica

Desde su creación, las computadoras digitales han utilizado un sistema de codificación de instrucciones en sistema de numeración binaria, es decir con los 0s y 1s. Esto se debe a que los circuitos integrados funcionan con este principio, es decir, hay corriente (1) o no hay corriente (0).

En el origen de la historia de las computadoras (hace unos cuarenta años), los sistemas operativos no existían y la introducción de un programa para ser ejecutado se convertía en un increíble esfuerzo que solo podía ser llevado a cabo por muy pocos expertos. Esto hacia que las computadoras fueran muy complicadas de usar y que se requiriera tener altos conocimientos técnicos para operarlas. Era tan complejo su manejo, que en algunos casos el resultado llegaba a ser desastroso.

Además, el tiempo requerido para introducir un programa en aquellas grandes máquinas de lento proceso superaba por mucho el de ejecución y resultaba poco provechosa la utilización de computadoras para resolución de problemas prácticos.

Se buscaron medios más elaborados para manipular la computadora, pero que a su vez simplificaran la labor del operador o el usuario. Es entonces cuando surge la idea de crear un medio para que el usuario pueda operar la computadora con un entorno, lenguaje y operación bien definido para hacer un verdadero uso y explotación de esta. Surgen los sistemas operativos.

Un sistema operativo es el encargado de brindar al usuario una forma amigable y sencilla de operar, interpretar, codificar y emitir las ordenes al procesador central para que este realice las tareas necesarias y especificas para completar una orden.

El sistema operativo, es el instrumento indispensable para hacer de la computadora un objeto útil. Bajo este nombre se agrupan todos aquellos programas que permiten a los usuarios la utilización de este enredo de cables y circuitos, que de otra manera serian difíciles de controlar.

Un sistema operativo es un conjunto de programas que hacen al hardware utilizable. El sistema operativo es un administrador de recursos: cpu, dispositivos de E/S, memoria, datos. Las funciones del sistema operativo son:

 Interfaz con el usuario

 Compartir información entre usuarios

 Compartir el hardware

 Recuperarse ante errores

 Etc.

Evolución: primera, segunda, tercera y cuarta generación

Los Sistemas Operativos, al igual que el Hardware de las computadoras, han sufrido una serie de cambios revolucionarios llamados generaciones. En el caso del Hardware, las generaciones han sido marcadas por grandes avances en los componentes utilizados, pasando de válvulas (primera generación) a transistores (segunda generación), a circuitos integrados (tercera generación), a circuitos integrados de gran y muy gran escala (cuarta generación). Cada generación sucesiva de hardware ha ido acompañada de reducciones substanciales en los costos, tamaño, emisión de calor y consumo de energía, y por incrementos notables en velocidad y capacidad.

Primera generación (1945-1955).

Las maquinas eran construidas con valvulas, eran enormes, ocupaban toda una habitación.

La maquina era diseñada, construida, programada y operada por un pequeño grupo de personas.

La programación se realizaba en lenguaje de maquina, y con frecuencia se utilizaban conexiones para controlar las funciones básicas de la maquina. El sistema operativo aun no se conocía.

(3)

Los problemas que resolvían eran cálculos numéricos, como por ejemplo una tabla con las funciones seno y coseno.

El modo usual de operación consistía en que un programador reservaba cierto tiempo en una hoja de reservación pegada a la pared, iba al cuarto de la maquina, insertaba su conexión en la computadora y pasaba unas horas esperando que ninguna de las 20000 o mas bulbos se quemara durante la ejecución.

A partir de la década del 50, la rutina mejoro un poco con la aparición de las tarjetas perforadas, que permitían escribir los programas en las tarjetas y leerlas, en vez de usar conexiones.

Segunda generación (1955-1965).

Aparece el transistor. Los programadores escribían sus programas primero en papel (fortran o assembler) y lo pasaban a tarjetas perforadas. Luego le entregaban la tarjeta perforada al operador.

El operador le entregaba al programador la salida por impresora del programa. Se perdía mucho tiempo mientras el operador hacia sus tareas de cargar programas y retirar la salida de la impresora. Dado que el costo de estos equipos eran muy altos, se buscaron formas de reducir el tiempo perdido.

La solución fue el sistema por lotes (figura 1.1), que consistía en juntar conjuntos o lotes de tareas similares que se leían con el lector de tarjetas perforadas y se almacenaban en una cinta, esta tarea se realizaba en una maquina económica, de baja capacidad de procesamiento, que eran útiles para leer tarjetas, copiar cintas e imprimir.

Luego de armar el lote de tareas, se llevaba la cinta a la maquina que realizaba el procesamiento, esta era muy cara y de alta capacidad de procesamiento para esos tiempos.

El operador cargaba un programa especial que leía la primer tarea de la cinta y la ejecutaba, la salida se escribía en una segunda cinta. Este programa fue el precursor de los sistemas operativos. Después que finalizaba cada tarea, el sistema operativo cargaba la siguiente y así sucesivamente hasta finalizar todas la tareas contenidas.

Luego el operador remplazaba las cintas de entrada y salida, la cinta de entrada se remplazaba con una nueva que contenía otro lote para su ejecución, la cinta de salida se llevaba a una maquina económica de bajo procesamiento, que realizaba la impresión off-line.

Las grandes computadoras de segunda generación se utilizaron principalmente para los cálculos científicos y de ingeniería, por ejemplo, en la resolución de ecuaciones diferenciales parciales. Por lo general se programaba en lenguaje FORTRAN y assembler.

Tercera generación (1956-1980).

Aparecen los circuitos integrados. Aparecen nuevas técnicas, como la multiprogramación y los sistemas de tiempo compartido.

Antiguamente mientras un proceso esperaba que finalice alguna operación de entrada-salida, el procesador permanecía inactivo. La multiprogramación consiste en dividir la memoria en sectores y cargar varios procesos, cada uno en un sector de memoria, cuando uno de ellos necesita esperar que una operación de entrada-salida finalice, otro proceso puede utilizar el procesador. Si la cantidad de procesos que se cargan en memoria simultáneamente son los suficientes, el procesador permanecerá ocupado casi el 100% del tiempo. En los sistemas de

(4)

tiempo compartido, cada usuario tenía una terminal online, donde podía enviar comandos y ver su salida en forma interactiva. Igual que en multiprogramación, se cargan varios procesos simultaneamente en memoria, la conmutación entre procesos no solo sucedía cuando el proceso estaba esperando que alguna operación de entrada-salida finalice, también se conmutaba de proceso cada cierto intervalo, dándole la apariencia a cada usuario que poseía la maquina para el solo.

Gracias a los circuitos integrados, hicieron su aparición la minicomputadoras, que eran computadoras muy reducidas en sus capacidades de procesamiento, pero muy económicas (solo 120.000 dólares, el 5% de una computadora de gran porte). De esta forma, la computación dejo de ser una industria solo para las grandes corporaciones.

Cuarta generación (1980-presente).

Aparecen los circuitos integrados de gran escala (contienen miles de transistores en un centímetro cuadrado de cilicio), esto hace disminuir costos y aparecen las computadoras personales. También aparece el software para computadoras personales, desarrollado para gente que no conoce nada de computadoras y no tiene intenciones de aprender.

Aparecen los sistemas multiprocesador, los procesadores comparten la memoria y el reloj.

Se incrementa la capacidad de procesamiento y la confiabilidad, así como baja su precio.

 Multiprocesamiento simétrico: Cada procesador ejecuta una copia del sistema operativo

 Multiprocesamiento asimétrico: Cada procesador tiene asignado una tarea especifica, existe un procesador master que asigna tareas a los procesadores esclavos.

Aparecen los sistemas operativos para redes, donde el usuario puede dar ordenes a una computadora en forma remota, copiar archivos de una computadora a otra, etc.

Aparecen los sistemas distribuidos. Cada procesador tiene su memoria y reloj local, se comunican con otros procesadores a través de una red. Los procesos se ejecutan en distintos procesadores, unidos mediante una red, sin que el usuario se entere.

Aparecen los sistemas en tiempo real, se utiliza cuando las tareas se deben realizar dentro de un limite de tiempo.

Estructura

Internamente los sistemas operativos estructuralmente se clasifican según como se hayan organizado internamente en su diseño, por esto la división más común de los S.O. es:

o Sistemas monolíticos o Sistemas por capas

Sistemas monolíticos

En este esquema, el sistema operativo se describen como un conjunto de procedimientos, cada uno de los cuales puede llamar a cualquiera de los otros siempre que lo necesite. Cuando se emplea esta técnica, cada procedimiento del sistema tiene una interfaz bien definida en términos de parámetros y resultados, y cada una tiene la libertad de llamar a cualquiera otra, si la última ofrece algún cálculo útil que la primera necesite.

Para construir el programa objeto real del sistema operativo cuando se usa este método, se compilan todos los procedimientos individuales a archivos que contienen los procedimientos y después se combinan todos en un solo archivo objeto con el enlazador.

En términos de ocultamiento de información, esencialmente no existe ninguno; todo procedimiento es visible para todos (al contrario de una estructura que contiene módulos o paquetes, en los cuales mucha información es local a un módulo y sólo pueden llamar puntos de registro designados oficialmente del exterior del módulo)

Esta organización sugiere una estructura básica del sistema operativo:

1.- Un programa central que invoque el procedimiento de servicio solicitado (Shell o Kernel) 2.- Un conjunto de procedimientos de servicios que realice las llamadas al sistema.

3.- Un conjunto de procedimientos de uso general que ayude a los procedimientos de servicio.

(5)

Sistemas por capas

Estos sistemas operativos se organizan como una jerarquía de estratos, cada uno construido arriba del que está debajo de él. Un ejemplo puede ser un sistema de 6 capas, que se detalla a continuación:

o El estrato 0 trabaja con la distribución del procesador, cambiando entre procesos cuando ocurren interrupciones o los relojes expiran. Sobre el estrato 0, el sistema consta de procesos secuenciales, cada uno de los cuales puede programarse sin tener que preocuparse por el hecho de que múltiples procesos estén corriendo en un solo procesador. En otras palabras, el estrato 0 ofrece la multiprogramación básica de la CPU.

o El estrato 1 realiza el manejo de memoria. Este distribuye el espacio de memoria para procesos. Sobre el estrato 1, los procesos no necesitan preocuparse si esta en la memoria o en el disco; el software del estrato 1 se hace cargo de asegurar que las porciones del proceso se trajeran a memoria (desde el disco) siempre que se necesitaran.

o El estrato 2 manejaba la comunicación entre cada proceso y la consola de operador. Por encima de este estrato, cada proceso tiene su propia consola de operador.

o El estrato 3 se hace cargo de manejar los dispositivos de E/S y de separar la información entrante y saliente de ellos. Para realizar la comunicación, utiliza búferes de memoria en cada proceso, para guardar la información que no puede ser pasada directamente entre ellos. Sobre el estrato 3 cada proceso puede trabajar con dispositivos de E/S abstractos con propiedades agradables, en vez de dispositivos reales con muchas peculiaridades.

o El estrato 4 es donde se encuentran los programas de los usuarios. Estos no tienen que preocuparse por el manejo de los procesos, memoria, consola o E/S.

o El proceso operador del sistema se localiza en el estrato 5.

Esta estructura se utiliza actualmente, y se puede visualizar mejor si realizamos círculos concéntricos representando cada capa:

Interfaces de Usuario

Todo sistema Operativo necesita tener una interfaz de Usuario. Ésta es la encargada de interpretar los comandos ingresados por el usuario y traducirlos en instrucciones al sistema operativo. Existen diferentes tipos de interfaces de usuario:

 Interfaz de línea de comandos

(6)

 Interfaz gráfica

 Interfaz por menú

Los dos tipos más comunes de interfaces son la interfaz de línea de comandos y la interfaz gráfica.

Interfaz de línea de comandos

Es la forma de interfaz entre el sistema operativo y el usuario en la que el usuario escribe los comandos utilizando un lenguaje de comandos especial (ver figura 3.1). Los sistemas con interfaces de líneas de comandos se consideran más difíciles de aprender y utilizar que los de las interfaces gráficas. Sin embargo, los sistemas basados en comandos son por lo general programables, lo que les otorga una flexibilidad que no tienen los sistemas basados en gráficos carentes de una interfaz de programación.

Los Sistemas Operativos más comunes que tienen interfaz de línea de comandos son el D.O.S., Unix y Linux (vale aclarar que tanto Unix como las distintas distribuciones de Linux, proveen una interfaz gráfica que se puede instalar como adicional).

Interfaz gráfica

Es el tipo de visualización que permite al usuario elegir comandos, iniciar programas y ver listas de archivos y otras opciones utilizando las representaciones visuales (iconos) y las listas de elementos del menú.

Generalmente se encuentran organizadas en “ventanas” (secciones de la pantalla) que representan una aplicación (o parte de una). Las selecciones pueden activarse bien a través del teclado o con el mouse.

Para los autores de aplicaciones, las interfaces gráficas de usuario ofrecen un entorno que se encarga de la comunicación con el ordenador o computadora.

Esto hace que el programador pueda concentrarse en la funcionalidad, ya que no esta sujeto a los detalles de la visualización ni a la entrada a través del mouse o el teclado. También permite a los programadores crear programas que realicen de la misma forma las tareas más frecuentes, como guardar un archivo, porque la interfaz proporciona mecanismos estándar de control como ventanas y cuadros de diálogo. Otra ventaja es que las aplicaciones escritas para una interfaz gráfica de usuario son independientes de los dispositivos: a medida que la interfaz cambia para permitir el uso de nuevos dispositivos de entrada y salida, como un monitor de pantalla grande, una nueva impresora o un dispositivo óptico de almacenamiento, las aplicaciones pueden utilizarlos sin necesidad de cambios.

Los Sistemas Operativos más comunes que tienen interfaz gráfica son Microsoft Windows, MacOS (de Apple), OS/2 (de IBM) y las interfaces gráficas para Unix y Linux (GNOME, KDE, CDE)

Modos de procesamiento

Los S.O. pueden procesar las tareas y comunicarse con el usuario de diferentes modos. Entre los diferentes modos podemos nombrar: monotareas, multitareas, monousuario, multiusuario, interactivos, por lotes, de tiempo compartido, etc.

En este Tema vamos a ver cada uno de estos modos, pero vamos a organizarlos de acuerdo a sus características comunes. Primero veremos las distintas formas de procesar las tareas, donde veremos las características de los sistemas multitareas y monotareas. Luego veremos las distintas formas de aceptar los usuarios (sistemas monousuarios y multiusuarios) y por último veremos la forma en que ejecutan los comandos (en tiempo real, por lotes, de tiempo compartido).

Sistemas Multitareas y Monotareas

Sistema Operativo Multitareas: Es el modo de funcionamiento disponible en algunos sistemas operativos, mediante el cual la computadora procesa varias tareas al mismo tiempo. Existen varios tipos de multitareas, la conmutación de contextos, la multitarea cooperativa y multitarea de tiempo compartido. La conmutación de contextos (context Switching) es un tipo muy simple de multitarea en el que dos o más aplicaciones se cargan al mismo tiempo, pero en el que solo

(7)

se esta procesando la aplicación que se encuentra en primer plano (la que ve el usuario). Para activar otra tarea que se encuentre en segundo plano, el usuario debe traer al primer plano la ventana o pantalla que contenga esa aplicación. Dos ejemplos de Sistemas Operativos con multitarea por conmutación de contextos son el Novell DOS y la dupla D.O.S.-Windows 3.x.

En la multitarea cooperativa, la que se utiliza en el sistema operativo Macintosh, las tareas en segundo plano reciben tiempo de procesado durante los tiempos muertos de la tarea que se encuentra en primer plano (por ejemplo, cuando esta aplicación esta esperando información del usuario), y siempre que esta aplicación lo permita. En los sistemas multitarea de tiempo compartido, como OS/2, cada tarea recibe la atención del microprocesador durante una fracción de segundo. Para mantener el sistema en orden, cada tarea recibe un nivel de prioridad o se procesa en orden secuencial. Dado que el sentido temporal del usuario es mucho más lento que la velocidad de procesamiento del ordenador, las operaciones de multitarea en tiempo compartido parecen ser simultáneas.

Sistema Operativo Monotareas: Los sistemas operativos monotareas son más primitivos y es todo lo contrario al visto anteriormente, es decir, solo pueden manejar un proceso en cada momento o que solo puede ejecutar las tareas de una en una. Por ejemplo cuando la computadora esta imprimiendo un documento, no puede iniciar otro proceso ni responder a nuevas instrucciones hasta que se termine la impresión. El Sistema Operativo monotareas más común es el D.O.S.

Sistemas Multiusuario y Monousuario

Sistemas Operativos Monousuario: Los sistemas monousuarios son aquellos que nada más puede atender a un solo usuario, gracias a las limitaciones creadas por el hardware, los programas o el tipo de aplicación que se este ejecutando.

Estos tipos de sistemas son muy simples, porque todos los dispositivos de entrada, salida y control dependen de la tarea que se esta utilizando, esto quiere decir, que las instrucciones que se dan, son procesadas de inmediato; ya que existe un solo usuario. Y están orientados principalmente por los microcomputadores. Ejemplo de un sistema operativo monousuarios es el D.O.S.

Sistema Operativo Multiusuario: Es todo lo contrario a monousuario; y en esta categoría se encuentran todos los sistemas que cumplen simultáneamente las necesidades de dos o más usuarios, que comparten mismos recursos. En otras palabras consiste en el fraccionamiento del tiempo (timesharing). Este tipo de sistemas se emplean especialmente en redes.

Generalmente piden una identificación de ingreso al sistema (lo que comúnmente se llama login) que puede estar acompañado por una contraseña, y mantienen distintos perfiles para los usuarios donde se especifican los distintos permisos de acceso a archivos o ejecución de programas. Cada usuario puede configurar ciertas características de funcionalidad del S.O. que se cargarán cada vez que inicie el sistema. Existen varios Sistemas operativos multiusuario, y los mas comunes son Windows NT/2000, Unix, Linux.

Sistemas Por lotes, Interactivos y de Tiempo compartido

Sistemas de Secuencia por Lotes: La secuencia por lotes o procesamiento por lotes en microcomputadoras es la eje cución de una lista de comandos del sistema operativo uno tras otro sin intervención del usuario. Procesamiento por lotes también puede referirse al proceso de almacenar transacciones durante un cierto lapso antes de su envío a un archivo maestro, por lo general una operación separada que se efectúa durante la noche. Los sistemas operativos por lotes (batch) tratan los programas por grupos (lotes) en vez de individualmente.

La función de estos sistemas operativos consiste en cargar en memoria un programa desde la unidad de almacenamiento (cinta, disco, etc.) y ejecutarlo. Al final de la ejecución, se realiza un salto a una dirección de memoria desde donde el S.O. reasumía el control y cargaba el siguiente programa y lo ejecutaba. De esta manera el tiempo entre un trabajo y otro disminuye considerablemente. Un ejemplo de Sistema Operativo por lotes es el antiguo OS/360 de IBM.

Sistemas en Tiempo Real: Un sistema operativo en tiempo real procesa las instrucciones recibidas al instante, y una vez que han sido procesadas muestra el resultado. Este tipo de S.O. tiene relación con los sistemas operativos con actividades críticas, donde se necesita una respuesta inmediata a los requerimientos, sin tener que esperar por su turno de ejecución o

(8)

turnar su procesamiento con otros procesos. Generalmente son sistemas monousuarios, ya que existe un solo operador y no necesita compartir el procesador entre varias solicitudes.

Su característica principal es dar respuestas rápidas; por ejemplo en un caso de peligro se necesit arían respuestas inmediatas para evitar una catástrofe.

Sistemas de Tiempo Compartido: El tiempo compartido en ordenadores o computadoras consiste en el uso de un sistema por más de una persona al mismo tiempo. El tiempo compartido ejecuta programas separados de forma concurrente, intercambiando porciones de tiempo asignadas a cada programa (usuario). En este aspecto, es similar a la capacidad de multitareas que es común en la mayoría de los microordenadores o las microcomputadoras.

Sin embargo el tiempo compartido se asocia generalmente con el acceso de varios usuarios a computadoras más poderosas (mainframes o servidores) y a organizaciones de servicios, mientras que la multitarea esta relacionada con las microcomputadoras e implica la realización de múlt iples tareas por un solo usuario.

Procesos

El concepto central dentro de cualquier sistema operativo es el de proceso: una abstracción de un programa en ejecución. Todo lo demás se articula en torno de este concepto. Por este motivo dedicaremos un capitulo completo al estudio de ellos.

El modelo de procesos

Proceso vs. programa.

Un programa es un archivo o conjunto de archivos que contienen código ejecutable, el programa normalmente esta ubicado en un disco. Se denomina proceso a un programa cuando se encuentra en ejecución. Los sistemas modernos pueden ejecutar varios procesos a la vez, el procesador ejecuta un proceso durante un determinado tiempo y luego conmuta a otro, esto da la sensación de paralelismo. Por eso muchos programadores hablan de seudoparalelismo.

Puede suceder que el mismo programa se ejecute varias veces simultáneamente, produciendo varios procesos.

Por ejemplo supongamos dos usuarios en un sistema unix , user1 y user2, ambos están utilizando el programa ls, user1 esta listando sus archivos (ls /home/user1) y el user2 también (ls /home/user2). Ambos utilizan el mismo programa pero generan dos procesos distintos.

Ambos procesos se están ejecutando simultáneamente (en realidad, utilizando el concepto de seudoparalelismo).

Memoria utilizada por un proceso.

Los procesos tienen su memoria dividida en 3 segmentos: segmento de texto (código ejecutable), segmento de datos (variables, etc) y segmento de stack, como se muestra en la figura.

Entre los segmentos de datos y stack hay un espacio de memoria libre, se deja para que el segmento de datos pueda crecer hacia arriba y el segmento de snack puede crecer hacia abajo.

(9)

El segmento de stack crece o disminuye automáticamente cuando sea necesario, el de datos crece o disminuye cuando el proceso lo solicita, generalmente mediante una llamada al sistema. Además de estos segmentos, cada proceso posee un PCB (process control block).

Si en el sistema se ejecuta un programa varias veces, genera varios procesos, que comparten el segmento de texto.

Tomemos el ejemplo anterior que decía así: Dos usuarios en un sistema unix, user1 y user2, están utilizando el programa ls. user1 esta listando sus archivos (ls /home/user1) y el user2 también (ls /home/user2). Esto genera dos procesos distintos, pero comparten el segmento de texto (código del programa).

Estados de un proceso.

Un proceso puede estar en 3 estados:

 Corriendo

 Listo

 Bloqueado

Un proceso se encuentra en el estado denominado corriendo, cuando el procesador lo esta ejecutando, o sea esta haciendo uso del procesador. Después de permanecer en el estado corriendo durante un cierto intervalo de tiempo, el sistema operativo conmuta a otro proceso, quedando el anterior en estado listo, es decir listo para ser ejecutado próximamente.

La conmutación entre procesos la realiza una parte del sistema operativo llamada scheduler y la selección del proceso al cual se conmuta se realiza utilizando un algoritmo denominado algoritmo de scheduling.

Los procesos poseen mecanismos para intercomunicarse, por ejemplo el siguiente comando produce una intercomunicación entre dos procesos en un unix:

cat file | more

El proceso cat genera una salida, la cual se redirecciona al proceso more, el proceso more espera por los datos enviados por cat y los visualiza en pantalla, estos dos procesos se intercomunican mediante un pipe (representado por el carácter “|”).

Dependiendo de la velocidad de ambos procesos, puede suceder que el proceso more se encuentre en el estado corriendo, pero no tenga datos en su entrada para visualizar, entonces debe pasar al estado bloqueado hasta que haya datos disponibles en su entrada.

Cuando un proceso se encuentra en el estado bloqueado, debe pasar primero al estado listo para luego poder pasar al estado corriendo. En la figura se visualiza un diagrama de estados y las flechas indican las posibles transiciones.

Un proceso se bloquea cuando no puede continuar su ejecución, generalmente se produce cuando espera por datos en su entrada y aun no están disponibles.

Por ejemplo el intérprete de comandos (shell) permanece bloqueado hasta que el usuario ingresa una orden.

Se visualizan cuatro posibles transiciones indicadas por las flechas, veamos cuando se producen estas transiciones. Las transiciones 1 y 2 entre los estados corriendo y listo las realiza el scheduler. La transición de corriendo a listo se produce cuando el scheduler decide

(10)

que el proceso corrió lo suficiente y conmuta a otro proceso en estado listo. La transición de listo a corriendo se produce cuando el scheduler decide entregarle el procesador a un proceso que se encuentra listo.

La transición 4, de corriendo a bloqueado se produce cuando el proceso no puede continuar, debido a que debe esperar que suceda algún evento externo, el scheduler conmuta a otro proceso que se encuentre listo.

Por ultimo, la transición 3, de bloqueado a listo se produce cuando sucede el evento que se estaba esperando, una vez en el estado listo el proceso esta dentro del conjunto de los elegibles por el scheduler para pasar a corriendo. Cuando se crea un proceso nuevo, se carga en memoria y se le asigna el estado listo, la finalización de un proceso se produce cuando esta en el estado ejecutando, devolviendo los recursos que utilizaba.

Veamos como ejemplo el intérprete de comandos: supongamos que el intérprete de comando se encuentra en el estado corriendo, pero pasa a bloqueado, esperando que el usuario ingrese una orden Cuando el usuario pulsa una tecla, se produce una interrupción de teclado, esto hace que el procesador deje de ejecutar el proceso en curso y ejecute el proceso de atención de teclado.

El intérprete de comandos pasa al estado listo, esperando que el scheduler le asigne el procesador para leer la tecla pulsada.

El scheduler le entrega el procesador al intérprete de comandos, este último lee la tecla pulsada.

PCB.

Como ya dijimos, el sistema operativo administra todos los recursos de la computadora, para ello debe saber qué recursos están siendo utilizados y por quien. Para realizar esta tarea mantiene un conjunto de estructuras de datos para los recursos que administra, por ej.:

dispositivos de entrada-salida, memoria, procesos, archivos, etc.

Para administrar procesos, el sistema operativo mantiene por cada uno de ellos, una estructura de datos, denominada PCB (process control block), la cual contiene información de los recursos utilizados por el proceso. El PCB de un proceso se debe actualizar antes de conmutar del estado corriendo a listo o bloqueado, para luego poder continuar la ejecución desde el lugar donde se abandonó.

Después que se conmutó a otro, se debe actualizar el PCB del proceso, como por ejemplo cambiar su estado de listo a corriendo.

Algunos de los datos contenidos en la estructura serán:

 Registros del procesador.

 Program counter y stack pointer.

 Estado del proceso.

 Tiempo que utilizó el procesador.

 Identificador de proceso (PID).

 etc.

El proceso, además de utilizar el procesador, utiliza otros recursos, como memoria, archivos, etc. Por esto, el PCB contendrá punteros a otras estructuras, que almacenan información de los recursos utilizados por él.

Hilos de ejecución.

Hasta ahora, solo existía un program counter (PC) en cada proceso, esto se denomina heavyweight processes.

Existe la posibilidad de que dentro de un proceso existan varios hilos de ejecución, cada uno con su PC, conjunto de registros del procesador y segmento de stack, pero comparten con los demás hilos de ejecución el segmento de datos, el segmento de texto, recursos del sistema y el PCB. Este tipo de procesamiento se denomina threads o lightweight processes. El PCB contendrá información por cada hilo de ejecución, como el PC, registros del procesador, estado del hilo de ejecución, etc.

 Ventajas:

(11)

Se puede compartir la memoria de datos entre procesos. La conmutación entre distintos hilos de ejecución de un mismo proceso es mas rápida que la conmutación de procesos, solo se requiere del cambio de los registros del procesador y el estado.

 Implementación:

Los hilos de ejecución se pueden implementar a nivel usuario o a nivel kernel.

A nivel usuario el sistema operativo no se entera de la existencia de hilos de ejecución. La conmutación entre hilos se realiza sin intervención del sistema operativo, esto hace que sea muy rápido. La desventaja es que si un hilo de ejecución se bloquea, el sistema operativo bloquea todo el proceso, bloqueando los demás hilos.

A nivel kernel, el sistema operativo conoce la existencia de ellos, la conmutación se realiza mediante la intervención del sistema operativo, como por ejemplo mediante una llamada al sistema, la cual es mas lenta, pero si un hilo se bloquea el scheduler seleccionara un hilo del mismo proceso o de algún otro proceso. El kernel debe mantener una zona de memoria por hilo de ejecución, conteniendo registros del procesador, estado, etc.

Algunos sistemas soportan ambos tipos de implementación.

Comunicación entre procesos

El sistema operativo nos provee mecanismos para que los procesos se puedan intercomunicar.

La intercomunicación se puede realizar utilizando o no almacenamiento compartido.

Almacenamiento compartido.

Se comparte algún medio de almacenamiento entre ambos procesos, como un archivo, variable o buffer. Ambos procesos pueden leer y escribir el recurso compartido, esto puede traer problemas de concurrencia, dado que si ambos procesos intentan escribir en el espacio de almacenamiento, pueden bloquearse mutuamente.

Sistema de intercambio de mensajes.

Mecanismo de comunicación entre procesos sin utilizar variables compartidas. Se utilizan llamadas al sistema para enviar y recibir mensajes:

send(destino, &mensaje) recieve(origen, &mensaje)

Se puede implementar de distintas formas:

 Comunicación directa.

Los mensajes se envían directamente entre procesos, cada proceso debe ser identificado con un nombre. En las llamadas al sistema se especifica el nombre de los procesos.

send(destino, &mensaje) recieve(origen, &mensaje)

 Comunicación indirecta.

Los mensajes son enviados indirectamente, mediante una casilla de mensajes (mailbox), la cual almacena los mensajes que aun no aceptó el proceso destino.

Estas casillas pueden almacenar hasta cierta cantidad de mensajes, especificado cuando se crea.

En los parámetros dirección de las llamadas al sistema, se especifica el nombre de la casilla de mensajes.

send(mailbox, &mensaje) recieve(mailbox, mensaje)

Si la casilla esta llena, la llamada a send() bloqueará al proceso origen.

Concurrencia

Introducción.

Estudiaremos que sucede cuando varios eventos ocurren simultáneamente, por ejemplo:

 Varios procesos acceden al mismo tiempo a una zona de memoria compartida (comunicación entre procesos).

(12)

 Varios procesos utilizan al mismo tiempo un dispositivo de entrada-salida.

 Varios usuarios leen o escriben al mismo tiempo el mismo archivo.

 etc.

Normalmente cuando se escribe un programa, no se tiene en cuenta que otros programas se ejecutarán al mismo tiempo. Por ejemplo si se necesita escribir un archivo a disco, se asume que el disco esta disponible para el solo, pero en realidad otros programas pueden tratar de escribir el disco simultáneamente.

Estudiaremos los mecanismos que nos provee el sistema operativo para que varios procesos puedan acceder simultáneamente a un recurso.

Condiciones de carrera.

Consideremos un ejemplo, dos procesos acceden a una variable compartida, incrementando su valor en uno. El incremento de una variable consiste de 3 pasos:

1. Cargar el valor de la variable en un registro del procesador.

2. Incrementar el contenido del registro.

3. Escribir el contenido del registro a memoria

Cuando un proceso trata de incrementar el valor de la variable, ejecutará:

read inc write

Consideremos un caso de dos procesos A y B que tratan de incrementar al mismo tiempo el valor de la variable:

El proceso A quiere incrementar el valor de la variable y ejecuta read.

El scheduler conmuta al proceso B, que también quiere incrementar el valor de la variable, ejecuta read, inc y write.

Luego se conmuta al proceso A, que aún no finalizó de realizar el incremento, ejecuta inc y write.

La variable sólo se incrementó en una unidad, se perdió uno de los incrementos. El problema se produjo porque el proceso B accedió al recurso compartido cuando el proceso A aun no había terminado. Situaciones como esta, donde dos o mas procesos acceden a un recurso compartido y el resultado final depende como se ejecuten los procesos se llaman condiciones de carrera.

secciones críticas.

Cualquier recurso que puede ser alterado por dos o mas procesos al mismo tiempo se denomina recurso crítico.

Una sección crítica es la parte del programa que accede al recurso compartido. Debemos encontrar alguna manera de prohibir que dos procesos accedan a un recurso compartido al mismo tiempo, o sea una exclusión mutua.

Esta condición es suficiente para eliminar condiciones de carreras, pero no para que los procesos usen eficientemente el recurso compartido. Las condiciones son las siguientes:

 Solo un proceso debe estar dentro de la sección crítica (exclusión mutua).

 No se debe asumir nada acerca de la velocidad y el número de procesadores.

 Un proceso fuera de su sección crítica no puede bloquear a otros procesos.

 Los procesos no deben esperar indefinidamente para acceder a su sección crítica.

Planificación (scheduling) de procesos

Cuando hay más de un proceso que está en condiciones de ejecutarse en la CPU, se debe escoger alguno. El encargado de tomar esa decisión es el planificador o scheduler, y el algoritmo que usa se llama algoritmo de planificación.

Existen varios criterios que puede tomar el planificador para seleccionar el siguiente proceso a ejecutar. Algunos de ellos pueden ser:

 Equidad. Asegurarse que todos los procesos tengan su turno de CPU.

 Eficacia. Mantener la CPU ocupada todo el tiempo.

 Tiempo de respuesta. Minimizar el tiempo de respuesta de los usuarios interactivos.

(13)

 Rendimiento (throughput). Maximizar el número de trabajos terminados por hora.

 Tiempo de espera. Minimizar el tiempo medio de espera (en la cola READY) de los procesos.

Una complicación adicional que hay que tener presente es que cada proceso es único e impredecible. Algunos son intensivos en entrada/salida, es decir, pierden la mayor parte del tiempo esperando por E/S; otros son intensivos en CPU, es decir, requieren más que nada tiempo de CPU. En cualquier caso, todos los procesos alternan entre una fase de ejecución de CPU y otra de espera por E/S. Aunque la duración de las fases de CPU es impredecible y varía mucho entre un proceso y otro, tiende a tener una frecuencia como la de la figura 5.3: hay un gran número de fases de CPU cortos, y muy pocos largos. Esta Información puede ser importante para seleccionar un algoritmo de planificación adecuado.

¿Cuándo hay que planificar? Recordando el diagrama de estados, una decisión de planificación puede o debe tomarse cuando ocurre cualquiera de las siguientes transiciones entre estados de un proceso:

1. EJECUTANDO a BLOQUEADO.

2. EJECUTANDO a TERMINADO.

3. EJECUTANDO a LISTO.

4. BLOQUEADO a LISTO.

En los casos 1 y 2, necesariamente hay que escoger un nuevo proceso, pero en los casos 3 y 4 podría no tomarse ninguna decisión de scheduling, y dejar que continúe ejecutando el mismo proceso que estaba ejecutando. En ese caso, se habla de planificación no-expropiadora (non- preemptive, como en Windows 3.x).

Si en cambio se toma una decisión de scheduling en los casos 3 y 4, entonces se habla de planificación expropiadora (porque se suspende un proceso sin dejarlo terminar para asignarle tiempo de CPU a otro).

Esta última es más segura y más justa, pero tiene un problema: consideremos dos procesos que comparten Información, y que a uno de ellos se le quita la CPU justo cuando estaba en medio de una actualización de los datos compartidos.

Cuando sea el turno del segundo proceso, éste podría intentar leer los datos cuando están en un estado inconsistente. Este problema se remedia con sincronización.

Analizaremos ahora algunos algoritmos específicos de scheduling:

El primero que llega se atiende primero

FCFS (First-come, first-served) por sus siglas en inglés. Es un algoritmo que no usa expropiación, y que consiste en atender a los procesos por estricto orden de llegada a la cola 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.

La ventaja de este algoritmo es que se trata de un algoritmo muy simple: la cola READY se maneja como una simple cola FIFO. El problema es que el algoritmo no es adecuado para los sistemas de propósito general.

Primero el trabajo más corto

SJN (shortest-job-next) por sus siglas en inglés. En este algoritmo se procesa primero el trabajo que tenga el menor tiempo de CPU de los que se encuentran el la cola READY.

O sea, 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 el menor tiempo de procesamiento 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, y ejecutar el proceso con menor tiempo estimado de ejecución.

Es importante aclarar que este algoritmo es óptimo unicamente cuando se dispone de todas las tareas de forma simultánea.

(14)

Round-robin (RR)

Volviendo a FCFS, una forma obvia de mejorarlo es agregando expropiación o preemption de manera que cada proceso no retenga la CPU por más de un quantum o cantidad de tiempo predefinida. FCFS con tajada de tiempo se conoce como round-robin, y se implementa igual que FCFS, sólo que antes de cederle la CPU a un proceso se mantiene un contador de tiempo (timer) para que provoque una interrupción dentro de un quantum de tiempo. El proceso se ejecuta hasta que haga una llamada bloqueante o hasta que use todo su quantum. En cualquiera de los dos casos, la CPU se entrega al siguiente proceso en la cola READY. El round-robin es muy fácil de implementar. Odo lo que necesita el planificador es mantener un alista de procesos ejecutables. Cuando el quantum de un proceso se consume, se le coloca al final de la lista.

Planificación por prioridad

La planificación por prioridad es la planificación en la cual a cada proceso se le asigna una prioridad y la CPU se asigna al proceso con mayor prioridad en la cola READY. SJN es un caso especial de planificación por prioridad. SJN es planificación por prioridad donde la prioridad es función del estimador de la duración de la próxima fase de CPU.

Hay muchas criterios para definir la prioridad. Ejemplos:

 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.

Múltiples colas

Para complicar más la cosa, podemos agrupar los procesos en distintas clases, y usar distintos algoritmos de planificación intra-clase, más algún algoritmo interclases.

Por ejemplo, los procesos interactivos y los procesos por lotes tienen distintos requerimientos en cuanto a tiempos de respuesta. Entonces, podemos planificar los procesos interactivos usando Round-Robin, y los procesos por lotes según FCFS, teniendo los primeros prioridad absoluta sobre los segundos.

Una forma de implementar este algoritmo es dividiendo la cola READY en varias colas, según la categoría del proceso. Por ejemplo, podemos tener diferentes colas, cada una 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 Round-Robin, a la de procesos por lote es el 5% para que asigne a sus procesos según FCFS, y a las otras el resto.

Por otra parte, podríamos 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 x 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 x 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.

(15)

Así los procesos de fases más cortas tienen prioridad. Este algoritmo es uno de los más generales, pero también uno de los más complejos de implementar. También es difícil de afinar, pues hay múltiples parámetros que definir.

Administración de la Memoria

Monoprogramación vs. Multiprogramación

El esquema más sencillo de administración de memoria es aquel en el que se tiene un solo proceso en memoria en cada instante; pero por contraparte, si el sistema está organizado de esta forma, sólo se puede ejecutar un proceso a la vez. Cuando el proceso termina, se carga en memoria un nuevo proceso para ser ejecutado.

Este modelo tiene muchas desventajas si lo comparamos con sistemas operativos multiprogramación. Existen muchas razones para utilizar multiprogramación:

 Facilita la programación de aplicaciones al dividirla en dos o más procesos.

 Permite proporcionar servicio a varios usuarios simultáneamente.

 Permite la programación de aplicaciones más complejas.

 Hace un mejor aprovechamiento del tiempo de procesador.

Vamos a centrarnos en el último punto para poder comprenderlo mejor:

La mayoría de los procesos tardan una porción sustancial de su tiempo en la espera de trabajos de entrada/salida al disco. Es común que un proceso permanezca en un ciclo, leyendo bloques del disco, para poder realizar luego un cálculo con el contenido de los bloques. Si, por ejemplo, tarda 40 mseg. en leer un bloque, el cálculo tarda 10 mseg. y si utilizamos la monoprogramación, la CPU estará inactiva, esperando los datos del disco, el 80% del tiempo.

Si en cambio utilizáramos la multiprogramación, suponiendo que tenemos procesos promedios que realizan cálculos sólo durante un 20% del tiempo que permanecen en memoria y contamos con 5 procesos en memoria, la CPU estaría ocupada el 100% del tiempo. Sin embargo, este optimismo es irreal, porque estamos suponiendo también que nunca los 5 procesos estarán esperando por trabajos de entrada/salida al mismo tiempo. Aún así, de acuerdo a cálculos estadísticos, se estima que el uso de la CPU asciende a un 80-90% manteniendo 5 procesos en memoria.

Análisis del rendimiento de un sistema con multiprogramación:

Consideremos, por ejemplo, un centro de cómputos que trabaja por lotes, cuyos trabajos esperan la entrada/salida un tiempo promedio del 80%. Una mañana se envían 4 trabajos para su procesamiento, como lo muestra tabla:

Trabajo Tpo. De llegada Minutos necesarios de CPU

1 10:00 4

2 10:10 3

3 10:15 2

4 10:20 2

El primer trabajo, que llega a las 10:00 requiere 4 minutos de tiempo de CPU.

(16)

Con un tiempo de entrada/salida de 80%, el trabajo sólo utiliza 12 segundos de tiempo de CPU por cada minuto que se encuentra en memoria, incluso aunque no haya otros trabajos que compitan con él por la CPU. Los 48 segundos restantes se gastan en en espera de que termine la operación de entrada/salida.

Así, el trabajo debe esperar en memoria durante 20 minutos para finalizar.

Desde las 10:00 hasta las 10:10, el trabajo 1 está solo en memoria y ha utilizado 2 minutos de CPU. Cuando el trabajo 2 llega a las 10:10, el uso de CPU aumenta del 20% al 36% (ver figura 6.1) debido a la multiprogramación. Sin embargo, con la multiprogramación, cada proceso utiliza un 50% de la CPU, con lo que cada trabajo obtiene un 0.18 minutos de CPU por cada minuto que permanece en memoria. Se puede notar que, al agregar un segundo trabajo, éste le cuesta al trabajo 1 solo un 10% de su desempeño (baja de 0.20 a 0.18 minutos de CPU).

A las 10:15 llega el tercer trabajo. En este momento, el trabajo 1 ha recibido 2.9 minutos de CPU y el trabajo 2 ha tenido 0.9. Con la multiprogramación, cada trabajo ahora obtiene un 16

% de CPU. En la tabla siguiente se continuó el ejemplo para los 4 trabajos:

Utilización de la memoria

Los programas residen en disco como ejecutables binarios y deben ser traídos a memoria para su ejecución. El conjunto de programas residentes en disco esperando para ser cargados en memoria forman la cola de entrada.

Los programas pueden direccionar memoria en varias formas:

 Direccionamiento al momento de la compilación: se genera código absoluto (es decir que siempre ejecuta en una dirección de memoria de inicio fija, si esta ocupada no ejecuta).

 Direccionamiento al momento de la carga: maneja direcciones relativas al momento de carga del programa (no genera código absoluto). Se generan direcciones relativas a la posición inicial de memoria (desplazamiento). Genera código reubicable.

 Dynamic Linking: se difiere la link-edición hasta el momento de ejecución: se usa normalmente con bibliotecas del sistema. Al momento de la link edición (generación del ejecutable) se entrega un trozo de código indicando la referencia a la rutina que se debe cargar.

 Overlays: Esta técnica permite ejecutar un programa en un espacio de memoria menor que su tamaño. Mantiene en memoria toda aquella parte del programa que es utilizada en un instante determinado. Las rutinas independientes son cargadas a medida que se necesitan ejecutar (son partes del programa que se dividen en forma independiente).

Administración de Memoria Real

El método mas sencillo para administrar la memoria es cuando tenemos un sistema monousuario y monoprogramación:

Monoprogramación:

Asignación contigua de almacenamiento de un solo usuario:

 Un solo usuario a la vez

 Todos los recursos a disposición de un solo usuario

 El tamaño de los programas está limitado por la cantidad de memoria, aunque se pueden usar overlays para sacar mayor provecho a ésta.

Protección de Memoria: Se incorpora un registro de limite (limit register) a la cpu. Cada petición de acceso a la memoria realizada por el usuario se compara con el registro limite. Si la dirección a la que se quiere acceder es menor o igual al registro limite se cancela la solicitud.

Multiprogramación de partición fija:

La memoria se divide en particiones de tamaño fijo (puede ser distinto el tamaño de cada partición). Originalmente asignaba un proceso a cada partición, es decir que los programas se compilaban y link-editaban para ejecutar en una partición en particular (direcciones absolutas).

Posteriormente los compiladores y link editores generaron código reubicable para que un programa pudiera ejecutarse en cualquier partición de memoria suficientemente grande (direcciones relativas).

(17)

Figura – Particiones fijas de memoria con colas de procesos independientes para cada partición.

Con esta estructura de administración de memoria se desperdicia memoria y tiempo de CPU (si hay un programa corriendo los demás quedan encolados aunque haya otra partición libre). Ver figura.

Multiprogramación de partición variable:

Cada programa o usuario utiliza tanta memoria como sea necesaria siempre que quepa en el almacenamiento real. Se necesita de alguna estructura tipo índice para referenciar cada proceso y su ubicación. Es un método mucho mas eficiente que las particiones fijas, pero hace mas compleja la asignación y reasignación de memoria, así como también complica el mantener una registro de esto.

Cuando los programas van terminando su ejecución se van generando agujeros en memoria que quedan desaprovechados. Para solucionar este problema existen distintos enfoques:

Métodos de solución al problema de agujeros en memoria:

 Combinación

Cuando un programa termina su ejecución el sistema operativo verifica si hay algún bloque de memoria contigua libre (antes o después) en cuyo caso los combina para generar un único bloque de memoria libre de tamaño igual a la suma de ambos.

 Compactación o Compresión

Agrupa toda la memoria disponible en un único bloque de memoria al final. Es el método ideal en cuanto a aprovechamiento de espacio, pero su gran desventaja es el alto consumo de CPU (casi dedicada exclusivamente a esto). Mientras se ejecuta el algoritmo la actividad del sistema debe ser detenida. En definitiva no es conveniente utilizar este método para solucionar el problema de los agujeros en memoria debido al alto costo.

Estrategias de asignación de memoria.

Cuando se crea un proceso se debe asignarle una porción de memoria. Si los procesos se crearan con un tamaño fijo, entonces la asignación es muy sencilla: se asigna exactamente los que el proceso utilizará, ni más ni menos. Pero en la realidad, los procesos durante su ejecución pueden crecer; si existe un bloque de memoria libre sobre el proceso, éste pasa a ser utilizado, pero si no existe un bloque libre contiguo, es necesario hacer una reubicación del proceso creciente, con el consabido gasto de CPU. Entonces, es muy importante realizar una buena asignación de memoria en la creación del proceso para minimizar las reubicaciones.

(18)

En cualquiera de estas estrategias cuando a una petición le es asignada una partición y sobra memoria el sistema operativo se da cuenta y deja a este espacio sobrante como una nueva partición.

 Best Fit

Los bloques de memoria son asignados según las necesidades del programa que se esta ejecutando.

 First Fit

Asignan la primera partición disponible en la que pueda entrar el programa. Esta estrategia es mas rápida pero se desperdicia mucho espacio de memoria.

 Worst Fit

Consiste en asignar la partición en la que sobra mas espacio.

Administración de Memoria Virtual

Las direcciones de memoria no están necesariamente contenidas en el almacenamiento real.

El S.O. se apoya en el almacenamiento secundario (el disco, por ejemplo) para extender el uso de la memoria.

Las direcciones referidas por los procesos son direcciones virtuales. Los procesos no tienen por qué saber en que posición de almacenamiento real o secundario están almacenados. Las direcciones de memoria que referencian al almacenamiento primario (memoria real) se denominan "direcciones reales". Por más que los procesos hagan referencia a direcciones virtuales, estos solo pueden ejecutarse en el almacenamiento primario; por lo tanto las direcciones virtuales deben ser transformadas en direcciones reales mientras el proceso está en ejecución.

Como el almacenamiento real es compartido por varios procesos solo se mantiene al mismo tiempo una pequeña parte de cada uno de ellos en memoria real.

El mecanismo de traducción de direcciones deberá mantener mapas que ilustren que direcciones del almacenamiento virtual se encuentran en memoria real, y donde se encuentran.

La información se agrupa en bloques y el sistema operativo debe estar informado del lugar en que se encuentra almacenada cada página.

Bloque:

 Cuando los bloques son del mismo tamaño se denominan páginas y el mecanismo de administración del almacenamiento virtual asociado se denomina paginación.

 Si los bloques son de tamaño variable se denominan segmentos y el mecanismo de administración del almacenamiento virtual segmentación.

Las direcciones en un esquema de almacenamiento virtual son un par (b,d) donde b indica el bloque y d el desplazamiento desde el inicio del bloque.

Paginación

Las direcciones en un sistema de paginación están dadas por el par:

 Desplazamiento

 Página

Las páginas se transfieren del almacenamiento secundario al primario en bloques llamados page frames (los cuales tienen el mismo tamaño de las páginas). El sistema lleva una tabla de páginas para cada proceso. De esta forma se asegura la protección.

Cuando un proceso debe ser cargado en memoria para ser ejecutado se siguen determinados pasos:

 Se calcula su tamaño en páginas

 Se carga el programa en memoria

 Se construye la tabla de páginas del proceso

 Soporte de Hardware

Como la tabla de páginas de cada proceso puede llegar a tornarse demasiado grande, se buscan mecanismos que permitan acelerar el tiempo de búsqueda en esa información. La solución mas simple es la utilización de un conjunto de registros dedicados (no usar tabla de paginación de cada proceso). El CPU dispatcher será el encargado de cargar y actualizar la información de estos registros con cada conmutación de procesos. Esta solución sería razonable si se hablara de pocos registros pero en la realidad no se utiliza.

(19)

La solución mas común es apoyarse en un cache de acceso muy rápido llamado TLB (Translation Location Buffer). La TLB solo puede contener algunas entradas de la tabla de páginas. Con cada conmutación de proceso la TLB debe ser variada para evitar que un proceso haga referencia a direcciones de memoria utilizadas por otro proceso (protección). Si se hace referencia a una dirección que no estaba cargada en TLB, se debe cargar en esta, eliminando alguna otra dirección si fuera necesario.

 Protección

Por la forma en que se construyen las tablas de páginas los procesos solo van a hacer referencia a direcciones de su espacio virtual. La protección se implementa por un conjunto de bits que son asociados a cada frame. Un bit puede definir si una página es read-only o read- write y cada vez que se hace una operación sobre un page frame se verifica sobre estos bits.

 Páginas Compartidas

Es posible que los procesos compartan código común (reentrante). El código común no podrá ser modificado durante su ejecución, por lo cual se deberán proteger mediante los bits de protección.

Segmentación

En un esquema de segmentación un espacio de direcciones lógic as es un conjunto de segmentos. Cada segmento tendrá un nombre y un largo. Las direcciones van a hacer referencia tanto al nombre como al desplazamiento dentro del segmento.

<nro. segmento, desplazamiento>

Mecanismo de traducción de direcciones:

Se lleva una tabla de segmento por cada proceso, cada entrada a la tabla de segmento lleva la siguiente información:

 segment base (dirección base del segmento)

 segment limit (largo del segmento)

 Protección

La protección se asegura verificando cada acceso a la memoria con la tabla de segmentos para asegurar que se está direccionando dentro del espacio de direcciones lógicas del proceso.

Además el mecanismo de traducción de direcciones asegura que no se direcciones fuera de un segmento en particular. Existen también bits de protección para cada entrada de la tabla de segmentos que indicaran si el segmento es read only

o read-write.

 Segmentos Compartidos

Similar a lo visto para paginación. Se comparte la totalidad de un segmento

Paginación por demanda

Es similar a lo visto para la paginación introduciendo el concepto de swapping. Los procesos residen en el disco y al ser ejecutados deben ser cargados en memoria. Cuando un proceso va a ser ejecutado, el mismo es swappeado a memoria, utilizando lazy swapping. El lazy swapping nunca trae páginas a memoria si no van a ser ejecutadas. Se necesita determinar si un página esta en memoria o en disco, por lo cual se utiliza el bit de válido/inválido de la tabla de páginas.

Si el bit = 1 la página es valida y esta cargada en memoria si es 0 la página es inválida y no esta cargada en memoria (esta en disco).

Cuando un proceso intenta acceder a una página que no esta cargada en memoria ocurre un page fault (fallo de página). Los pasos para manejar un page fault son los siguientes:

1. Verificar si la referencia a la página es valida (se utiliza una tabla interna generalmente llevada en PCB) donde se indica las páginas validas.

2. Si la referencia no es válida, se suspende la ejecución del proceso.

3. Encontrar un frame disponible para cargarla (la página esta en disco).

4. Solicitar operación de E/S para leer la página de disco y cargarla en el frame obtenido.

5. Modificar la tabla interna y la tabla de páginas para que ahora esta página figure en memoria.

6. Continuar con la ejecución del proceso en la instrucción en la que fue suspendido.

 Reemplazo de Página

(20)

Si al momento de generarse un page fault no hubiera ningún frame disponible, se debe proceder a reemplazar una página de memoria. La rutina de page fault debe ser modificada para incluir reemplazo de páginas:

 Encontrar un frame disponible.

 Si hay un frame disponible usarlo.

 Si no hay frames disponibles utilizar un algoritmo de reemplazo de paginas para seleccionar la víctima.

 Escribir la página víctima a disco y actualizar tabla de páginas.

Algoritmos de reemplazo de página

Para poder seleccionar cuál va ser la página a eliminar de la memoria para ubicar una nueva, existen diferentes algoritmos. Vamos a estudiar los más comunes y ver sus implicancias en la performance.

 Primero en entrar, Primero en Salir “FIFO” (Firs In First Out):

Asocia a cada página el tiempo en que fue cargada en memoria. Cuando se debe reemplazar una página, se selecciona la que hace más tiempo que está en memoria. El funcionamiento es similar al de una cola de tamaño fijo, en la que cuando se necesita agregar un elemento, se agrega al final de la cola y se elimina el primer elemento de ella. Este algoritmo es muy sencillo de implementar, pero no es funcional, dado que el hecho de que una página haya sido la primera en ingresar no implica que vaya a ser la menos utilizada.

 Algoritmo de Segunda Chance:

Es una variante del algoritmo FIFO con el adicional de mantener para cada página un bit de referencia. Cada vez que se selecciona una página a reemplazar, se inspecciona el bit de referencia. Si el valor es 0 se procede a reemplazar la página. Si el valor es 1, se da una segunda oportunidad a la página y se selecciona la siguiente víctima por criterio FIFO. Cada vez que una página recibe una segunda oportunidad, se carga en 0 el bit de referencia. Si una página vuelve a ser accedida, se incrementa el bit de referencia a 1. De esta forma, una página con muchas referencias constantemente va a tener su bit de referencia en 1 y no va a ser reemplazada.

 De Menor uso reciente “LRU” (Least Recently Used):

Se selecciona la página que hace más tiempo que no se utiliza. Asocia a cada página el tiempo de la última vez que fue utilizada. En LRU se necesita llevar tiempo de última utilización de cada página:

 Contadores: Se asocia a cada entrada en la tabla de páginas, el tiempo de la última vez que se uso. Cada vez que se accede a una página se actualiza el contador.

 Stack: Se lleva un stack con los números de página, cada vez que una página es accedida, se quita del stack y se agrega al comienzo. En el tope del stack estará la página que fue utilizada por última vez, y en el fondo la página que hace más tiempo que no se accede.

Este método es muy bueno y disminuye notablemente el Page Fault Frequency, pero es prácticamente irrealizable por su costo de implementación.

 De Menor Uso Frecuente “LFU” (Least Frequently Used):

Establece que la página con menos cantidad de referencias debe ser reemplazada. Una contra del algoritmo es que una página puede haber sido muy utilizada en la fase de carga de un proceso, pero no ser vuelta a utilizar, por lo cual se estaría manteniendo en memoria páginas que no son necesarias para la ejecución.

 MFU (Most Frequently Used):

La página con mayor cantidad de referencias es la víctima a ser reemplazada. El argumento es que si una página fue utilizada pocas veces, se debe a que recién fue cargada en memoria y aún deba ser utilizada. Este algoritmo tiene como desventaja que se van a eliminar las páginas con mayor cantidad de referencias, por lo tanto si tenemo s un proceso con repetidos accesos a una página, cada vez que la necesite ocurrirá un fallo. Este algoritmo no se utiliza en la práctica.

 PFF (Page Fault Frequency):

Se controla el page fault rate de cada proceso. Si un proceso tiene page fault rate muy bajo probablemente se deba a que este utilizando demasiados frames en memoria. Si el page fault rate es muy alto, probablemente no tenga suficientes frames otorgados. La idea de esta técnica es definir limite superior e inferior de page fault a cada proceso.

Referencias

Documento similar

Ciertamente su duración no es eterna, sobre todo si los usas para jabones de proceso en frío, pero son muy económicos, muy fáciles de hacer y de usar, y reproducen