Unidad 1 Introducción a los sistemas operativos 1.1 Definición, Objetivos, y Función Sistema Operativo Definición de Sistema Operativo El sistema operativo es el programa (o software) más importante de un ordenador. Para que funcionen los otros programas, cada ordenador de uso general debe tener un sistema operativo. Los sistemas operativos realizan tareas básicas, tales como reconocimiento de la conexión del teclado, enviar la información a la pantalla, no perder de vista archivos y directorios en el disco, y controlar los dispositivos periféricos tales como impresoras, escáner, etc. En sistemas grandes, el sistema operativo tiene incluso mayor responsabilidad y poder, es como un policía de tráfico, se asegura de que los programas y usuarios que están funcionando al mismo tiempo no interfieran entre ellos. El sistema operativo también es responsable de la seguridad, asegurándose de que los usuarios no autorizados no tengan acceso al sistema. Clasificación de los Sistemas Operativos Los sistemas operativos pueden ser clasificados de la siguiente forma: •Multiusuario: Permite que dos o más usuarios utilicen sus programas al mismo tiempo. Algunos sistemas operativos permiten a centenares o millares de usuarios al mismo tiempo. •Multiprocesador: soporta el abrir un mismo programa en más de una CPU. •Multitarea: Permite que varios programas se ejecuten al mismo tiempo. •Multitramo: Permite que diversas partes de un solo programa funcionen al mismo tiempo. •Tiempo Real: Responde a las entradas inmediatamente. Los sistemas operativos como DOS y UNIX, no funcionan en tiempo real. Cómo funciona un Sistema Operativo Los sistemas operativos proporcionan una plataforma de software encima de la cual otros programas, llamados aplicaciones, puedan funcionar. Las aplicaciones se programan para que funcionen encima de un sistema operativo particular, por tanto, la elección del sistema operativo determina en gran medida las aplicaciones que puedes utilizar. Los sistemas operativos más utilizados en los PC son DOS, OS/2, y Windows, pero hay otros que también se utilizan, como por ejemplo Linux. Cómo se utiliza un Sistema Operativo Un usuario normalmente interactúa con el sistema operativo a través de un sistema de comandos, por ejemplo, el sistema operativo DOS contiene comandos como copiar y pegar para copiar y pegar archivos respectivamente. Los comandos son aceptados y ejecutados por una parte del sistema operativo llamada procesador de comandos o intérprete de la línea de comandos. Las interfaces gráficas permiten que utilices los comandos señalando y pinchando en objetos que aparecen en la pantalla. Ejemplos de Sistema Operativo A continuación detallamos algunos ejemplos de sistemas operativos: Familia Windows •Windows 95 •Windows 98 •Windows ME •Windows NT •Windows 2000 •Windows 2000 server •Windows XP •Windows Server 2003 •Windows CE •Windows Mobile •Windows XP 64 bits •Windows Vista (Longhorn) Familia Macintosh •Mac OS 7 •Mac OS 8 •Mac OS 9 •Mac OS X Familia UNIX •AIX •AMIX •GNU/Linux •GNU / Hurd •HP-UX •Irix •Minix •System V •Solaris •Unix Ware? El objetivo fundamental de los sistemas operativos Es gestionar y administrar eficientemente los recursos hardware, permitiendo que se ejecuten concurrentemente varios programas, sin que haya conflictos en el acceso de cada uno de ellos a cada uno de los recursos que necesite, y sin que ningún programa monopolice alguno de ellos. Un sistema operativo es un programa (o conjunto de programas) de control que tiene por objeto facilitar el uso de la computadora y conseguir que ésta se utilice eficientemente. Es un programa de control, ya que se encarga de gestionar y asignar los recursos hardware que requieren los programas. Pensemos en una computadora que es utilizada desde diversos terminales por varios usuarios. Los recursos hardware son: el procesador (CPU), la memoria principal, los discos, y otros periféricos. Obviamente, si varios usuarios están utilizando la misma Computadora, debe haber alguien o algo que asigne los recursos y evite los conflictos que puedan surgir cuando dos programas requieran los mismos elementos (la misma unidad de disco, o la impresora, por ejemplo). Esta es una de las funciones del sistema operativo. Además de esta función de asignar los recursos a cada programa, el sistema operativo se encarga de contabilizar de éstos, y de la seguridad (que un usuario no pueda acceder sin autorización a la información de otro, por ejemplo). El sistema operativo facilita el uso de la computadora. Veamos un ejemplo de cómo lo hace. Cada dispositivo de E/S, para ser utilizado, requiere varias instrucciones máquina que establezcan un dialogo entre las unidades centrales y el periférico, enviando o captando el dato de salida o de entrada, respectivamente. Estas instrucciones dependen considerablemente de las características concretas del periférico. Si se trata, por ejemplo, de una unidad de disco, hay que considerar el ancho de los buses, el tamaño de su memoria intermedia, el arranque (y parada) de los motores de la unidad, el código identificador de la posición a donde hay que acceder, etc. Por otra parte, un dispositivo dado, estas instrucciones u operaciones son comunes para grabar o leer cual tipo de información (programas o datos), sea cual sea su naturaleza. El sistema operativo, con objetivo de facilitar el trabajo de los programadores, contiene módulos de gestión de entradas / salidas que evitan a los usuarios tener que incluir esas instrucciones cada vez que hacen una operación de entrada o salida. Se puede decir que esos programas del sistema operativo hacen transparente al usuario las características hardware concretas de los dispositivos. El sistema operativo también hace que la computadora se utilice eficientemente. Para poner de manifiesto cómo el sistema operativo puede incrementar la eficiencia consideremos un ejemplo sencillo. Los programas tradicionalmente se ejecutan secuencialmente; es decir, hasta que no concluye la ejecución de una instrucción no se ejecuta la siguiente. Supongamos que dentro de un programa hay una instrucción que implica la escritura de una página en una impresora láser. Hasta que no acabe de imprimirse dicha página el programa no puede continuar ejecutándose. En resumen, el sistema operativo efectúa, entre otras, las siguientes funciones: - Facilita el uso de la computadora, en general, la comunicación computadora / usuario - Gestiona y asigna recursos hardware (procesador, memoria y periféricos) a los distintos programas o tareas - Gestiona y mantiene los archivos en dispositivos de memoria masiva - Apoya a otros programas - Protege los datos y los programas, cuestión especialmente compleja en sistemas multiusuario - Identifica y autentifica a los usuarios que hacen uso de la computadora. - Contabiliza la utilización de los recursos realizada por los distintos usuarios Sus funciones mas importantes son: -Gestiona los recursos del ordenador en sus niveles mas bajos. -Dispone de una interface (elemento que hace posible la fácil comunicación usuario maquina) liberando al usuario del conocimiento del hardware. El SO windows se basa en una interface graficas, “GUI” (Interface Grafica de Usuario), permitiendo al usuario interactuar con el hardware de una forma sencilla y rápida. -Sobre el SO funcionan el resto de programas y aplicaciones del software. Sus generaciones: -Generación 0º: década de los 40 los sistemas informáticos no disponían de SO con lo que los usuarios de estos debían introducir las instrucciones en código binario lo que hacia su uso restringido a personas de mucho conocimiento en esa materia. -Generación 1º: década de los 50. Aparece el primer SO para lograr la fluidez en la trasmisión de información. Aparece el JLC (lenguaje de control de trabajo), se usaban tarjetas perforadas y eran controladas por operadores (personas con cierto conocimiento). Posteriormente se pasaron de las tarjetas a las cintas perforadas y estas iban mucho mas rápido. En el primer SO había en ocupaba en memoria 64 KB (bastante en función de la capacidad total de la memoria en aquella época). Grafico interno de un sistema operativo de la época y en parte en la actualidad Generación 2º: Se dan los primeros pasos de la multiprogramación es decir varios programas de usuario. El SO reparte tiempos del procesador. Aparece la llamada tecnología DMA usa buffers entre terminales: impresora, etc. ¿Qué es un buffer?: Es una fuente de almacenamiento temporal que reside en el propio dispositivo ya sea de entrada, o de salida. Aparece el termino spooding: viene a ser como lo que gestiona la cola de instrucciones en el buffer. Terminales Impresora Durante esta generación aparecen los primeros multiprocesarodes (varios procesadores trabajando simultáneamente, a la vez). Generación 3º: segunda mitad de la década de los 60 y 1º mitad de los 70. Es entonces cuando se desarrollan los SO tan importantes como el UNÍS para la gestión de grandes mainframes. Durante esta generación el usuario perdió el control del hardware. Los equipos informáticos venían con el software (SO) de “regalo”. Generación 4º: segunda mitad de los 70 y primera de los 80. Los SO aumentan sus prestaciones y gestionan eficientemente los recursos del ordenador. Es en esta época donde mas facilidad se le da al usuario para su manejo. IBM separa los costos de hardware y software con esta estrategia de marketing se pensaba que facturarían el doble en ganancias. Pero no fue así. Los vendedores de software pasan a hacerse responsables de los bugs (o fallos de sus programas). Proliferaron las empresas desarrolladoras de software esto perjudico seriamente a IBM ya que así perdió la exclusividad. Posteriormente se abrió el mercado de computadoras compatibles con IBM, estos son los llamados clónicos (varios dispositivos informáticos de distintas fabricas o procedencias intercomunicados y compatibles entre si constituyendo un único equipo informático. Este tipo de ordenadores es de precio mucho mas reducido que los IBM y sus prestaciones son las mismas). Generación 5º: década de los 90. Los entornos gráficos cobraron mucha importancia, proliferaron y evolucionaron las llamadas GUIs (interfaces graficas del usuario). Los sistemas operativos tipo windows 9*, millenium o NT para empresas proliferaron y desplazaron de las empresas al ya consolidado UNÍS. Una de las características he imnovaciones mas importantes de un SO es la llamada multi-tarea. Un SO opera entre la CPU y los periféricos y con la tecnología multi-tarea reparte el tiempo entre ellos dos. Suponiendo que se lancen tres procesos (A,B,C), (proceso: conjunto de rutina, algoritmos, etc.). Lo que el SO hace internamente con esta tecnología es: recoger la primera operación del proceso A en CPU luego pasa a los periféricos con lo que la CPU descansa y en ese momento, recoge la primera operación del proceso B en CPU, posteriormente en cuanto va a los periféricos pasa al proceso C, y cuando a este le llegan los turnos que corresponden a los periféricos pasa a la segunda operación del proceso A. Y asi sucesivamente. Con esta tecnología se logra apurar y reducir enormemente el tiempo de proceso del SO, podemos llegar a unas 50 tareas simultáneamente. Sistemas operativos como, el ms-dos no disponen de ella, pero otros como windows si alcanzan esta tecnología. Y con esto doy por finalizado el tutorial de componentes que incluyen un equipo informático. Mulprogramacion o multiproceso: Es la técnica que permite cargar varios programas o procesos al mismo tiempo de forma simultanea. Cada programa o proceso ha de usar unos recursos y el SO ha de gestionar esto. Se pueden dar los llamados interbloqueos en la que 2 o mas programas estan a la espera por solicitar un dispositivo o memoria. El SO debe solucionar esto. Batch: sirve para aprovechar tiempos muertos (noches), etc. En grandes empresas los usan para hacer copias de seguridad. Tiempo real: Asigna de forma primaria unos tiempos si se pasa de ellos da paso al siguiente. Seguridad: El SO debe suministrar los medios para la ejecución concurrente de los procesos, sincronizacion entre procesos, comunicación entre procesos. El SO debe suministrar algoritmos de gestion y planificación de procesos que se encarguen de decidir que proceso se ejecutara o cual tomara al procesador y de llevar cuenta de los estados y procesos, sus prioridades y todo la restante información relevante. Estado de los procesos Activo: Ejecutándose en un instante de tiempo. En un sistema monoprocesador, solo puede haber uno. Preparado: Listos para ejecutarse. Esperando que un procesador quede libre, bloqueado o suspendido, a la espera de que se cumpla una condición. Muerto: Ha terminado su ejecución o el sistema ha detectado un error fatal y la ha transferido ha estado nonato. Nonato: El programa existe pero todavía no es conocido por el SO. El estado global del SO en un instante determinado, es el conjunto de recursos y procesos existentes con sus estados correspondientes Transición entre procesos El distribuidor: es uin modulo del SO que activa procesos preparados de acuerdo con unos criterios determinados por ejemplo: prioridad. Interrupción: Son las llamadas del SO para solicitar servicios por ejemplo: una operación.de E/S. Planificador: Modulo del SO que translada procesos de ejecución a preparados, por ejemplo: en sistemas de tiempo compartido porque se les ajusta el tiempo. O llega uno de mayor prioridad. Paso de preparado a bloqueado: Cuando tiene lugar un evento que estuviera esperando para poder perseguirlo 1.2 Historia Evolución Sistema Operativo Introducción Los sistemas operativos han venido evolucionando a través de los años. Ya que los sistemas operativos se han apegado íntimamente a la arquitectura de las computadoras en las cuales se ejecutan. La primera computadora digital real fue diseñada por el matemático ingles Charles Babbage ( 1792 - 1871) . Aunque Babbage gasto la mayor parte de su vida y de su fortuna intentando construir su “ maquina analítica “ , nunca la hizo funcionar adecuadamente porque era un diseño puramente mecánico y la tecnología de su época no podía producir las ruedas, el engranaje, levas y otras partes mecánicas con la alta precisión que el necesitaba. Sin tener que decirlo, la maquina analítica no tuvo un sistema operativo. La primera generación (1945 - 1955 ) : Tubos de vacio y tableros enchufables Después de los esfuerzos frustrados de Babbage, se progresó poco en la construcción de computadoras digitales hasta la segunda guerra mundial, alrededor de la mitad de la década de 1940, Howard Aiken en Hardvard, Jon Von Neumann en el Instituto de Estudios Avanzados en Princeton, J. Presper Ecker y William Mauchley en la Universidad de Pennsylvania y Konrad Zuse en Alemania, entre otros, todos obtuvieron resultados óptimos en la construcción de maquinas de calculo mediante el uso de tubos de vacío. En estos primeros días, un grupo singular de personas diseño, construyo, programo, opero y dio mantenimiento a cada maquina. Toda la programación se realizo en lenguaje de maquina absoluto. Los lenguajes de programación se desconocían ( todavía no existía el lenguaje ensamblador ). Los primeros sistemas operativos eran extraños. El modo usual de operación consistía en que el programador firmaba para tener acceso a un bloque de tiempo en la hoja de registro situada en la pared, después bajaba al cuarto de maquinas, insertaba su tablero enchufable en la computadora y pasaba las siguientes horas esperando que ninguno de los 20,000 tubos de vació se fundiera durante la ejecución de su programa. Al inicio de la década de 1950, la rutina había mejorado un poco con la introducción de la tarjetas perforadas. Ahora era posible escribir en tarjetas y leerlos, en vez de utilizar tableros enchufables; de lo contrario el procedimiento era el mismo. La segunda generación (1955 - 1965 ) : Transistores y sistemas de lote La introducción del transistor a mediados de la década de 1950 cambio la imagen radicalmente. Las computadoras se volvieron lo suficientemente confiables, en un principio hubo una clara separación entre los diseñadores, armadores, operadores, programadores y personal de mantenimiento. Estas maquinas se instalaban en cuartos de computadoras especialmente acondicionados con aire, con cuerpo de operadores profesionales para accionarlas. un programador primeramente escribiría el programa en papel ( en FORTRAN o en lenguaje Ensamblador ) y después lo perforaría en tarjetas. Después llevaría la pila de tarjetas al cuarto de introducción al sistema y la entregaría a uno de los operadores el cual iniciaba el proceso en la computadora, este proceso desperdiciaba mucho tiempo. Dado el alto costo del equipo, no es sorprendente que las personas buscaran rápidamente maneras de reducir el tiempo perdido. La solución que generalmente se adoptaba era el sistema de lote. La idea implícita en este sistema era la de conjuntar un cajón lleno de trabajos en el cuarto de introducción al sistema y después leerlos en una cinta magnética mediante el uso de una computadora ( relativamente ) pequeña y poco costosa, como la IBM 1401. Después de casi una hora de recolectar un lote de trabajos, la cinta se volvía a enrollar y se llevaba al cuarto de maquinas. Después el operador cargaba un programa especial ( el ancestro del sistema operativo de hoy en día ) , el cual leía el primer trabajo y lo ejecutaba, la salida se escribía en una segunda cinta, en vez de imprimirse. Después de terminar cada trabajo, el sistema operativo leía automáticamente el siguiente trabajo de la cinta, y comenzaba a ejecutarlo. La estructura de un trabajo de entrada común arrancaba con una tarjeta $JOB , que especifica el tiempo máximo de ejecución en minutos, el numero de cuenta que se cargara y el nombre del programador. Después venia una tarjeta $FORTRAN , que indicaba al sistema operativo que debía cargar el compilador de FORTRAN . Venia seguido de un programa que debía compilarse y después de una tarjeta $LOAD, que ordenaba al sistema operativo cargar el programa objeto recién compilado, después venia la tarjeta $RUN, que indicaba al sistema operativo que debía ejecutar el programa con los datos que le seguían. Por ultimo, la tarjeta $END marcaba el final del trabajo. Los sistemas operativos comunes eran FMS ( el sistema monitor del FORTRAN ) e IBSYS, sistema operativo de IBM de la 7094. La tercera generacion (1965 - 1980 ) : Circuitos integrados ( CI ) y multiprogramacion Al inicio de la década de 1960 muchos fabricantes de computadoras tenían dos líneas de trabajo distintas y totalmente incompatibles. Por un lado existían las computadoras científicas de grande escala orientadas a las palabras, como la 7094, que se utilizaban para realizar cálculos numéricos de ciencias e ingeniería. Por el otro lado estaban las computadoras comerciales orientadas a los caracteres, como 1401, que se utilizaban para el ordenamiento de cintas e impresión por parte de bancos y compañías de seguros. El desarrollo y mantenimiento de dos líneas de productos diferentes era una proposición costosa para los fabricantes. Además, muchos nuevos compradores de computadoras necesitaban una maquina pequeña, pero después se expandían y querían una maquina de mayor tamaño que ejecutara todos sus programas antiguos, pero con mayor velocidad. IBM intento resolver estos dos problemas de un solo golpe introduciendo en el mercado el Sistema/360. El 360 era una serie de maquinas compatibles con el software que variaban del tamaño de la 1401 a una mucho mas poderosa que la 7094, el 360 estaba diseñado para realizar cálculos tanto científicos como comerciales. Por lo tanto una sola familia de maquinas podía satisfacer las necesidades de todos los clientes. El sistema 360 fue la primera línea importante de computadoras que utilizo circuitos integrados ( CI ), con lo cual ofreció una mayor ventaja de precio/rendimiento sobre las maquinas de la segunda generación. La intención era que todo el software, como el sistema operativo, tenían que funcionar en todos los modelos. Tenia que correr en sistemas pequeños, y en sistemas muy grandes. Tenia que funcionar adecuadamente en sistemas con algunos periféricos y en sistemas con muchos periféricos. No había manera de que IBM escribiera una pieza de software que cumpliera todos esos requisitos conflictivos. El resultado fue un sistema operativo enorme y extraordinariamente complejo. Constaba de millones de líneas de lenguaje ensamblador escritas por miles de programadores, y contenía miles y miles de errores ocultos. A pesar de tamaño y problemas enormes , OS/360 y los sistemas operativos similares de la tercera generación satisfacían a muchos de sus clientes razonablemente bien, También popularizaron varias técnicas importantes ausentes en los sistemas operativos de la segunda generación. La mas importante de estas fue la multiprogramación. Cuando el trabajo corriente se detenía para esperara a que se completara una operación en cinta u otra operación de E/S, la unidad central de procesamiento ( CPU ) simplemente permanecía ociosa hasta que terminara la operación de E/S . La solución que evoluciono consistía en partir la memoria en varias partes, con trabajo diferente en cada partición. Mientras que un trabajo esperaba a que se completara la E/S, otro trabajo podía estar utilizando la CPU. Si se podían mantener suficientes trabajos en la memoria central al mismo tiempo, la CPU podía mantenerse ocupada casi el 100% del tiempo. Otra característica de importancia en los sistemas operativos de la tercera generación era la capacidad de leer trabajos de tarjetas contenidas en el disco tan pronto como se llevaban al cuarto de computación. Siempre que se terminaba un trabajo, el sistema operativo podía cargar uno nuevo del disco en la partición no vacía y ejecutarlo, esta técnica se denomina manejo por cola de impresión. Los sistemas operativos de la tercera generación seguían siendo básicamente sistemas de lote. Con los sistemas operativos de la tercera generación, el tiempo entre la entrega de un trabajo y la devolución de la salida comprendía a menudo varias horas. El deseo de obtener un tiempo de respuesta corto marco el camino para el tiempo compartido, variante de la multiprogramación, en la cual cada usuario tiene una terminal en línea. En un sistema de tiempo compartido si hay 20 usuarios dentro del sistema y 17 de ellos están pensando o platicando o bien tomando café, la CPU puede distribuirse en turno para los tres trabajos que necesitan servicio. Aunque el primer sistema de tiempo compartido ( CTSS ) serio fue creado en MIT en una unidad 7094 especialmente modificada, no se volvió popular sino hasta que el hardware de protección necesario se disemino durante la tercera generación. Después del éxito del sistema CTSS, MIT, Bell laboratories y General electric decidieron embarcarse en el desarrollo de la “ computadora de servicio publico “. conocido como MULTICS ( Multiplexed information and computing service, información multicanalizada y servicio de computación ) . Para resumir una larga historia, MULTICS introdujo muchas ideas originales en la literatura de computación, pero su construcción era mas difícil de lo que nadie había sospechado. MULTICS tuvo enorme influencia sobre otros sistemas subsiguientes. Otro avance durante la tercera generación fue el crecimiento de las minicomputadoras, comenzando con DEC PDP-1 en 1961. Uno de los científicos que había trabajado en el proyecto MULTICS, Ken Thompson, hallo después una pequeña PDP-7 y empezó a escribir después una versión desguarnecida de MULTICS para un usuario. Este sistema se llamo “UNICS” ( Uniplexed information and computing service, información unicanalizada y servicio de computación ), pero su ortografía cambio mas tarde por UNIX. UNIX se ha desplazado a mas computadoras que ningún otro sistema operativo de la historia y su uso sigue aumentando rápidamente. La cuarta generacion (1980 - 1990 ) : Computadoras personales Con la creación de los circuitos integrados LSI ( integración a grande escala ) , chips que contiene miles de transistores en un centímetro cuadrado de silicon, la era de computadora personal vio sus inicios. Dos sistemas operativos han dominado la escena de la computadora personal: MS-DOS, escrito por Microsoft, Inc., para la IBM PC y otras computadoras que utilizan la CPU Intel 8088 y sus sucesores. y UNIX, que domina en las computadoras personales mayores que hacen uso de CPU Motorola 68000. Aunque la versión inicial de MS-DOS era relativamente primitiva, versiones subsiguientes han incluido mas y mas características de UNIX, lo que no es totalmente sorprendente dado que Microsoft es un proveedor importante de UNIX, que usa el nombre comercial de XENIX. Un avance importante que empezó a tomar su sitio a mediados de la década de 1980 es el desarrollo de redes de computadoras personales que corren sistemas operativos en red y sistemas operativos distribuidos. En un sistema operativo en red, los usuarios tienen conocimiento de la existencia de múltiples computadoras y pueden ingresar en maquinas remotas y reproducir archivos de una maquina a la otra. Cada maquina ejecuta su sistema operativo local y tiene un usuario propio ( o usuarios). Un sistema distribuido, es aquel que se presenta ante sus usuarios como un sistema uniprocesador tradicional, aunque en realidad este compuesto de múltiples procesadores. En un sistema distribuido real, los usuarios no tienen conocimiento de donde se están ejecutando sus programas o de donde están ubicados sus archivos; todo esto se debe manejar en forma automática y eficiente por medio del sistema operativo. Los sistemas operativos en red no son fundamentalmente diferentes de los sistemas operativos uniprocesadores. Sin duda necesitan un controlador de interfaz en red y algún software de bajo nivel para impulsarlo, así como programas para lograr un ingreso remoto al sistema y un acceso remoto del archivo . Los sistemas operativos distribuidos reales requieren mas que simplemente agregar un poco de código a un sistema operativo uniprocesador, ya que los sistemas operativos distribuidos y centralizados difieren de manera decisiva. Historia de Minix Cuando UNIX era joven ( versión 6 ), el código fuente se encontraba en todas partes, con autorización de AT&T, y se estudiaba frecuentemente, John Lions, llego a escribir un pequeño folleto que describía su operación, línea por línea, este folleto se utilizo como libro de texto en muchos cursos universitarios. Cuando AT&T entrego la versión 7, empezó a comprender que UNIX era un valioso producto comercial, así que emitió la versión 7 con una licencia que prohibía el estudio del código fuente en cursos con el objeto de evitar poner en peligro su condición como secreto comercial, muchas universidades se quejaron simplemente descartando el estudio de UNIX y enseñando solo teoría. Por desgracia, el solo enseñar teoría deja al estudiante con una visión desproporcionada de lo que en realidad es un sistema operativo. Para remediar esta situación, decidí escribir un nuevo sistema operativo que seria compatible con UNIX desde el punto de vista del usuario, pero completamente diferente en el interior. El nombre MINIX surge de mini-UNIX porque es lo suficientemente pequeño que hasta alguien que no sea maestro puede entender la forma en que trabaja. MINIX tiene otra ventaja sobre UNIX, se escribió una década después que UNIX y se ha estructurado en forma mas modular. El sistema de archivo de MINIX , por ejemplo, no es parte del sistema operativo en absoluto, pero corre como un programa de usuario. Otra diferencia es que UNIX se diseño para ser eficiente; MINIX se diseño para ser legible, el código de MINIX, por ejemplo , tiene mas de 3000 comentarios en él. MINIX se ha diseñado para ser compatible con la versión 7 de UNIX. AL igual que UNIX, MINIX se escribe en lenguaje de programación C. La implementacion inicial se hizo en la IBM PC, MINIX no requiere un disco duro para correr, con lo cual se ajusta a los presupuestos de muchos estudiantes Historia de los sistemas operativos Los Sistemas Operativos, al igual que el Hardware de los computadores, 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. Generación Cero (década de 1940) Los primeros sistemas computacionales no poseían sistemas operativos. Los usuarios tenían completo acceso al lenguaje de la maquina. Todas las instrucciones eran codificadas a mano. Primera Generación (década de 1950) Los sistemas operativos de los años cincuenta fueron diseñados para hacer mas fluida la transición entre trabajos. Antes de que los sistemas fueran diseñados, se perdía un tiempo considerable entre la terminación de un trabajo y el inicio del siguiente. Este fue el comienzo de los sistemas de procesamiento por lotes, donde los trabajos se reunían por grupos o lotes. Cuando el trabajo estaba en ejecución, este tenia control total de la maquina. Al terminar cada trabajo, el control era devuelto al sistema operativo, el cual limpiaba y leía e iniciaba el trabajo siguiente. Al inicio de los 50′s esto había mejorado un poco con la introducción de tarjetas perforadas (las cuales servían para introducir los programas de lenguajes de máquina), puesto que ya no había necesidad de utilizar los tableros enchufables. Además el laboratorio de investigación General Motors implementó el primer sistema operativo para la IBM 701. Los sistemas de los 50′s generalmente ejecutaban una sola tarea, y la transición entre tareas se suavizaba para lograr la máxima utilización del sistema. Esto se conoce como sistemas de procesamiento por lotes de un sólo flujo, ya que los programas y los datos eran sometidos en grupos o lotes. La introducción del transistor a mediados de los 50′s cambió la imagen radicalmente. Se crearon máquinas suficientemente confiables las cuales se instalaban en lugares especialmente acondicionados, aunque sólo las grandes universidades y las grandes corporaciones o bien las oficinas del gobierno se podían dar el lujo de tenerlas. Para poder correr un trabajo (programa), tenían que escribirlo en papel (en Fortran o en lenguaje ensamblador) y después se perforaría en tarjetas. Enseguida se llevaría la pila de tarjetas al cuarto de introducción al sistema y la entregaría a uno de los operadores. Cuando la computadora terminara el trabajo, un operador se dirigiría a la impresora y desprendería la salida y la llevaría al cuarto de salida, para que la recogiera el programador. Segunda Generación (a mitad de la década de 1960) La característica de los sistemas operativos fue el desarrollo de los sistemas compartidos con multiprogramación, y los principios del multiprocesamiento. En los sistemas de multiprogramación, varios programas de usuario se encuentran al mismo tiempo en el almacenamiento principal, y el procesador se cambia rápidamente de un trabajo a otro. En los sistemas de multiprocesamiento se utilizan varios procesadores en un solo sistema computacional, con la finalidad de incrementar el poder de procesamiento de la maquina. La independencia de dispositivos aparece después. Un usuario que desea escribir datos en una cinta en sistemas de la primera generación tenia que hacer referencia especifica a una unidad de cinta particular. En la segunda generación, el programa del usuario especificaba tan solo que un archivo iba a ser escrito en una unidad de cinta con cierto numero de pistas y cierta densidad. Se desarrollo sistemas compartidos, en la que los usuarios podían acoplarse directamente con el computador a través de terminales. Surgieron sistemas de tiempo real, en que los computadores fueron utilizados en el control de procesos industriales. Los sistemas de tiempo real se caracterizan por proveer una respuesta inmediata. Tercera Generación (mitad de década 1960 a mitad década de 1970) Se inicia en 1964, con la introducción de la familia de computadores Sistema/360 de IBM. Los computadores de esta generación fueron diseñados como sistemas para usos generales . Casi siempre eran sistemas grandes, voluminosos, con el propósito de serlo todo para toda la gente. Eran sistemas de modos múltiples, algunos de ellos soportaban simultáneamente procesos por lotes, tiempo compartido, procesamiento de tiempo real y multiprocesamiento. Eran grandes y costosos, nunca antes se había construido algo similar, y muchos de los esfuerzos de desarrollo terminaron muy por arriba del presupuesto y mucho después de lo que el planificador marcaba como fecha de terminación. Estos sistemas introdujeron mayor complejidad a los ambientes computacionales; una complejidad a la cual, en un principio, no estaban acostumbrados los usuarios. Cuarta Generación (mitad de década de 1970 en adelante) Los sistemas de la cuarta generación constituyen el estado actual de la tecnología. Muchos diseñadores y usuarios se sienten aun incómodos, después de sus experiencias con los sistemas operativos de la tercera generación. Con la ampliación del uso de redes de computadores y del procesamiento en línea los usuarios obtienen acceso a computadores alejados geográficamente a través de varios tipos de terminales. Los sistemas de seguridad se ha incrementado mucho ahora que la información pasa a través de varios tipos vulnerables de líneas de comunicación. La clave de cifrado esta recibiendo mucha atención; han sido necesario codificar los datos personales o de gran intimidad para que; aun si los datos son expuestos, no sean de utilidad a nadie mas que a los receptores adecuados. El porcentaje de la población que tiene acceso a un computador en la década de los ochenta es mucho mayor que nunca y aumenta rápidamente. El concepto de maquinas virtuales es utilizado. El usuario ya no se encuentra interesado en los detalles físicos de; sistema de computación que esta siendo accedida. En su lugar, el usuario ve un panorama llamado maquina virtual creado por el sistema operativo. Los sistemas de bases de datos han adquirido gran importancia. Nuestro mundo es una sociedad orientada hacia la información, y el trabajo de las bases de datos es hacer que esta información sea conveniente accesible de una manera controlada para aquellos que tienen derechos de acceso. 1.3 Subsistemas de Sistemas Operativos TIPOS DE SISTEMAS OPERATIVOS En esta sección se describirán las características que clasifican a los sistemas operativos, básicamente se cubrirán tres clasificaciones: sistemas operativos por su estructura (visión interna), sistemas operativos por los servicios que ofrecen y, finalmente, sistemas operativos por la forma en que ofrecen sus servicios (visión externa). 2.1 Sistemas Operativos por su Estructura Según [Alcal92], se deben observar dos tipos de requisitos cuando se construye un sistema operativo, los cuales son: Requisitos de usuario: Sistema fácil de usar y de aprender, seguro, rápido y adecuado al uso al que se le quiere destinar. Requisitos del software: Donde se engloban aspectos como el mantenimiento, forma de operación, restricciones de uso, eficiencia, tolerancia frente a los errores y flexibilidad. A continuación se describen las distintas estructuras que presentan los actuales sistemas operativos para satisfacer las necesidades que de ellos se quieren obtener. 2.1.1 Estructura monolítica. Es la estructura de los primeros sistemas operativos constituídos fundamentalmente por un solo programa compuesto de un conjunto de rutinas entrelazadas de tal forma que cada una puede llamar a cualquier otra (Ver Fig. 2). Las características fundamentales de este tipo de estructura son: Construcción del programa final a base de módulos compilados separadamente que se unen a través del ligador. Buena definición de parámetros de enlace entre las distintas rutinas existentes, que puede provocar mucho acoplamiento. Carecen de protecciones y privilegios al entrar a rutinas que manejan diferentes aspectos de los recursos de la computadora, como memoria, disco, etc. Generalmente están hechos a medida, por lo que son eficientes y rápidos en su ejecución y gestión, pero por lo mismo carecen de flexibilidad para soportar diferentes ambientes de trabajo o tipos de aplicaciones. Estructura jerárquica. A medida que fueron creciendo las necesidades de los usuarios y se perfeccionaron los sistemas, se hizo necesaria una mayor organización del software, del sistema operativo, donde una parte del sistema contenía subpartes y esto organizado en forma de niveles. Se dividió el sistema operativo en pequeñas partes, de tal forma que cada una de ellas estuviera perfectamente definida y con un claro interface con el resto de elementos. Se constituyó una estructura jerárquica o de niveles en los sistemas operativos, el primero de los cuales fue denominado THE (Technische Hogeschool, Eindhoven), de Dijkstra, que se utilizó con fines didácticos (Ver Fig. 3). Se puede pensar también en estos sistemas como si fueran `multicapa’. Multics y Unix caen en esa categoría. [Feld93]. En la estructura anterior se basan prácticamente la mayoría de los sistemas operativos actuales. Otra forma de ver este tipo de sistema es la denominada de anillos concéntricos o “rings”. En el sistema de anillos, cada uno tiene una apertura, conocida como puerta o trampa (trap), por donde pueden entrar las llamadas de las capas inferiores. De esta forma, las zonas más internas del sistema operativo o núcleo del sistema estarán más protegidas de accesos indeseados desde las capas más externas. Las capas más internas serán, por tanto, más privilegiadas que las externas. Máquina Virtual. Se trata de un tipo de sistemas operativos que presentan una interface a cada proceso, mostrando una máquina que parece idéntica a la máquina real subyacente. Estos sistemas operativos separan dos conceptos que suelen estar unidos en el resto de sistemas: la multiprogramación y la máquina extendida. El objetivo de los sistemas operativos de máquina virtual es el de integrar distintos sistemas operativos dando la sensación de ser varias máquinas diferentes. El núcleo de estos sistemas operativos se denomina monitor virtual y tiene como misión llevar a cabo la multiprogramación, presentando a los niveles superiores tantas máquinas virtuales como se soliciten. Estas máquinas virtuales no son máquinas extendidas, sino una réplica de la máquina real, de manera que en cada una de ellas se pueda ejecutar un sistema operativo diferente, que será el que ofrezca la máquina extendida al usuario (Ver Fig. 5). Cliente-servidor ( Microkernel) El tipo más reciente de sistemas operativos es el denominado Cliente-servidor, que puede ser ejecutado en la mayoría de las computadoras, ya sean grandes o pequeñas. Este sistema sirve para toda clase de aplicaciones por tanto, es de propósito general y cumple con las mismas actividades que los sistemas operativos convencionales. El núcleo tiene como misión establecer la comunicación entre los clientes y los servidores. Los procesos pueden ser tanto servidores como clientes. Por ejemplo, un programa de aplicación normal es un cliente que llama al servidor correspondiente para acceder a un archivo o realizar una operación de entrada/salida sobre un dispositivo concreto. A su vez, un proceso cliente puede actuar como servidor para otro.” [Alcal92]. Este paradigma ofrece gran flexibilidad en cuanto a los servicios posibles en el sistema final, ya que el núcleo provee solamente funciones muy básicas de memoria, entrada/salida, archivos y procesos, dejando a los servidores proveer la mayoría que el usuario final o programador puede usar. Estos servidores deben tener mecanismos de seguridad y protección que, a su vez, serán filtrados por el núcleo que controla el hardware. Actualmente se está trabajando en una versión de UNIX que contempla en su diseño este paradigma. 2.2 Sistemas Operativos por Servicios Esta clasificación es la más comúnmente usada y conocida desde el punto de vista del usuario final. Esta clasificación se comprende fácilmente con el cuadro sinóptico que a continuación se muestra en la Fig. 6. Monousuario. Los sistemas operativos monousuarios son aquéllos que soportan a un usuario a la vez, sin importar el número de procesadores que tenga la computadora o el número de procesos o tareas que el usuario pueda ejecutar en un mismo instante de tiempo. Las computadoras personales típicamente se han clasificado en este renglón. Multiusuario Los sistemas operativos multiusuarios son capaces de dar servicio a más de un usuario a la vez, ya sea por medio de varias terminales conectadas a la computadora o por medio de sesiones remotas en una red de comunicaciones. No importa el número de procesadores en la máquina ni el número de procesos que cada usuario puede ejecutar simultáneamente. Monotareas Los sistemas monotarea son aquellos que sólo permiten una tarea a la vez por usuario. Puede darse el caso de un sistema multiusuario y monotarea, en el cual se admiten varios usuarios al mismo tiempo pero cada uno de ellos puede estar haciendo solo una tarea a la vez. Multitareas Un sistema operativo multitarea es aquél que le permite al usuario estar realizando varias labores al mismo tiempo. Por ejemplo, puede estar editando el código fuente de un programa durante su depuración mientras compila otro programa, a la vez que está recibiendo correo electrónico en un proceso en background. Es común encontrar en ellos interfaces gráficas orientadas al uso de menús y el ratón, lo cual permite un rápido intercambio entre las tareas para el usuario, mejorando su productividad. Uniproceso Un sistema operativo uniproceso es aquél que es capaz de manejar solamente un procesador de la computadora, de manera que si la computadora tuviese más de uno le sería inútil. El ejemplo más típico de este tipo de sistemas es el DOS y MacOS?. Multiproceso Un sistema operativo multiproceso se refiere al número de procesadores del sistema, que es más de uno y éste es capaz de usarlos todos para distribuir su carga de trabajo. Generalmente estos sistemas trabajan de dos formas: simétrica o asimétricamente. Cuando se trabaja de manera asimétrica, el sistema operativo selecciona a uno de los procesadores el cual jugará el papel de procesador maestro y servirá como pivote para distribuir la carga a los demás procesadores, que reciben el nombre de esclavos. Cuando se trabaja de manera simétrica, los procesos o partes de ellos (threads) son enviados indistintamente a cualesquira de los procesadores disponibles, teniendo, teóricamente, una mejor distribución y equilibrio en la carga de trabajo bajo este esquema. Se dice que un thread es la parte activa en memoria y corriendo de un proceso, lo cual puede consistir de un área de memoria, un conjunto de registros con valores específicos, la pila y otros valores de contexto. Us aspecto importante a considerar en estos sistemas es la forma de crear aplicaciones para aprovechar los varios procesadores. Existen aplicaciones que fueron hechas para correr en sistemas monoproceso que no toman ninguna ventaja a menos que el sistema operativo o el compilador detecte secciones de código paralelizable, los cuales son ejecutados al mismo tiempo en procesadores diferentes. Por otro lado, el programador puede modificar sus algoritmos y aprovechar por sí mismo esta facilidad, pero esta última opción las más de las veces es costosa en horas hombre y muy tediosa, obligando al programador a ocupar tanto o más tiempo a la paralelización que a elaborar el algoritmo inicial. Sistemas Operativos por la Forma de Ofrecer sus Servicios Esta clasificación también se refiere a una visión externa, que en este caso se refiere a la del usuario, el cómo accesa los servicios. Bajo esta clasificación se pueden detectar dos tipos principales: sistemas operativos de red y sistemas operativos distribuídos. Sistemas Operativos de Red Los sistemas operativos de red se definen como aquellos que tiene la capacidad de interactuar con sistemas operativos en otras computadoras por medio de un medio de transmisión con el objeto de intercambiar información, transferir archivos, ejecutar comandos remotos y un sin fin de otras actividades. El punto crucial de estos sistemas es que el usuario debe saber la sintaxis de un cinjunto de comandos o llamadas al sistema para ejecutar estas operaciones, además de la ubicación de los recursos que desee accesar. Por ejemplo, si un usuario en la computadora hidalgo necesita el archivo matriz.pas que se localiza en el directorio /software/codigo en la computadora morelos bajo el sistema operativo UNIX, dicho usuario podría copiarlo a través de la red con los comandos siguientes: hidalgo% hidalgo% rcp morelos:/software/codigo/matriz.pas . hidalgo% En este caso, el comando rcp que significa “remote copy” trae el archivo indicado de la computadora morelos y lo coloca en el directorio donde se ejecutó el mencionado comando. Lo importante es hacer ver que el usuario puede accesar y compartir muchos recursos. Sistemas Operativos Distribuídos Los sistemas operativos distribuídos abarcan los servicios de los de red, logrando integrar recursos ( impresoras, unidades de respaldo, memoria, procesos, unidades centrales de proceso ) en una sola máquina virtual que el usuario accesa en forma transparente. Es decir, ahora el usuario ya no necesita saber la ubicación de los recursos, sino que los conoce por nombre y simplementa los usa como si todos ellos fuesen locales a su lugar de trabajo habitual. Todo lo anterior es el marco teórico de lo que se desearía tener como sistema operativo distribuído, pero en la realidad no se ha conseguido crear uno del todo, por la complejidad que suponen: distribuír los procesos en las varias unidades de procesamiento, reintegrar sub-resultados, resolver problemas de concurrencia y paralelismo, recuperarse de fallas de algunos recursos distribuídos y consolidar la protección y seguridad entre los diferentes componentes del sistema y los usuarios. [Tan92]. Los avances tecnológicos en las redes de área local y la creación de microprocesadores de 32 y 64 bits lograron que computadoras mas o menos baratas tuvieran el suficiente poder en forma autónoma para desafiar en cierto grado a los mainframes, y a la vez se dio la posibilidad de intercomunicarlas, sugiriendo la oportunidad de partir procesos muy pesados en cálculo en unidades más pequeñas y distribuirlas en los varios microprocesadores para luego reunir los sub-resultados, creando así una máquina virtual en la red que exceda en poder a un mainframe. El sistema integrador de los microprocesadores que hacer ver a las varias memorias, procesadores, y todos los demás recursos como una sola entidad en forma transparente se le llama sistema operativo distribuído. Las razones para crear o adoptar sistemas distribuídos se dan por dos razones principales: por necesidad ( debido a que los problemas a resolver son inherentemente distribuídos ) o porque se desea tener más confiabilidad y disponibilidad de recursos. En el primer caso tenemos, por ejemplo, el control de los cajeros automáticos en diferentes estados de la república. Ahí no es posible ni eficiente mantener un control centralizado, es más, no existe capacidad de cómputo y de entrada/salida para dar servicio a los millones de operaciones por minuto. En el segundo caso, supóngase que se tienen en una gran empresa varios grupos de trabajo, cada uno necesita almacenar grandes cantidades de información en disco duro con una alta confiabilidad y disponibilidad. La solución puede ser que para cada grupo de trabajo se asigne una partición de disco duro en servidores diferentes, de manera que si uno de los servidores falla, no se deje dar el servicio a todos, sino sólo a unos cuantos y, más aún, se podría tener un sistema con discos en espejo ( mirror ) a través de la red,de manera que si un servidor se cae, el servidor en espejo continúa trabajando y el usuario ni cuenta se da de estas fallas, es decir, obtiene acceso a recursos en forma transparente. Ventajas de los Sistemas Distribuídos En general, los sistemas distribuídos (no solamente los sistemas operativos) exhiben algunas ventajas sobre los sistemas centralizados que se describen enseguida. • Economía: El cociente precio/desempeño de la suma del poder de los procesadores separados contra el poder de uno solo centralizado es mejor cuando están distribuídos. • Velocidad: Relacionado con el punto anterior, la velocidad sumada es muy superior. • Confiabilidad: Si una sola máquina falla, el sistema total sigue funcionando. • Crecimiento: El poder total del sistema puede irse incrementando al añadir pequeños sistemas, lo cual es mucho más difícil en un sistema centralizado y caro. • Distribución: Algunas aplicaciones requieren de por sí una distribución física. Por otro lado, los sistemas distribuídos también exhiben algunas ventajas sobre sistemas aislados. Estas ventajas son: • Compartir datos: Un sistema distribuído permite compartir datos más fácilmente que los sistemas aislados, que tendrian que duplicarlos en cada nodo para lograrlo. • Compartir dispositivos: Un sistema distribuído permite accesar dispositivos desde cualquier nodo en forma transparente, lo cual es imposible con los sistemas aislados. El sistema distribuído logra un efecto sinergético. • Comunicaciones: La comunicación persona a persona es factible en los sistemas distribuídos, en los sistemas aislados no. _ Flexibilidad: La distribución de las cargas de trabajo es factible en el sistema distribuídos, se puede incrementar el poder de cómputo. Desventajas de los Sistemas Distribuídos Así como los sistemas distribuídos exhiben grandes ventajas, también se pueden identificar algunas desventajas, algunas de ellas tan serias que han frenado la producción comercial de sistemas operativos en la actualidad. El problema más importante en la creación de sistemas distribuídos es el software: los problemas de compartición de datos y recursos es tan complejo que los mecanismos de solución generan mucha sobrecarga al sistema haciéndolo ineficiente. El checar, por ejemplo, quiénes tienen acceso a algunos recursos y quiénes no, el aplicar los mecanismos de protección y registro de permisos consume demasiados recursos. En general, las soluciones presentes para estos problemas están aún en pañales. Otros problemas de los sistemas operativos distribuídos surgen debido a la concurrencia y al paralelismo. Tradicionalmente las aplicaiones son creadas para computadoras que ejecutan secuencialmente, de manera que el identificar secciones de código `paralelizable’ es un trabajo ardúo, pero necesario para dividir un proceso grande en sub-procesos y enviarlos a diferentes unidades de procesamiento para lograr la distribución. Con la concurrencia se deben implantar mecanismos para evitar las condiciones de competencia, las postergaciones indefinidas, el ocupar un recurso y estar esperando otro, las condiciones de espera circulares y , finalmente, los “abrazos mortales” (deadlocks). Estos problemas de por sí se presentan en los sistemas operativos multiusuarios o multitareas, y su tratamiento en los sistemas distribuídos es aún más complejo, y por lo tanto, necesitará de algoritmos más complejos con la inherente sobrecarga esperada. Por otro lado, en el tema de sistemas distribuídos existen varios conceptos importantes referentes al hadware que no se ven en este trabajo: multicomputadoras, multiprocesadores, sistemas acoplados débil y fuertemente, etc. En [Tan92] páginas 366 - 376 puede encontrarse material relacionado a estos conceptos. 1.4 Estructuras de Sistemas Operativos 1.4.1 Sistemas monolíticos Este tipo de organización es, con diferencia, la más común. El sistema operativo se escribe como una colección de procedimientos, cada uno de los cuales puede llamar a los demás cada vez que así lo requiera. Cuando se usa esta técnica, cada procedimiento del sistema tiene una interfaz bien definida en términos de parámetros y resultados, y cada uno de ellos es libre de llamar a cualquier otro, si éste último proporciona un cálculo útil para el primero. Para construir el programa objeto real del sistema operativo siguiendo este punto de vista, se compilan de forma individual los procedimientos, o los ficheros que contienen los procedimientos, y después se enlazan en un sólo fichero objeto con el enlazador. En términos de ocultación de la información, ésta es prácticamente nula: cada procedimiento es visible a los demás (en contraste con una estructura con módulos o paquetes, en la que la mayoría de la información es local a un módulo, y donde sólo los datos señalados de forma expresa pueden ser llamados desde el exterior del módulo). Los servicios (mediante llamadas al sistema) que proporciona el sistema operativo se solicitan colocando los parámetros en lugares bien definidos, como los registros o la pila, para después ejecutar una instrucción especial de trampa, a veces referida como llamada al núcleo o llamada al supervisor. Esta instrucción cambia la máquina del modo usuario al modo núcleo (también conocido como modo supervisor), y transfiere el control al sistema operativo, lo que se muestra en el evento (1) de la figura 5.1. El sistema operativo examina entonces los parámetros de la llamada para determinar cual de ellas se desea realizar, como se muestra en (2) de la figura 5.1. A continuación, el sistema operativo analiza una tabla que contiene en la entrada k un apuntador al procedimiento que implementa la k-ésima llamada al sistema. Esta operación, que se muestra en (3) de la figura 5.1, identifica el procedimiento de servicio, al cual se llama. Por último, la llamada al sistema termina y el control vuelve al programa del usuario. Esta organización sugiere una estructura básica del sistema operativo: Un programa principal que llama al procedimiento del servicio solicitado. Un conjunto de procedimientos de servicio que lleva a cabo las llamadas al sistema. Un conjunto de procedimientos de utilidades que ayudan a los procedimientos de servicio. En este modelo, para cada llamada al sistema existe un procedimiento de servicio que se encarga de ella. Los procedimientos de utilidad hacen cosas necesarias para varios procedimientos de servicio, como por ejemplo, buscar los datos del programa del usuario. Esta división de los procedimientos en tres capas se muestra en la figura 5.2. 1.4.2 Modelo cliente-servidor Una tendencia de los sistema operativos modernos es la de trasladar el código a capas superiores, y eliminar la mayor parte posible del sistema operativo para mantener un núcleo mínimo. El punto de vista usual es el implantar la mayoría de las funciones del sistema operativo como procesos de usuario. Para solicitar un servicio, como la lectura de un bloque de cierto fichero, un proceso de usuario (denominado en este caso proceso cliente) envía la solicitud a un proceso servidor, que realiza el trabajo y devuelve la respuesta. En este modelo, que se muestra en la figura 5.3, lo único que hace el núcleo es controlar la comunicación entre los clientes y los servidores. Al separar el sistema operativo en partes, cada una de ellas controla una faceta del sistema, como el servicio a ficheros, servicio a procesos, servicio a terminales o servicio a la memoria; cada parte es pequeña y controlable. Además, puesto que todos los servidores se ejecutan como procesos en modo usuario, y no en modo núcleo, no tienen acceso directo al hardware. En consecuencia, si hay un error en el servidor de ficheros éste puede fallar, pero esto no afectará en general a toda la máquina. Otra de las ventajas del modelo cliente-servidor es su capacidad de adaptación para su uso en sistemas distribuidos (véase la figura 5.4). Si un cliente se comunica con un servidor mediante mensajes, el cliente no necesita saber si el mensaje se gestiona de forma local, en su máquina, o si se envía por medio de una red a un servidor en una máquina remota. En lo que respecta al cliente, lo mismo ocurre en ambos casos: se envió una solicitud y se recibió una respuesta. La idea anterior de un núcleo que sólo controla el transporte de mensajes de clientes a servidores, y viceversa, no es totalmente real. Algunas funciones del sistema operativo (como la introducción de órdenes en los registros físicos de los controladores de E/S) son difíciles, si no es que imposible de realizar, a partir de programas de usuario. Existen dos formas de afrontar este problema. Una es hacer que algunos procesos de servidores críticos (por ejemplo, los gestores de los dispositivos de E/S) se ejecuten en realidad en modo núcleo, con acceso total al hardware, pero de forma que se comuniquen con los demás procesos mediante el mecanismo normal de mensajes. La otra forma es construir una cantidad mínima de mecanismos dentro del núcleo, pero manteniendo las decisiones de política relativos a los usuarios dentro del espacio de los usuarios. Por ejemplo, el núcleo podría reconocer que cierto mensaje enviado a una dirección especial indica que se tome el contenido de ese mensaje y se cargue en los registros del controlador de algún disco, para iniciar la lectura del disco. En este ejemplo, el núcleo ni siquiera inspeccionaría los bytes del mensaje para ver si son válidos o tienen algún sentido; sólo los copiaría ciegamente en los registros del controlador del disco. Es evidente que debe utilizarse cierto esquema para limitar tales mensajes sólo a los procesos autorizados. La separación entre mecanismos y política es un concepto importante, aparece una y otra vez en diversos contextos de los sistemas operativos. 1.5 Analisis de So Comerciales (LINUX, UNÍX, Windows, MACH, OS/2, VAX, MVS, etc.) Unidad 2 Administración de procesos 2.1 Descripción y Control de Procesos Sistemas Operativos En algunos sistemas operativos como en los de tiempo compartido, cada programa que se ejecuta, por ejemplo mediante una orden de EJECUTAR dada por el usuario, se trata como un proceso independiente. Estos procesos generados por el O.S se denominan IMPLÍCITOS. Una vez terminada la ejecución de los mismos, su eliminación también la realiza el propio O.S. Asi mismo, el O.S proporciona en tiempo real los servicios que son necesarios para que el usuario pueda definir procesos de forma explicita. Los programa acceden a estos servicios realizando LLAMADAS AL SISTEMA(SYSTEM CALL). Estas llamadas pueden aparecer incrustadas en el código de un programa de usuario o del propio sistema, en cuyo caso, se asemejan a llamadas a procedimientos o funciones que dan lugar a transferencias de rutinas del O.S cuando se invocan en tiempo real. Las llamadas al sistema se realizan tambien, pero de forma indirecta, cuando se dan ordenes al O.S a través de un terminal(ó SHELL)la rutina de monitorización del terminal( que es asu vez un proceso) se encarga de transformar la ordenes en llamadas al sistema. De este modo, al comienzo de la ejecución del programa principal de un usuario se inicia la ejecución de un proceso. A su vez el proceso podría crear nuevos procesos. En este caso, el proceso que crea otro nuevo se denomina proceso padre(parent process), y el proceso creado de denomina proceso hijo(child process). Una vez creado un proceso hijo, la ejecución de padre e hijo transcurre de manera concurrente. De esta forma se puede crear una jerarquía arborescente de procesos, en la que un padre puede tener varios hijos y estos pueden tener otros hijos, etc, pero donde cada hijo sólo tiene un padre. [CAPTURADO POR R.EDER. W.J ITVH VHSA, TAB!!] 2.2 Definicion de Proceso Sistemas Operativos Un proceso es una instancia de ejecución de un programa, caracterizado por su contador de programa, su palabra de estado(Palabra que recoge en binario el estado del entorno de programa, después de la ejecución de cada instrucción.), sus registros ( pequeña memoria interna del microprocesador, formada generalmente por biestables) del procesador, su segmento de texto, pila (zona reservada de la memoria o registros hardware donde se almacena temporalmente el estado o información de un programa, rutina, etc..) y datos, etc. 2.3 Estados de Procesos Sistemas Operativos Como se ha puesto de manifiesto, el proceso es un elemento dinámico que puede pasar por diferentes estados a lo lo largo de su existencia. De forma general, un proceso puede encontrarse en un instante determinado en uno de lo siguientes estados: Activo, Preparado, Bloqueado o suspendido, Nonato y Muerto. La tarea activa es la que está ejecutándose en un instante dado.En el caso de sistemas con un único procesador, sólo puede haber una tarea en dicho estado en cada instante. En el estado de preparado, se encuentran todas las tareas que están listas para ejecutarse pero que esperan a que un/el procesador quede libre(hay otros procesos más prioritarios en ejecución). Las tareas que están a la espera de que se cumpla una condición y que por lo tanto, no están preparadas para ejecutase, se dice que están en el estado bloqueado o suspendido; alguno ejemplos de condición son: que se termine una operación de E/S o que se reciba una señal de sincronización. Un proceso esta muerto cuando ha terminado su ejecución o bien el sistema operativo a detectado un error fatal y lo ha transferido a dicho estado. Tambien es posible que haya entrado él como resultado de un fallo del propio sistema operativo(cuestión que desgraciadamente suele ocurrir frecuentemente en algunos OS. El estado de nonato indica que el programa realmente existe pero todavía no es conocido por el OS. 2.4 Control de Procesos Sistemas Operativos Procesos Def: Un proceso es un programa en ejecución. Estados de procesos El estado de un proceso define su actividad actual. Durante su existencia, un proceso pasa por una serie de estados discretos. Estos estados son se muestran en el siguiente diagrama: A continuación se describe cada uno de estos procesos: Estado Descripción Nuevo Se dice que un proceso está en estado de nuevo cuando apenas se encuentra en proceso de crearse. Listo Un proceso está en estado de listo, cuando podría usar una UCP, si hubiera una disponible. En Se dice que un proceso está estado de ejecución, si en ese ejecución momento tiene esta ocupando la CPU. Se dice que un proceso está en estado de bloqueado, si espera que Bloqueado ocurra algo, como por ejemplo, la terminación de una E/S, para así poder ponerse en marcha. Terminado Cuando un proceso se ha completado su ejecución pasa a ser un proceso terminado. Transiciones de estados: Cuando un trabajo es admitido se crea un proceso equivalente, y es insertado en la ultima parte de la cola de listos (ésta se tratara más adelante). Cuando un proceso pasa de un estado a otro se dice que hace una transición de estado, estas transiciones se describen a continuación. Transición Descripción Admitido(Proceso):Nuevo Listo Cuando un proceso se ha creado y se le es permito para competir por la CPU. Despacho(Proceso):Listo En ejecución La asignación de la CPU al primer proceso de la lista de listos es llamado despacho y es ejecutado por la entidad de sistema llamada despachador. Mientras que el proceso tenga la CPU se dice que esta en ejecución. Tiempo excedido(Proceso):En ejecución Listo El S.O , cuando un proceso se le expira el intervalo de tiempo asignado para estar en ejecución (CUANTO), hace que este proceso que se hallaba en estado de ejecución pase al estado de listo y inmediatamente el despachador hace que el primer proceso de la lista pase a estado de ejecución. Bloqueo(Proceso):En ejecución Bloqueado Si un proceso que se encuentra en estado de ejecución inicia una operación de E/s antes que termine su cuanto, el proceso voluntariamente abandona la CPU, es decir, el proceso se bloquea a sí mismo. Despertar(Proceso):Bloqueo Listo La única transición posible en nuestro modelo básico ocurre cuando acaba una operación de E/S (o alguna otra causa por la que esté esperando el proceso), y esta termina pasa a el estado de listo. Salir(Proceso):En ejecución Terminado Esta transición ocurre cuando el proceso se ha terminado de ejecutarse, y pasa a un estado de terminado. Nota: Para prevenir que un proceso monopolice el sistema, el S.O ajusta un reloj de interrupción del hardware para permitir al usuario ejecutar su proceso durante un intervalo de tiempo especifico. Cuando este tiempo expira el reloj genera una interrupción, haciendo que el S.O recupere el control. Cuando hay demasiada carga en el sistema se puede hacer uso de suspensión y reanudación por el S.O, para equilibrar la carga del sistema. Para la reanudación y la suspensión será necesario anexar otros dos estado los cuales son: suspendido listo y suspendido bloqueado, con las siguiente definición de transiciones: Suspende_ejecución(Proceso): En ejecución Suspendido listo Suspende_bloqueado(Proceso): Bloqueado Suspendido bloqueado Reanuada(Proceso): Suspendido listo Listo Termino_E/S(Proceso): Suspendido bloqueado Suspendido listo Bloque de control de proceso (PCB): La manifestación de un proceso en un S.O es un bloque de control de proceso (PCB), es decir cada proceso es representado por su PCB. El PCB es una estructura de datos que contiene cierta información importante acerca del proceso. Esta información es: Estado actual del proceso Id único para proceso Prioridad del proceso Apuntadores para localizar la memoria del proceso Apuntadores para asignar recursos Area para preservar registros. El PCB es un almacenamiento central de información que permite al S.O localizar toda la información clave sobre el proceso. Operaciones con procesos: Crear un proceso Destruir un proceso Suspender un proceso Reanudar un proceso Cambiar la prioridad de un proceso Bloquear un proceso Despertar un proceso Despachar un proceso La creación de un proceso implica varias operaciones, incluyendo: Dar nombre al proceso Registrarlo en la lista de sistema de procesos conocidos Determinar la prioridad inicial del proceso Crear el PCB Asignar recursos iniciales Un proceso puede crear varios procesos nuevos, a través de una llamada al sistema de “crear proceso”, durante el curso de ejecución. El proceso creador se denomina proceso padre , y los nuevos procesos son los hijos de ese proceso. Cada uno de estos procesos nuevos puede a su vez crear otros procesos, formando un árbol de procesos. Cuando un proceso crea un proceso nuevo, hay dos posibilidades en términos de ejecución: El padre sigue ejecutándose de forma concurrente con sus hijos. El padre espera hasta que algunos de sus hijos, o todos, han terminado. También hay dos posibilidades en términos del espacio de direcciones del nuevo proceso: El proceso hijo es un duplicado del proceso padre. Se carga un programa en el proceso hijo. La terminación de procesos Un proceso acaba cuando termina de ejecutar su último enunciado y le pide al S.O que lo elimine utilizando la llamada al sistema salir (exit). En este momento, el proceso podría devolver datos(salidas) a su proceso padre (por medio de la llamada al sistema esperar). El S.O liberará los recursos del proceso, incluidos la memoria física y virtual, archivos abiertos y buffers de E/S. Hay otras circunstancias por la que un proceso puede terminar, esta puede ser por la llamada al sistema abortar. Un padre podría terminar la ejecución de uno de sus hijos por diversas razones, como: El hijo ha excedido en la utilización de algunos de los recursos que se le asignaron. La tarea que se asignó al hijo ya no es necesaria. El padre va a salir, y el sistema operativo no permite que un hijo continúe si su padre termina. Planificadores: En un sistema por lotes cuando hay demasiados procesos de los que se pueden inmediatamente ejecutarse se deben de colocar en spool en un dispositivo de almacenamiento masivo (por lo regular un disco), y ahí esperar hasta que puedan ser ejecutados. Para la planificación se hace uso de los sig. planificadores de procesos: Planificador a corto plazo (o planificador de trabajos): este escoge procesos que estén el spool (en disco) y los carga en la memoria para que se ejecuten. Planificador a largo plazo (o planificador de CPU): este escoge entre los procesos que estén listos para ejecutarse, y asigna la CPU a uno de ellos. El planificador a largo plazo se ejecuta con mayor frecuencia que el de largo plazo y también controla el grado de multiprogramación (numero de procesos que están en la MP). Un proceso se describen como: Proceso limitado por CPU, este ocupa su mayor tiempo para realizar cálculos. Proceso limitado por E/S, este dedica su mayor tiempo a realizar operaciones de E/S. En un sistema de tiempo se introduce un planificador más , el cual es: Planificador a mediano plazo: este intercambia procesos a disco y luego intercambia a la memoria, a este esquema se le denomina intercambio (swapping). Esto puede ser necesario para mejorar la mezcla de procesos o cuando un cambio en las necesidades de memoria ha distribuido la memoria disponible entre demasiados procesos y es preciso liberar algo de memoria. Comunicación entre procesos: Hay dos formas en que se puede comunicar los procesos, los cuales son: Por medio de un esquema de comunicación por memoria compartida (Buffer) Por medio de un mecanismo de comunicación entre procesos (IPC, Interprocess comunication). La IPC ofrece un mecanismo que permite a los procesos cumunicarse y sincronizar sus acciones. La mejor forma de proveer la comunicación entre procesos es mediante un sistema de mensajes. La función de un sistema de mensaje es permitir a los procesos comunicarse entre sí sin tener que recurrir a variables compartidas. Un recurso IPC ofrece por los menos 2 operaciones: enviar (mensaje) (send) y recibir (mesanje) (receive). Sea P y Q dos procesos que requieren comunicarse deberán enviarse mensajes; para ello debe existir un enlace de comunicación entre ellos. Este enlace puede implementarse de diversas maneras. Los métodos para implementar lógicamente un enlace y las operaciones de enviar / recibir son: Comunicación directa o indirecta Uso de buffer automático o explícito Envío por copia o envío por referencia Mensajes de tamaño fijo o variables Comunicación directa: Aquí cada proceso que desee comunicarse debe nombrar explícitamente el destinatario o el remitente de la comunicación. Este esquema se define las primitivas de la sig. manera: Enviar(P,mensaje): Enviar un mensaje al proceso P. Recibir(Q. Mensaje): Recibir un mensaje del proceso Q. Con las siguientes propiedades: Se establece automáticamente el enlace entre cada par de procesos. Lo procesos sólo necesitan conocer la identidad de otro para la comunicación. Solo hay un enlace entre cada par de procesos. El enlace puede ser unidireccional o bidireccional. Este esquema exhibe un simetría de direccionamiento; es decir, los procesos tanto emisor como receptor necesitan nombrar al otro para comunicarse. Otra variante de este esquema es utilizar asimetría de direccionamiento, con la sig. primitivas: Enviar(P,mensaje): enviar un mensaje al proceso P. Recibir(Id,mensaje) : recibir un mensaje de cualquier proceso con el que hubo comunicación. Aquí sólo el emisor nombra al destinatario; el destinatario no ésta obligado a nombrar al emisor. Comunicación indirecta: Aquí los mensajes se envían a, y se reciben de, buzones (también llamados PUERTOS). Un buzón puede considerarse en lo abstracto como un objeto en el que los procesos pueden colocar mensajes y del cual se pueden sacar mensajes. Cada buzón tiene una identificación única. Aquí dos proceso se pueden comunicarse sólo si comparten un buzón. Las primitivas se definen como: Enviar (A,mensaje): enviar un mensaje al buzón A. Recibir (A,mensaje): recibir un mensaje del buzón A. Un enlace de comunicación tiene las sig. propiedades: Se establece un enlace entre un par de procesos sólo si tienen un buzón compartido. Un enlace puede estar asociado a más de dos procesos. Entre cada par de procesos en comunicación puede haber varios enlaces distintos, cada uno de los cuales corresponderá a un buzón. Los enlaces pueden ser unidireccionales o bidereccionales. Hay varias formas de designar el dueño de y los usuarios de un buzón dado. Una posibilidad es permitir que un proceso declare variables de tipo buzón. El proceso que declara un buzón es el dueño de ese buzón. Cualquier otro proceso que conozca el nombre de dicho buzón podrá usarlo. Por otro lado, un buzón propiedad del S.O tiene existencia propia; es independiente y no está unido a ningún proceso específico. El S.O establece un mecanismo que permite a un proceso: Crear un buzón nuevo Enviar y recibir mensajes a través del buzón Destruir un buzón. 2.5 Procesos e Hilos Tema en otro documento…. 2.6Concurrencia Exclusión Mutua y Sincronización Los temas fundamentales del diseño de sistemas operativos están relacionados con la gestión de procesos e hilos: • Multiprogramación: consiste en la gestión de varios procesos dentro de un sistema mono-procesador. • Multiprocesamiento: consiste en la gestión de varios procesos, dentro de un sistema multiprocesador. • Procesamiento distribuido: consiste en la gestión de varios procesos, ejecutándose en sistemas de computadores múltiples y distribuidos. La reciente proliferación de las agrupaciones es el principal ejemplo de este tipo de sistemas. La concurrencia es fundamental en todas estas áreas y para el diseño sistemas operativos. La concurrencia comprende un gran número de cuestiones de diseño, incluida la comunicación entre procesos, compartición y competencia por los recursos, sincronización de la ejecución de varios procesos y asignación del tiempo de procesador a los procesos. Se verá que estas cuestiones no solo surgen en entornos de multiprocesadores y proceso distribuido, sino incluso en sistemas multiprogramados con un solo procesador. La concurrencia puede presentarse en tres contextos diferentes: • Múltiples aplicaciones: la multiprogramación se creó para permitir que el tiempo de procesador de la máquina fuese compartido dinámicamente entre varias aplicaciones activas. • Aplicaciones estructuradas: como ampliación de los principios del diseño modular y la programación estructurada, algunas aplicaciones pueden implementarse eficazmente como un conjunto de procesos concurrentes. • Estructura del sistema operativo: las mismas ventajas de estructuración son aplicables a los programadores de sistemas y se ha comprobado que algunos sistemas operativos están implementados como un conjunto de procesos o hilos. PRINCIPIOS GENERALES DE LA CONCURRENCIA En un sistema multiprogramado con un único procesador, los procesos se intercalan en el tiempo aparentando una ejecución simultánea. Aunque no se logra un procesamiento paralelo y produce una sobrecarga en los intercambios de procesos, la ejecución intercalada produce beneficios en la eficiencia del procesamiento y en la estructuración de los programas. La intercalación y la superposición pueden contemplarse como ejemplos de procesamiento concurrente en un sistema monoprocesador, los problemas son consecuencia de la velocidad de ejecución de los procesos que no pueden predecirse y depende de las actividades de otros procesos, de la forma en que el sistema operativo trata las interrupciones surgen las siguientes dificultades: Compartir recursos globales es riesgoso Para el sistema operativo es difícil gestionar la asignación óptima de recursos. Las dificultades anteriores también se presentan en los sistemas multiprocesador. El hecho de compartir recursos ocasiona problemas, por esto es necesario proteger a dichos recursos. Los problemas de concurrencia se producen incluso cuando hay un único procesado LABORES DEL SISTEMA OPERATIVO Elementos de gestión y diseño que surgen por causa de la concurrencia: 1) El sistema operativo debe seguir a los distintos procesos activos 2) El sistema operativo debe asignar y retirar los distintos recursos a cada proceso activo, entre estos se incluyen: _Tiempo de procesador _Memoria _Archivos _Dispositivos de E/S 3) El sistema operativo debe proteger los datos y los recursos físicos de cada proceso contra injerencias no intencionadas de otros procesos. 4) Los resultados de un proceso deben ser independientes de la velocidad a la que se realiza la ejecución de otros procesos concurrentes. Para abordar la independencia de la velocidad debemos ver las formas en las que los procesos interactúan. INTERACCIÓN ENTRE PROCESOS Se puede clasificar los en que interactúan los procesos en función del nivel de conocimiento que cada proceso tiene de la existencia de los demás. Existen tres niveles de conocimiento: 1) Los procesos no tienen conocimiento de los demás: son procesos independientes que no operan juntos. Ej: la multiprogramación de procesos independientes. Aunque los procesos no trabajen juntos, el sistema operativo se encarga de la “competencia” por los recursos. 2) Los procesos tienen un conocimiento indirecto de los otros: los procesos no conocen a los otros por sus identificadores de proceso, pero muestran cooperación el objeto común. 3) Los procesos tienen conocimiento directo de los otros: los procesos se comunican por el identificador de proceso y pueden trabajar conjuntamente. Competencia entre procesos por los recursos Los procesos concurrentes entran en conflicto cuando compiten por el uso del mismo recurso; dos o más procesos necesitan acceder a un recurso durante su ejecución .Cada proceso debe dejar tal y como esté el estado del recurso que utilice. La ejecución de un proceso puede influir en el comportamiento de los procesos que compiten. Por Ej. Si dos procesos desean acceder a un recurso, el sistema operativo le asignará el recurso a uno y el otro tendrá que esperar. Cuando hay procesos en competencia, se deben solucionar tres problemas de control: la necesidad de exclusión mutua. Suponiendo que dos procesos quieren acceder a un recurso no compartible. A estos recursos se les llama “recursos críticos” y la parte del programa que los utiliza es la “sección crítica” del programa. Es importante que sólo un programa pueda acceder a su sección crítica en un momento dado. Hacer que se cumpla la exclusión mutua provoca un interbloqueo. Otro problema es la inanición si tres procesos necesitan acceder a un recurso, P1 posee al recurso, luego lo abandona y le concede el acceso al siguiente proceso P2, P1 solicita acceso de nuevo y el sistema operativo concede el acceso a P1 YP2 alternativamente, se puede negar indefinidamente a P3 el acceso al recurso. El control de competencia involucra al sistema operativo, porque es el que asigna los recursos. Cooperación entre procesos por compartimiento Comprende los procesos que interactúan con otros sin tener conocimiento explícito de ellos. Ej. : Varios procesos pueden tener acceso a variables compartidas. Los procesos deben cooperar para asegurar que los datos que se comparten se gestionan correctamente. Los mecanismos de control deben garantizar la integridad de los datos compartidos. Cooperación entre procesos por comunicación Los distintos procesos participan en una labor común que une a todos los procesos. La comunicación sincroniza o coordina las distintas actividades, está formada por mensajes de algún tipo. Las primitivas para enviar y recibir mensajes, vienen dadas como parte del lenguaje de programación o por el núcleo del sistema operativo REQUISITOS PARA LA EXCLUSIÓN MUTUA Sólo un proceso, de todos los que poseen secciones críticas por el mismo recurso compartido, debe tener permiso para entrar en ella en un momento dado. Un proceso que se interrumpe en una sección no crítica debe hacerlo sin interferir con los otros procesos. Un proceso no debe poder solicitar acceso a una sección crítica para después ser demorado indefinidamente, no puede permitirse el interbloqueo o la inanición. Si ningún proceso está en su sección crítica, cualquier proceso que solicite entrar en la suya debe poder hacerlo sin demora. No se debe suponer sobre la velocidad relativa de los procesos o el número de procesadores. Un proceso permanece en su sección crítica por un tiempo finito. Una manera de satisfacer los requisitos de exclusión mutua es dejar la responsabilidad a los procesos que deseen ejecutar concurrentemente. Tanto si son programas del sistema como de aplicación, los procesos deben coordinarse unos con otros para cumplir la exclusión mutua, sin ayuda del lenguaje de programación o del sistema operativo. Estos métodos se conocen como soluciones por software. EXCLUSIÓN MUTUA: SOLUCIONES POR SOFTWARE Pueden implementarse soluciones de software para los procesos concurrentes que se ejecuten en máquinas monoprocesador o multiprocesador con memoria principal compartida. ALGORITMO DE DEKKER La solución se desarrolla por etapas. Este método ilustra la mayoría de los errores habituales que se producen en la construcción de programas concurrentes. Primer intento Cualquier intento de exclusión mutua debe depender de algunos mecanismos básicos de exclusión en el hardware. El más habitual es que sólo se puede acceder a una posición de memoria en cada instante, teniendo en cuenta esto se reserva una posición de memoria global llamada turno. Un proceso que desea ejecutar su sección crítica primero evalúa el contenido de turno. Si el valor de turno es igual al número del proceso, el proceso puede continuar con su sección crítica. En otro caso el proceso debe esperar. El proceso en espera, lee repetitivamente el valor de turno hasta que puede entrar en su sección crítica. Este procedimiento se llama espera activa. Después de que un proceso accede a su sección crítica y termina con ella, debe actualizar el valor de turno para el otro proceso. Segundo intento: Cada proceso debe tener su propia llave de la sección crítica para que, si uno de ellos falla, pueda seguir accediendo a su sección crítica; para esto se define un vector booleano señal. Cada proceso puede evaluar el valor de señal del otro, pero no modificarlo. Cuando un proceso desea entrar en su sección crítica, comprueba la variable señal del otro hasta que tiene el valor falso (indica que el otro proceso no está en su sección crítica). Asigna a su propia señal el valor cierto y entra en su sección crítica. Cuando deja su sección crítica asigna falso a su señal. Si uno de los procesos falla fuera de la sección crítica, incluso el código para dar valor a las variables señal, el otro proceso no se queda bloqueado. El otro proceso puede entrar en su sección crítica tantas veces como quiera, porque la variable señal del otro proceso está siempre en falso. Pero si un proceso falla en su sección crítica o después de haber asignado cierto a su señal, el otro proceso estará bloqueado permanentemente. Tercer intento El segundo intento falla porque un proceso puede cambiar su estado después de que el otro proceso lo ha comprobado pero antes de que pueda entrar en su sección crítica. Si un proceso falla dentro de su sección crítica, incluso el código que da valor a la variable señal que controla el acceso a la sección crítica, el otro proceso se bloquea y si un proceso falla fuera de su sección crítica, el otro proceso no se bloquea. Si ambos procesos ponen sus variables señal a cierto antes de que ambos hayan ejecutado una sentencia, cada uno pensará que el otro ha entrado en su sección crítica, generando así un interbloqueo. Cuarto intento En el tercer intento, un proceso fijaba su estado sin conocer el estado del otro. Se puede arreglar esto haciendo que los procesos activen su señal para indicar que desean entrar en la sección crítica pero deben estar listos para desactivar la variable señal y ceder la preferencia al otro proceso. Existe una situación llamada bloqueo vital, esto no es un interbloqueo, porque cualquier cambio en la velocidad relativa de los procesos rompería este ciclo y permitiría a uno entrar en la sección crítica. Recordando que el interbloqueo se produce cuando un conjunto de procesos desean entrar en sus secciones críticas, pero ninguno lo consigue. Con el bloqueo vital hay posibles secuencias de ejecución con éxito. Una solución correcta Hay que observar el estado de ambos procesos, que está dado por la variable señal, pero es necesario imponer orden en la actividad de los procesos para evitar el problema de “cortesía mutua”. La variable turno del primer intento puede usarse en esta labor, indicando que proceso tiene prioridad para exigir la entrada a su sección crítica. ALGORITMO DE PETERSON El algoritmo de Deker resuelve el problema de la exclusión mutua pero mediante un programa complejo, difícil de seguir y cuya corrección es difícil de demostrar. Peterson ha desarrollado una solución simple y elegante. Como antes, la variable global señal indica la posición de cada proceso con respecto a la exclusión mutua y la variable global turno resuelve los conflictos de simultaneidad. Considérese el proceso P0. Una vez que ha puesto señal[0] a cierto, P1 no puede entrar en su sección crítica. Si P1 esta aun en su sección crítica, entonces señal[1] = cierto y P0 está bloqueado en su bucle while. Esto significa que señal[1] es cierto y turno = 1. P0 puede entrar en su sección crítica cuando señal[1] se ponga a falso o cuando turno se ponga a 0. Considérense ahora los siguientes casos exhaustivos: P1 no está interesado en entrar en su sección crítica. Este caso es imposible porque implica que señal[1] = falso. P1 está esperando entrar en su sección crítica. Este caso es también imposible porque si turno = 1, P1 podría entrar en su sección crítica. P1 entra en su sección crítica varias veces y monopoliza el acceso a ella. Esto no puede pasar porque P1 está obligado a dar a P0 una oportunidad poniendo turno a 0 antes de cada intento de entrar en su sección crítica. Así pues, se tiene una solución posible al problema de la exclusión mutua para dos procesos. Es más, el algoritmo de Peterson se puede generalizar fácilmente al caso de n procesos. Disciplina de cola La disciplina de cola mas simple es la de primero en llegar/ primero en salir, pero ésta puede no ser suficiente si algunos mensajes son mas urgentes que otros. Una alternativa es permitir la especificación de prioridades de los mensajes, en función del tipo de mensaje o por designación del emisor. Otra alternativa es permitir al receptor examinar la cola de mensajes y seleccionar el mensaje a recibir a continuación. Exclusión mutua Supóngase que se usan primitivas receive bloqueantes y send no bloqueantes. Un conjunto de procesos concurrentes comparten un buzón, exmut, que puede ser usado por todos los procesos para enviar y recibir. El buzón contiene inicialmente un único mensaje, de contenido nulo. Un proceso que desea entrar en su sección crítica intenta primero recibir el mensaje. Si el buzón está vacío, el proceso se bloquea. Una vez que un proceso ha conseguido el mensaje, ejecuta su sección crítica y, después, devuelve el mensaje al buzón. De este modo, el mensaje funciona como testigo que se pasa de un proceso a otro. Esta técnica supone que si hay más de un proceso ejecutando la acción receive concurrentemente, entonces: Si hay un mensaje, se entrega sólo a uno de los procesos y los otros se bloquean. Si el buzón está vacío, todos los procesos se bloquean; cuando haya un mensaje disponible, sólo se activará y tomará el mensaje uno de los procesos bloqueados. EXCLUSIÓN MUTUA: SOLUCIONES POR HARDWARE INHABILITACIÓN DE INTERRUPCIONES En una máquina monoprocesador, la ejecución de procesos concurrentes no puede superponerse; los procesos solo pueden intercalarse. Es más, un proceso continuará ejecutándose hasta que solicite un servicio el sistema operativo o hasta que sea interrumpido. Por lo tanto, para garantizar la exclusión mutua, es suficiente con impedir que un proceso sea interrumpido. Esta capacidad puede ofrecerse en forma de primitivas definidas por el núcleo del sistema para habilitar o inhabilitar las interrupciones. Un proceso puede hacer cumplir la exclusión mutua del siguiente modo: While (cierto) { /*inhabilitar interrupciones */; /* sección critica */; /* habilitar interrupciones */; /* resto */; } Puesto que la sección crítica no puede ser interrumpida, la exclusión mutua está garantizada. Sin embargo, el precio de esta solución es alto. La eficiencia de la ejecución puede verse notablemente degradada debido a que se limita la capacidad del procesador para intercalar programas. Un segundo problema es que está técnica no funciona en arquitecturas de multiprocesador. Cuando el sistema tenga más de un procesador, es posible (y habitual) que haya más de un proceso ejecutándose al mismo tiempo. En este caso, inhabilitar las interrupciones no garantiza la exclusión mutua. INSTRUCCIONES ESPECIALES DE MAQUINA En configuraciones multiprocesador, varios procesadores comparten el acceso a una memoria principal común. En este caso, no hay relación maestro/esclavo, sino que los procesadores funcionan independientemente en una relación de igualdad. No hay un mecanismo de interrupciones entre los procesadores en el que se pueda basar la exclusión mutua. A nivel de hardware, como se ha mencionado, los accesos a posiciones de memoria excluyen cualquier otro acceso a la misma posición. Con esta base, los diseñadores han propuesto varias instrucciones de máquina que realizan dos acciones atómicamente, tales cono leer y escribir o leer y examinar, sobre una misma posición de memoria en un único ciclo de lectura de instrucción. Puesto que estas acciones se realizan en un único ciclo de instrucción, no están sujetas a injerencias por parte de otras instrucciones. -La instrucción COMPARAR Y FIJAR (TS, Test and Set)puede definirse de la siguiente forma: booleano TS(int i) { if (I==0) { I=1; return cierto; } else { return falso; } } La instrucción examina el valor de su argumento i. Si el valor es 0 , lo cambia por 1 y devuelve cierto. En otro caso, el valor no se modifica y se devuelve falso. La función Comparar y Fijar se ejecuta atómicamente en su totalidad, es decir, no esta sujeta a interrupciones. La instrucción INTERCAMBIAR se puede definir como sigue: void intercambiar (int registro, int memoria) { int temp; temp = memoria; memoria = registro; registro = temp; } Esta instrucción intercambia el contenido de un registro con el de una posición de memoria. Durante la ejecución de la instrucción, se bloquea el acceso a la posición de memoria de cualquier otra instrucción que haga referencia a la misma posición. Propiedades de las soluciones con instrucciones de maquina El uso de instrucciones especiales de la maquina para hacer cumplir la exclusión mutua tiene varias ventajas: Es aplicable a cualquier número de procesos en sistemas con memoria compartida, tanto de monoprocesador como de multiprocesador. Es simple y fácil de verificar. Puede usarse para disponer de varias secciones críticas; cada sección crítica puede definirse con su propia variable. Algunas desventajas importantes son las siguientes: SE EMPLEA ESPERA ACTIVA. Así pues, mientras un proceso está esperando para acceder a la sección crítica, continúa consumiendo tiempo del procesador. PUEDE PRODUCIRSE INANICIÓN. Cuando un proceso abandona la sección crítica y hay más de un proceso esperando, la selección es arbitraria. Así pues se podría denegar el acceso a algún proceso indefinidamente. PUEDE PRODUCIRSE INTERBLOQUEO. Supóngase la siguiente escena en un sistema monoprocesador. El proceso “P1″ ejecuta una instrucción especial (sea TS o Intercambiar) y entra su sección crítica. Se interrumpe a “P1″ para dar el procesador a “P2″, que tiene mayor prioridad. Si “P2″ intenta ahora usar el mismo recurso que “P1″, se le negará el acceso por el mecanismo de exclusión mutua. De este modo, “P2″ entrará en un bucle de espera activa. Sin embargo, “P1″ nunca será expedido porque su prioridad es menor que la del proceso listo “p2″. SEMÁFOROS Para solucionar problemas de procesos concurentes, se diseño un S.O. como un conjunto de procesos secuenciales, eficiente y fiables para dar soporte a la cooperación. Los procesos de usuario podrían utilizar estos mecanismos si el procesador y el S.O. los hacían disponible. El principio fundamental es el siguiente, 20+ procesos pueden cooperar por medio de simples señales, de manera que se pueda obligar a un proceso a detener en una posición determinada hasta que reciba una señal específica. Para la señalización se usan variables especiales llamadas semáforos “S”, los procesos ejecutan las primitivas wait(s) si la señal aun no se transmitió, el proceso se suspende hasta que tiene lugar la transmisión. A los semáforos se los contemplan como variables que tienen un N° entero sobre el que se definen las siguientes operaciones: un semáforo puede iniciarse con un valor negativo la operación wait disminuye el valor del semáforo. Si el valor no es positivo el proceso que ejecuta wait se bloquea. las operaciones signal incrementa el N° del semáforo. Si el valor es positivo se desbloquea el proceso bloqueado por una operación wait. No hay forma de examinar o manipular los semáforos aparte de estas tres operaciones. Las primitivas wait y signal se suponen atómicas, es decir no pueden ser interrumpidas y cada rutina puede considerarse como un peso indivisible. Un semáforo solo puede tomar los valores 0 y 1. Son más sencillos de implantar y pueden demostrarse que tienen la misma potencia de expresión que los semáforos generales. En ambos semáforos se emplean una cola para mantener los procesos en espera, la cuestión reside en el orden en que se retiran los procesos de la cola. La política utilizada el la de FIFO; el proceso que estuvo bloqueado durante mas tiempo se libera de la cola, se denomina semáforo robusto (incluye esta estrategia). Un semáforo débil no especifica el orden en que se retiran los procesos de la cola. Los semáforos robustos garantizan la inexistencia de inanición en el algoritmo de exclusión mutua, pero no así en los semáforos débiles, se supone que los semáforos son siempre robustos ya que son los más adecuados y porque son los tipos de semáforos que más incluyen los S.O. Implementación de los semáforos. Como se menciono anteriormente es impredecible que las operaciones wait y signal sean implementadas como primitivas atómicas. La esencia del problema del productor/consumidor, es la exclusion mutua: solo 1 proceso puede manipular un semáforo a la vez, en una operación wait o signal. Se pueden utilizar cualquier esquema de software con los algoritmos de Dekker o Peterson los que suponen una sobrecarga de procesos sustancial. Otra alternativa es usar uno de los esquemas de soporte del hardware p/la exclusion mutua.. En sistemas monoprocesador procesador, se pueden inhibir las interrupciones durante una operación wait o signal. MONITORES Los monitores son estructuras de un lenguaje de programación que ofrecen una funcionalidad equivalente a las de los semáforos pero son más fáciles de controlar. El concepto de monitor fue definido por primera vez en [HOAR 74] . La estructura de monitor se ha implementado en varios lenguajes de programación como: Pascal concurrente, Modulo-2, Java, etc. En concreto, para una lista enlazada se puede necesitar un cierre que bloquee todas las listas enlazadas o bien un cierre por cada elemento de una lista. Monitores con Señales: ( definición de Hoare ) Un monitor es un modulo de software que consta de uno o más procedimientos, una secuencia de inicio y uno datos locales. Sus características son las siguientes: Solo los procedimientos del monitor acceden a variables de datos locales. Un proceso entra en el monitor invocando a uno de sus procedimientos. En el monitor solo un proceso puede ser ejecutado en un momento dado; cualquier otro proceso quedara suspendido esperando la disponibilidad del monitor. Al ser un proceso por vez, el monitor puede ofrecer un servicio de exclusión mutua fácilmente. Así una estructura de datos puede protegerse situándola dentro de un monitor. Los monitores deben ofrecer herramientas de sincronización. Por ejemplo: si un proceso llama a un monitor y una vez dentro de él el proceso queda suspendido esperando alguna condición, hará falta un servicio que libere al monitor y lo deje disponible para el siguiente proceso. Mas tarde cuando la condición se cumpla el proceso suspendido podrá regresar al monitor y ejecutarse desde el momento de la suspensión. El monitor proporciona variables de condición que son accesibles solo desde dentro del monitor. Hay dos funciones para operar variables de condición: cwait ©: suspende la ejecución del proceso que llama bajo la condición “c”. El monitor está ahora disponible para otro proceso. csignal ©: retorna la ejecución de un proceso suspendido después de un cwait, bajo la misma condición. Si hay varios procesos elige uno de ellos. Si un proceso de monitor ejecuta un csignal y no hay tareas esperando entonces el csignal de pierde. Aunque un proceso puede entrar al monitor llamando a cualquiera de sus procedimientos, se puede decir que el monitor tiene un solo punto de acceso, custodiado para que solo un proceso este en el monitor en un instante dado. Si existen otros procesos tratando de entrar al monitor, estos se colocan en una cola de procesos suspendidos esperando la disponibilidad del monitor. Un proceso dentro de un monitor puede suspenderse a sí mismo, temporalmente, bajo la condición X ejecutando cwait(x), entonces se coloca en una cola de procesos que esperan que cambie la condición X entonces ejecuta un csignal(x) que avisa a la cola de condición correspondiente de que la condición a cambiado. En el código se puede apreciar la solución al problema de productor / consumidor usando monitores: El modulo monitor, buffers_acotado, controla el buffer para almacenar y retirar caracteres. El monitor incluye dos variables de condición: No-lleno es verdadero su hay lugar para agregar al menos un carácter. No-vació es verdadero si hay al menos un carácter en el buffer. Un productor solo puede agregar caracteres al buffer mediante el procedimiento añadir del monitor; el productor no tiene acceso directo al buffer. El procedimiento comprueba si hay espacio en el buffer, mediante la condición nolleno, si no lo hay el proceso queda suspendido y cualquier otro proceso (consumidor o productor) puede entrar al monitor. Luego, cuando el buffer ya no esta lleno, el proceso se retira de la cola y es reactivado. Luego de añadir un carácter el proceso activa la condición no-vació. La estructura misma del monitor garantiza la exclusión mutua (solo un proceso por vez puede acceder al buffer). Sin embargo, el programador debe situar correctamente las primitivas cwait( ) y csignal( ) en el monitor para evitar que los procesos depositen elementos en un buffer lleno o los extraigan de uno vació. Un proceso sale del monitor inmediatamente después de ejecutar csignal( ). Si csignal( ) se ejecuta antes del final entonces ese proceso libera el monitor y esta colocado en una lista de procesos suspendidos. Un nuevo proceso puede entrar el monitor. Si no hay procesos esperando en la condición X, la ejecución de csignal (x) no tiene efecto. Es posible cometer errores en la sincronización de los monitores, por ejemplo, si se omite cualquiera de las funciones csignal() en el monitor buffer _ acotado los procesos que entran en la cola de condición permanecen colgados permanentemente. Sin embargo la ventaja de los monitores es que todas las funciones de sincronización están incluidas dentro del monitor, lo que permite una fácil detección y corrección de fallas de sincronización. Monitores con Notificación y Difusión (definición de Lampson y Redell) Son varios los inconvenientes que presenta la solución de Hoare: -Si el proceso que ejecuta el csignal( ) no ha terminado en el monitor, se necesitaran dos cambios de procesos adicionales: uno para suspender el proceso y otro para reanudarlo. -La planificación de procesos asociados con las señales debe ser muy fiable. Si un proceso ejecuta un csignal ( ), el proceso de la cola de condición correspondiente debe activarse de inmediato, antes de que ingrese otro proceso del exterior o cambie la condición bajo la que se activó el proceso. Otro caso seria que un proceso productor escribe un carácter en el buffer y falla antes de dar la señal, entonces la cola de condición no-vacía se colgaría para siempre. Lampson y Redell desarrollaron una definición de monitores para el lenguaje MESA [Lamp 80]. La estructura de mesa reemplaza la primitiva csignal( ) por cnotify( ). Cuando un proceso ejecuta cnotify(x) envía una notificación a la cola de condición X, lo cual no significa que el proceso que esta ocupando el monitor vaya a detenerse, simplemente el cnotify(x) avisa al proceso de la cola de condición correspondiente de que será reanudado en un futuro cercano. Puesto que esta no garantiza que un proceso exterior entre al monitor, el proceso debe comprobar la condición nuevamente. En el código, la sentencia IF se reemplaza por un bucle While, lo cual genera una evaluación extra de la variable, pero sin embargo no hay cambios de procesos extra. Una modificación útil seria asociar un temporizador de guardia a cnotify( ) que permitiría que un proceso que ha esperado durante el intervalo máximo sea situado en estado de listo, independientemente de sí se ha notificado la condición. Con la norma de notificar a los procesos en lugar de reactivarlos, es posible añadir una primitiva de difusión cbroadcast. La difusión provoca que todos los procesos que están esperando por una condición se coloquen en el estado de listo. Esto es útil cuando un proceso no sabe cuantos procesos deben reactivarse. El método Lampson/Redell es menos propenso a errores debido a que cada procedimiento comprueba la condición luego de ser despertado, por medio de la instrucción while, un proceso puede realizar una señal o una difusión incorrectamente sin provocar un error en el programa que la recibe. Además este modelo presta un método mas modular de construcción de programas. Hay dos niveles de condición que deben satisfacerse para los procesos secuenciales cooperantes. Estructura de datos consistentes: significa que el monitor hace cumplir la exclusión mutua y concluye la operación de entrada o salida antes de permitir cualquier otra operación sobre el buffer. La misma condición del nivel 1 y, además disponer de suficiente memoria para que este proceso pueda completar su solicitud de asignación. PASO DE MENSAJES Son 2 los requisitos básicos que deben satisfacerse cuando los procesos interactúan entre si. Ellos son: La sincronización La comunicación Los procesos tienen que sincronizarse para cumplir la exclusión mutua, los procesos cooperantes pueden necesitar intercambiar información. El paso de mensajes es un método que permite que se realice ambas funciones. Este método tiene la ventaja de que es de fácil implementación en sistemas distribuidos y también es sistemas de multiprocesador y monoprocesador de memoria compartida. La funcionalidad real del paso de mensajes, generalmente, se da por medio de un par de primitivas: send(destino, mensaje) receive(origen, mensaje) Este es el conjunto mínimo de operaciones necesarias para que los procesos puedan dedicarse al paso de mensajes. SINCRONIZACION La comunicación de un mensaje entre 2 procesos implica cierto nivel de sincronización entre ambos. El receptor no puede recibir un mensaje hasta que sea enviado por otro proceso. Además hace falta especificar que le sucede a un proceso después de ejecutar una primitiva SEND o RECEIVE. Considérese en primer lugar la primitiva send. Cuando se ejecuta una primitiva send en un proceso, hay 2 posibilidades: o bien el proceso emisor se bloquea hasta que recibe el mensaje o no se bloquea. Igualmente cuando un proceso ejecuta una primitiva RECEIVE, existen 2 opciones: 1) Si previamente se ha enviado algún mensaje, este es recibido y continua la ejecución. 2) Si no hay ningún mensaje esperando entonces: a) el proceso se bloquea hasta que llega un mensaje o, b) el proceso continúa ejecutando, abandonando el intento de recepción. El emisor y el receptor pueden ser bloqueantes o no bloqueantes. Existen 3 tipos de combinaciones pero un sistema solo implementa uno o dos. I) Envío bloqueante, recepción bloqueante: tanto el emisor como el receptor se bloquean hasta que llega el mensaje; esta técnica se conoce como rendezvous. II) Envío no bloqueante, recepción bloqueante: aunque el emisor puede continuar, el receptor se bloquea hasta que llega el mensaje solicitado. Es la combinación más útil. III) Envío no bloqueante, recepción no bloqueante: nadie debe esperar. El send no bloqueante es la forma más natural para muchas tareas de programación concurrente. Un posible riesgo del send no bloquente es que por error puede llevar a una situación en la que el proceso genere mensajes repetidamente. Para el receive, la versión bloqueante es la mas natural para muchas tareas de programación concurrente. En general, un proceso que solicita un mensaje necesitara la información esperada antes de continuar. DIRECCIONAMIENTO Es necesario disponer de alguna forma de especificar en la primitiva send que proceso va a recibir el mensaje. La mayoría de las implementaciones permiten a los procesos receptores indicar el origen del mensaje que se va a recibir. Los distintos esquemas para hacer referencia a los procesos en las primitivas send y receive se encuadran dentro de 2 categorías: 1) Direccionamiento directo: la primitiva send incluye una identificación específica del proceso de destino. La primitiva receive se puede gestionar de 2 formas: Una posibilidad requiere que el proceso designe explícitamente un proceso emisor. El proceso debe conocer de antemano de que proceso espera un mensaje. En otros casos es imposible especificar el proceso de origen por anticipado. 2) Direccionamiento indirecto: los mensajes no se envían directamente del emisor al receptor, sino a una estructura de datos compartidos formada por colas, que pueden guardar los mensajes temporalmente, que se denominan BUZONES (mailboxes). Para que 2 procesos se comuniquen, uno envía mensajes al buzón apropiado y el otro los retira. Una ventaja de este tipo de direccionamiento es que se desacopla a emisor y receptor, asegurando mayor flexibilidad en el uso de mensajes. Relación entre emisores y receptores UNO A UNO: permite que se establezca un enlace privado entre 2 procesos. Aísla su interacción de injerencias erróneas de otros procesos. MUCHOS A UNO: resulta útil para interacciones cliente-servidor. En este caso el buzón se llama puerto. UNO A MUCHOS: permite un emisor y varios receptores. La asociación de procesos a buzones puede ser ESTATICA o DINAMICA. Los puertos suelen estar asociados estáticamente con algún proceso en particular. El puerto se crea y se asigna al proceso permanentemente. Una relación de UNO A UNO se define de forma estática y permanentemente. Cuando hay varios emisores, la asociación a un BUZON puede realizarse dinámicamente. Se pueden utilizar primitivas como CONECTAR o DESCONECTAR. Propiedad del buzón. en el caso de 1 puerto, normalmente pertenece y se crea por el RECPTOR. Entonces cuando se destruye el proceso, también se destruye el puerto. Para los buzones en general el S.O. ofrece un servicio de creación de buzones. Son considerados como propiedad del proceso creador en cuyo caso se destruyen junto con el proceso, o como propiedad del S.O., en este caso se necesita una orden explicita para destruir el buzón. FORMATO DE MENSAJES Para algunos S.O. los diseñadores han elegido mensajes cortos y tamaños fijos para minimizar procesamiento y coste de almacenamiento. Si se van a pasar una gran cantidad de datos, estos pueden ponerse en un archivo y el mensaje simplemente hará referencia a este archivo. Una solución más flexible es utilizar mensajes de longitud variable. DISCIPLINA DE COLA La disciplina de cola más simple es FIFO, pero esta puede no ser suficiente para mensajes más urgentes que otros. Una opción es habilitar la especificación de prioridades de los mensajes, en función del tipo de mensajes o por designación del emisor. Otra es permitir al receptor examinar la cola de mensajes y seleccionar el mensaje a recibir a continuación. EXCLUSION MUTUA Con el siguiente algoritmo de muestra una forma de usar el PASO DE MENSAJES para cumplir la exclusión mutua. Se usan RECEIVE bloqueantes y SEND no bloqueantes. Unos procesos concurrentes comparte un BUZON, EXMUT, que puede ser usado por todos los procesos. EXMUT (buzón) tiene inicialmente un único mensaje nulo. Un proceso que requiere entrar en su sección crítica intenta: 1) recibir el mensaje. Si el EXMUT(buzón) esta vacío, se bloquea el proceso. 2) Una vez que consiguió el mensaje, ejecuta su sección crítica y devuelve el mensaje al buzón. El mensaje funciona como un testigo(TOKEN) que se pasa de proceso a otro. Esta técnica supone que si hay más de un proceso ejecutando la acción RECEIVE concurrentemente, entonces: Si hay un mensaje, se entrega solo a uno de los procesos y los demás se bloquean. Si el buzón esta vacío, todos se bloquean cuando hay un mensaje, solo se activara y tomara el mensaje uno de los procesos bloqueados. PROBLEMA DE LOS LECTORES/ESCRITORES Descripción del problema: Existe un área de datos compartida entre una serie de procesos, algunos sólo leen los datos (lectores) y otros sólo escriben datos (escritores). El problema es satisfacer las siguientes condiciones: 1. Cualquier número de lectores puede leer el archivo simultáneamente. 2. En el archivo sólo puede escribir un escritor en cada instante. 3. Si un escritor está accediendo al archivo, ningún lector puede leerlo. El problema general de exclusión mutua, consiste en permitir a cualquiera de los procesos (lectores o escritores) leer o escribir en el área de datos. Esto hace necesario declarar cualquier acceso como una sección crítica donde los procesos tendrían que acceder uno a uno produciéndose intolerables retrasos, además se debe impedir que los escritores interfieran unos con otros y también que se realicen consultas mientras se llevan a cabo modificaciones Aplicar la solución general al problema de los lectores/escritores sería inaceptablemente lenta. En este caso más restrictivo es posible crear soluciones más eficientes. A continuación se examinan dos soluciones al problema: PRIORIDAD A LOS LECTORES Es una solución que utiliza semáforos para respetar la exclusión mutua. Se permite el acceso a varios lectores, pero mientras que un escritor está accediendo a los datos compartidos, no se permite acceder a ningún escritor o lector. El primer lector que intenta acceder debe esperar en el semáforo, Cuando haya al menos un lector, los lectores siguientes no necesitan esperar para entrar, se les da prioridad. El problema es que un escritor estará esperando mientras que haya al menos un lector leyendo, que luego podría dar paso a otro lector, en este caso el lector estará sujeto a inanición. Se utiliza una variable global contlect para mantener el número de lectores y el semáforo x para que la actualización de contlect sea consistente. El semáforo esem controla el acceso al recurso compartido. /* program lectores_escntores*/ int contlect; semáforo x = 1, esem=1; void lector() { while (cierto) { wait(x); contlect++; If (contlect==1) wait(esem); signal(x); LEER_UNIDAD(); wait(x); contlect—; If (contlect==0) signal(esem); signal(x); } } void escritor() { while (cierto) { wait(esem); ESCRIBIR_UNIDAD(); signal(esem); } } vold main() { contlect = 0; parbegln(tector, escritor); } Una solución al problema de los lectores/escritores por medio de semáforos; los lectores tienen prioridad. 2. PRIORIDAD A LOS ESCRITORES Esta solución garantiza que no se permitirá acceder a los datos a ningún nuevo lector una vez que, al menos, un escritor haya declarado su deseo de escribir. Se utilizan los mismos semáforos que en la solución anterior y se añaden otros más: • Un semáforo Isem que inhibe todas las lecturas cuando al menos un escritor desea acceder • Una variable contesc que controla la activación de Isem • Un semáforo y que controla la actualización de contesc • Un semáforo y que controla la actualización de contesc • Un semáforo z donde se encolan los lectores cuando ya hay uno en lsem /* program lectores_escritores */ Int contlec, contesc; semáforo x=1, y =1, z=1, esem=1, lsem=1; void lector() { while (cierto) { wait(z); wait(lsem); wait(x); contlect++; if (contlect = = 1) { wait(esem); } signal(x); signal(lsem); signal(z); LEER_UNIDAD(); wait(x); contlect—; if (contlect == 0) signal(esem); signal(x); } } void escritor() { while (cierto) { wait(y); contesc++; if (contesc = = 1) wait(lsem) ; signal(y); wait(esem); ESCRIBIR_UNIDAD(); signal(esem); wait(y); contesc—; If (contesc== 0) signal(lsem); signal(y); } } void mailn() { contlect = contesc = 0; parbegin (lector, escritor); } 2.7 Principios Generales de Concurrencia En un sistema multiprogramado con un único procesador, los procesos se intercalan en el tiempo aparentando una ejecución simultánea. Aunque no se logra un procesamiento paralelo y produce una sobrecarga en los intercambios de procesos, la ejecución intercalada produce beneficios en la eficiencia del procesamiento y en la estructuración de los programas. La intercalación y la superposición pueden contemplarse como ejemplos de procesamiento concurrente en un sistema monoprocesador, los problemas son consecuencia de la velocidad de ejecución de los procesos que no pueden predecirse y depende de las actividades de otros procesos, de la forma en que el sistema operativo trata las interrupciones surgen las siguientes dificultades: 1. Compartir recursos globales es riesgoso 2. Para el sistema operativo es difícil gestionar la asignación óptima de recursos. 2.8 Exclusión Mutua : solución por hardware y software Los algoritmos de exclusión mutua (comúnmente abreviada como mutex por mutual exclusion) se usan en programación concurrente para evitar que fragmentos de código conocidos como secciones críticas accedan al mismo tiempo a recursos que no deben ser compartidos. La mayor parte de estos recursos son las señales, contadores, colas y otros datos que se emplean en la comunicación entre el código que se ejecuta cuando se da servicio a una interrupción y el código que se ejecuta el resto del tiempo. Se trata de un problema de vital importancia porque, si no se toman las precauciones debidas, una interrupción puede ocurrir entre dos instrucciones cualesquiera del código normal y esto puede provocar graves fallos. La técnica que se emplea por lo común para conseguir la exclusión mutua es inhabilitar las interrupciones durante el conjunto de instrucciones más pequeño que impedirá la corrupción de la estructura compartida (la sección crítica). Esto impide que el código de la interrupción se ejecute en mitad de la sección crítica. En un sistema multiprocesador de memoria compartida, se usa la operación indivisible test-and-set sobre una bandera, para esperar hasta que el otro procesador la despeje. La operación test-and-set realiza ambas operaciones sin liberar el bus de memoria a otro procesador. Así, cuando el código deja la sección crítica, se despeja la bandera. Esto se conoce como spin lock o espera activa. Algunos sistemas tienen instrucciones multioperación indivisibles similares a las anteriormente descritas para manipular las listas enlazadas que se utilizan para las colas de eventos y otras estructuras de datos que los sistemas operativos usan comúnmente. La mayoría de los métodos de exclusión mutua clásicos intentan reducir la latencia y espera activa mediante las colas y cambios de contexto. Algunos investigadores afirman que las pruebas indican que estos algoritmos especiales pierden más tiempo del que ahorran. A pesar de todo lo dicho, muchas técnicas de exclusión mutua tienen efectos colaterales. Por ejemplo, los semáforos permiten interbloqueos (deadlocks) en los que un proceso obtiene un semáforo, otro proceso obtiene el semáforo y ambos se quedan a la espera de que el otro proceso libere el semáforo. Otros efectos comunes incluyen la inanición, en el cual un proceso esencial no se ejecuta durante el tiempo deseado, y la inversión de prioridades, en el que una tarea de prioridad elevada espera por otra tarea de menor prioridad, así como la latencia alta en la que la respuesta a las interrupciones no es inmediata. La mayor parte de la investigación actual en este campo, pretende eliminar los efectos anteriormente descritos. Si bien no hay un esquema perfecto conocido, hay un interesante esquema no clásico de envío de mensajes entre fragmentos de código que, aunque permite inversiones de prioridad y produce una mayor latencia, impide los interbloqueos. Algunos ejemplos de algoritmos clásicos de exclusión mutua son: El algoritmo de Dekker es un algoritmo de programación concurrente para exclusión mutua, que permite a dos procesos o hilos de ejecución compartir un recurso sin conflictos. Fue uno de los primeros algoritmos de exclusión mutua inventados, implementado por Edsger Dijkstra. Si ambos procesos intentan acceder a la sección crítica simultáneamente, el algoritmo elige un proceso según una variable turno. Si el otro proceso está ejecutando en su sección crítica, deberá esperar su finalización. Existen 5 versiones del algoritmo Dekker, teniendo ciertos fallos los primeros cuatro. La versión 5 es la que trabaja más eficientemente, siendo una combinación de la 1 y la 4. Versión 1: Alternancia Estricta. Garantiza la exclusión mutua, pero su desventaja es que acopla los procesos fuertemente, esto significa que los procesos lentos atrasan a los procesos rápidos. Versión 2: Problema interbloqueo. No existe la alternancia, aunque ambos procesos caen a un mismo estado y nunca salen de ahí. Versión 3: Colisión región critica no garantiza la exclusión mutua. Este algoritmo no evita que dos procesos puedan acceder al mismo tiempo a la región critica. Versión 4: Postergación indefinida. Aunque los procesos no están en interbloqueo, un proceso o varios se quedan esperando a que suceda un evento que tal vez nunca suceda. shared int cierto = 1; ''/* Definición de variables compartidas */ '' shared int bandera[2] = {0,0}; shared int turno = 0; while (cierto) { bandera[proc_id] = cierto; while (bandera[1-proc_id] == cierto) { if (turno == 1-proc_id) { bandera[proc_id] = 0; while (turno == (1-proc_id)) su turno de intentar */; bandera[proc_id] = 1; } /* ''Sección crítica'' */ turno = 1-proc_id; otro proceso */ bandera[proc_id] = 0; /* ''Sección no crítica'' */ } } /* espera a que sea /* es el turno del Algoritmo de Peterson El algoritmo de Peterson es un algoritmo de programación concurrente para exclusión mutua, que permite a dos o más procesos o hilos de ejecución compartir un recurso sin conflictos, utilizando sólo memoria compartida para la comunicación. Peterson desarrolló el primer algoritmo (1981) para dos procesos que fue una simplificación del algoritmo de Dekker para dos procesos. Posteriormente este algoritmo fue generalizado para que funcione para N procesos. Algoritmo para dos procesos bandera[0] = 0 bandera[1] = 0 turno = 0 p0: bandera[0] = 1 turno = 1 while( bandera[1] && turno == 1 ); turno == 0 ); //no hace nada. espera. //no hace nada. espera. // sección crítica // fin de la sección crítica crítica bandera[0] = 0 p1: bandera[1] = 1 turno = 0 while( bandera[0] && // sección crítica // fin de la sección bandera[1] = 0 2.9 Semaforos Sistemas Operativos Semáforos es un algoritmo de control de procesos, que tiene solo dos operaciones básicas, las cuales son: Wait.- Pregunta a los procesos si su contador es > ó = que cero, en caso de no ser así, los decrementa. El proceso que cambia en este caso a negativo (−1) desde la cola de procesos Listos a ser ejecutados es el que automáticamente toma el control del procesador. Signal.- A partir de un tiempo t definido por el despachador se ejecuta, y pregunta a los procesos si su contador es < que cero en caso de que sea afirmativa la respuesta, saca a este proceso de su ejecución y depende de su estado 2.10 Monitores Sistemas Operativos Un monitor encapsula el código relativo a un recurso compartido en un solo módulo de programa; ventajas: • mantenimiento más simple • menos errores de programación La interfaz del monitor es un conjunto de funciones que representan las diferentes operaciones que pueden hacerse con el recurso La implementación del monitor garantiza la exclusión mutua • mediante semáforos o algún otro mecanismo • o implícitamente en los lenguajes concurrentes 2.11 Paso de Mensajes Sistemas Operativos Paso de mensajes El paso de mensajes es una técnica empleada en programación concurrente para aportar sincronización entre procesos y permitir la exclusión mutua, de manera similar a como se hace con los semáforos, monitores, etc. Su principal característica es que no precisa de memoria compartida, por lo que es muy importante en la programación para sistemas distribuidos. Los elementos principales que intervienen en el paso de mensajes son el proceso que envía, el que recibe y el mensaje. 2.12 Concurrencia e Interbloqueo deadlock INTRODUCCIÓN Los temas fundamentales del diseño de sistemas operativos están relacionados con la gestión de procesos e hilos: • Multiprogramación: consiste en la gestión de varios procesos dentro de un sistema mono-procesador. • Multiprocesamiento: consiste en la gestión de varios procesos, dentro de un sistema multiprocesador. • Procesamiento distribuido: consiste en la gestión de varios procesos, ejecutándose en sistemas de computadores múltiples y distribuidos. La reciente proliferación de las agrupaciones es el principal ejemplo de este tipo de sistemas. La concurrencia es fundamental en todas estas áreas y para el diseño sistemas operativos. La concurrencia comprende un gran número de cuestiones de diseño, incluida la comunicación entre procesos, compartición y competencia por los recursos, sincronización de la ejecución de varios procesos y asignación del tiempo de procesador a los procesos. Se verá que estas cuestiones no solo surgen en entornos de multiprocesadores y proceso distribuido, sino incluso en sistemas multiprogramados con un solo procesador. La concurrencia puede presentarse en tres contextos diferentes: • Múltiples aplicaciones: la multiprogramación se creó para permitir que el tiempo de procesador de la máquina fuese compartido dinámicamente entre varias aplicaciones activas. • Aplicaciones estructuradas: como ampliación de los principios del diseño modular y la programación estructurada, algunas aplicaciones pueden implementarse eficazmente como un conjunto de procesos concurrentes. • Estructura del sistema operativo: las mismas ventajas de estructuración son aplicables a los programadores de sistemas y se ha comprobado que algunos sistemas operativos están implementados como un conjunto de procesos o hilos. PRINCIPIOS GENERALES DE LA CONCURRENCIA En un sistema multiprogramado con un único procesador, los procesos se intercalan en el tiempo aparentando una ejecución simultánea. Aunque no se logra un procesamiento paralelo y produce una sobrecarga en los intercambios de procesos, la ejecución intercalada produce beneficios en la eficiencia del procesamiento y en la estructuración de los programas. La intercalación y la superposición pueden contemplarse como ejemplos de procesamiento concurrente en un sistema monoprocesador, los problemas son consecuencia de la velocidad de ejecución de los procesos que no pueden predecirse y depende de las actividades de otros procesos, de la forma en que el sistema operativo trata las interrupciones surgen las siguientes dificultades: Compartir recursos globales es riesgoso Para el sistema operativo es difícil gestionar la asignación óptima de recursos. Las dificultades anteriores también se presentan en los sistemas multiprocesador. El hecho de compartir recursos ocasiona problemas, por esto es necesario proteger a dichos recursos. Los problemas de concurrencia se producen incluso cuando hay un único procesado LABORES DEL SISTEMA OPERATIVO Elementos de gestión y diseño que surgen por causa de la concurrencia: 1) El sistema operativo debe seguir a los distintos procesos activos 2) El sistema operativo debe asignar y retirar los distintos recursos a cada proceso activo, entre estos se incluyen: _Tiempo de procesador _Memoria _Archivos _Dispositivos de E/S 3) El sistema operativo debe proteger los datos y los recursos físicos de cada proceso contra injerencias no intencionadas de otros procesos. 4) Los resultados de un proceso deben ser independientes de la velocidad a la que se realiza la ejecución de otros procesos concurrentes. Para abordar la independencia de la velocidad debemos ver las formas en las que los procesos interactúan. INTERACCIÓN ENTRE PROCESOS Se puede clasificar los en que interactúan los procesos en función del nivel de conocimiento que cada proceso tiene de la existencia de los demás. Existen tres niveles de conocimiento: 1) Los procesos no tienen conocimiento de los demás: son procesos independientes que no operan juntos. Ej: la multiprogramación de procesos independientes. Aunque los procesos no trabajen juntos, el sistema operativo se encarga de la “competencia” por los recursos. 2) Los procesos tienen un conocimiento indirecto de los otros: los procesos no conocen a los otros por sus identificadores de proceso, pero muestran cooperación el objeto común. 3) Los procesos tienen conocimiento directo de los otros: los procesos se comunican por el identificador de proceso y pueden trabajar conjuntamente. Competencia entre procesos por los recursos Los procesos concurrentes entran en conflicto cuando compiten por el uso del mismo recurso; dos o más procesos necesitan acceder a un recurso durante su ejecución .Cada proceso debe dejar tal y como esté el estado del recurso que utilice. La ejecución de un proceso puede influir en el comportamiento de los procesos que compiten. Por Ej. Si dos procesos desean acceder a un recurso, el sistema operativo le asignará el recurso a uno y el otro tendrá que esperar. Cuando hay procesos en competencia, se deben solucionar tres problemas de control: la necesidad de exclusión mutua. Suponiendo que dos procesos quieren acceder a un recurso no compartible. A estos recursos se les llama “recursos críticos” y la parte del programa que los utiliza es la “sección crítica” del programa. Es importante que sólo un programa pueda acceder a su sección crítica en un momento dado. Hacer que se cumpla la exclusión mutua provoca un interbloqueo. Otro problema es la inanición si tres procesos necesitan acceder a un recurso, P1 posee al recurso, luego lo abandona y le concede el acceso al siguiente proceso P2, P1 solicita acceso de nuevo y el sistema operativo concede el acceso a P1 YP2 alternativamente, se puede negar indefinidamente a P3 el acceso al recurso. El control de competencia involucra al sistema operativo, porque es el que asigna los recursos. Cooperación entre procesos por compartimiento Comprende los procesos que interactúan con otros sin tener conocimiento explícito de ellos. Ej. : Varios procesos pueden tener acceso a variables compartidas. Los procesos deben cooperar para asegurar que los datos que se comparten se gestionan correctamente. Los mecanismos de control deben garantizar la integridad de los datos compartidos. Cooperación entre procesos por comunicación Los distintos procesos participan en una labor común que une a todos los procesos. La comunicación sincroniza o coordina las distintas actividades, está formada por mensajes de algún tipo. Las primitivas para enviar y recibir mensajes, vienen dadas como parte del lenguaje de programación o por el núcleo del sistema operativo REQUISITOS PARA LA EXCLUSIÓN MUTUA Sólo un proceso, de todos los que poseen secciones críticas por el mismo recurso compartido, debe tener permiso para entrar en ella en un momento dado. Un proceso que se interrumpe en una sección no crítica debe hacerlo sin interferir con los otros procesos. Un proceso no debe poder solicitar acceso a una sección crítica para después ser demorado indefinidamente, no puede permitirse el interbloqueo o la inanición. Si ningún proceso está en su sección crítica, cualquier proceso que solicite entrar en la suya debe poder hacerlo sin demora. No se debe suponer sobre la velocidad relativa de los procesos o el número de procesadores. Un proceso permanece en su sección crítica por un tiempo finito. Una manera de satisfacer los requisitos de exclusión mutua es dejar la responsabilidad a los procesos que deseen ejecutar concurrentemente. Tanto si son programas del sistema como de aplicación, los procesos deben coordinarse unos con otros para cumplir la exclusión mutua, sin ayuda del lenguaje de programación o del sistema operativo. Estos métodos se conocen como soluciones por software. EXCLUSIÓN MUTUA: SOLUCIONES POR SOFTWARE Pueden implementarse soluciones de software para los procesos concurrentes que se ejecuten en máquinas monoprocesador o multiprocesador con memoria principal compartida. ALGORITMO DE DEKKER La solución se desarrolla por etapas. Este método ilustra la mayoría de los errores habituales que se producen en la construcción de programas concurrentes. Primer intento Cualquier intento de exclusión mutua debe depender de algunos mecanismos básicos de exclusión en el hardware. El más habitual es que sólo se puede acceder a una posición de memoria en cada instante, teniendo en cuenta esto se reserva una posición de memoria global llamada turno. Un proceso que desea ejecutar su sección crítica primero evalúa el contenido de turno. Si el valor de turno es igual al número del proceso, el proceso puede continuar con su sección crítica. En otro caso el proceso debe esperar. El proceso en espera, lee repetitivamente el valor de turno hasta que puede entrar en su sección crítica. Este procedimiento se llama espera activa. Después de que un proceso accede a su sección crítica y termina con ella, debe actualizar el valor de turno para el otro proceso. Segundo intento: Cada proceso debe tener su propia llave de la sección crítica para que, si uno de ellos falla, pueda seguir accediendo a su sección crítica; para esto se define un vector booleano señal. Cada proceso puede evaluar el valor de señal del otro, pero no modificarlo. Cuando un proceso desea entrar en su sección crítica, comprueba la variable señal del otro hasta que tiene el valor falso (indica que el otro proceso no está en su sección crítica). Asigna a su propia señal el valor cierto y entra en su sección crítica. Cuando deja su sección crítica asigna falso a su señal. Si uno de los procesos falla fuera de la sección crítica, incluso el código para dar valor a las variables señal, el otro proceso no se queda bloqueado. El otro proceso puede entrar en su sección crítica tantas veces como quiera, porque la variable señal del otro proceso está siempre en falso. Pero si un proceso falla en su sección crítica o después de haber asignado cierto a su señal, el otro proceso estará bloqueado permanentemente. Tercer intento El segundo intento falla porque un proceso puede cambiar su estado después de que el otro proceso lo ha comprobado pero antes de que pueda entrar en su sección crítica. Si un proceso falla dentro de su sección crítica, incluso el código que da valor a la variable señal que controla el acceso a la sección crítica, el otro proceso se bloquea y si un proceso falla fuera de su sección crítica, el otro proceso no se bloquea. Si ambos procesos ponen sus variables señal a cierto antes de que ambos hayan ejecutado una sentencia, cada uno pensará que el otro ha entrado en su sección crítica, generando así un interbloqueo. Cuarto intento En el tercer intento, un proceso fijaba su estado sin conocer el estado del otro. Se puede arreglar esto haciendo que los procesos activen su señal para indicar que desean entrar en la sección crítica pero deben estar listos para desactivar la variable señal y ceder la preferencia al otro proceso. Existe una situación llamada bloqueo vital, esto no es un interbloqueo, porque cualquier cambio en la velocidad relativa de los procesos rompería este ciclo y permitiría a uno entrar en la sección crítica. Recordando que el interbloqueo se produce cuando un conjunto de procesos desean entrar en sus secciones críticas, pero ninguno lo consigue. Con el bloqueo vital hay posibles secuencias de ejecución con éxito. Una solución correcta Hay que observar el estado de ambos procesos, que está dado por la variable señal, pero es necesario imponer orden en la actividad de los procesos para evitar el problema de “cortesía mutua”. La variable turno del primer intento puede usarse en esta labor, indicando que proceso tiene prioridad para exigir la entrada a su sección crítica. ALGORITMO DE PETERSON El algoritmo de Deker resuelve el problema de la exclusión mutua pero mediante un programa complejo, difícil de seguir y cuya corrección es difícil de demostrar. Peterson ha desarrollado una solución simple y elegante. Como antes, la variable global señal indica la posición de cada proceso con respecto a la exclusión mutua y la variable global turno resuelve los conflictos de simultaneidad. Considérese el proceso P0. Una vez que ha puesto señal[0] a cierto, P1 no puede entrar en su sección crítica. Si P1 esta aun en su sección crítica, entonces señal[1] = cierto y P0 está bloqueado en su bucle while. Esto significa que señal[1] es cierto y turno = 1. P0 puede entrar en su sección crítica cuando señal[1] se ponga a falso o cuando turno se ponga a 0. Considérense ahora los siguientes casos exhaustivos: P1 no está interesado en entrar en su sección crítica. Este caso es imposible porque implica que señal[1] = falso. P1 está esperando entrar en su sección crítica. Este caso es también imposible porque si turno = 1, P1 podría entrar en su sección crítica. P1 entra en su sección crítica varias veces y monopoliza el acceso a ella. Esto no puede pasar porque P1 está obligado a dar a P0 una oportunidad poniendo turno a 0 antes de cada intento de entrar en su sección crítica. Así pues, se tiene una solución posible al problema de la exclusión mutua para dos procesos. Es más, el algoritmo de Peterson se puede generalizar fácilmente al caso de n procesos. Disciplina de cola La disciplina de cola mas simple es la de primero en llegar/ primero en salir, pero ésta puede no ser suficiente si algunos mensajes son mas urgentes que otros. Una alternativa es permitir la especificación de prioridades de los mensajes, en función del tipo de mensaje o por designación del emisor. Otra alternativa es permitir al receptor examinar la cola de mensajes y seleccionar el mensaje a recibir a continuación. Exclusión mutua Supóngase que se usan primitivas receive bloqueantes y send no bloqueantes. Un conjunto de procesos concurrentes comparten un buzón, exmut, que puede ser usado por todos los procesos para enviar y recibir. El buzón contiene inicialmente un único mensaje, de contenido nulo. Un proceso que desea entrar en su sección crítica intenta primero recibir el mensaje. Si el buzón está vacío, el proceso se bloquea. Una vez que un proceso ha conseguido el mensaje, ejecuta su sección crítica y, después, devuelve el mensaje al buzón. De este modo, el mensaje funciona como testigo que se pasa de un proceso a otro. Esta técnica supone que si hay más de un proceso ejecutando la acción receive concurrentemente, entonces: Si hay un mensaje, se entrega sólo a uno de los procesos y los otros se bloquean. Si el buzón está vacío, todos los procesos se bloquean; cuando haya un mensaje disponible, sólo se activará y tomará el mensaje uno de los procesos bloqueados. EXCLUSIÓN MUTUA: SOLUCIONES POR HARDWARE INHABILITACIÓN DE INTERRUPCIONES En una máquina monoprocesador, la ejecución de procesos concurrentes no puede superponerse; los procesos solo pueden intercalarse. Es más, un proceso continuará ejecutándose hasta que solicite un servicio el sistema operativo o hasta que sea interrumpido. Por lo tanto, para garantizar la exclusión mutua, es suficiente con impedir que un proceso sea interrumpido. Esta capacidad puede ofrecerse en forma de primitivas definidas por el núcleo del sistema para habilitar o inhabilitar las interrupciones. Un proceso puede hacer cumplir la exclusión mutua del siguiente modo: While (cierto) { /*inhabilitar interrupciones */; /* sección critica */; /* habilitar interrupciones */; /* resto */; } Puesto que la sección crítica no puede ser interrumpida, la exclusión mutua está garantizada. Sin embargo, el precio de esta solución es alto. La eficiencia de la ejecución puede verse notablemente degradada debido a que se limita la capacidad del procesador para intercalar programas. Un segundo problema es que está técnica no funciona en arquitecturas de multiprocesador. Cuando el sistema tenga más de un procesador, es posible (y habitual) que haya más de un proceso ejecutándose al mismo tiempo. En este caso, inhabilitar las interrupciones no garantiza la exclusión mutua. INSTRUCCIONES ESPECIALES DE MAQUINA En configuraciones multiprocesador, varios procesadores comparten el acceso a una memoria principal común. En este caso, no hay relación maestro/esclavo, sino que los procesadores funcionan independientemente en una relación de igualdad. No hay un mecanismo de interrupciones entre los procesadores en el que se pueda basar la exclusión mutua. A nivel de hardware, como se ha mencionado, los accesos a posiciones de memoria excluyen cualquier otro acceso a la misma posición. Con esta base, los diseñadores han propuesto varias instrucciones de máquina que realizan dos acciones atómicamente, tales cono leer y escribir o leer y examinar, sobre una misma posición de memoria en un único ciclo de lectura de instrucción. Puesto que estas acciones se realizan en un único ciclo de instrucción, no están sujetas a injerencias por parte de otras instrucciones. -La instrucción COMPARAR Y FIJAR (TS, Test and Set)puede definirse de la siguiente forma: booleano TS(int i) { if (I==0) { I=1; return cierto; } else { return falso; } } La instrucción examina el valor de su argumento i. Si el valor es 0 , lo cambia por 1 y devuelve cierto. En otro caso, el valor no se modifica y se devuelve falso. La función Comparar y Fijar se ejecuta atómicamente en su totalidad, es decir, no esta sujeta a interrupciones. La instrucción INTERCAMBIAR se puede definir como sigue: void intercambiar (int registro, int memoria) { int temp; temp = memoria; memoria = registro; registro = temp; } Esta instrucción intercambia el contenido de un registro con el de una posición de memoria. Durante la ejecución de la instrucción, se bloquea el acceso a la posición de memoria de cualquier otra instrucción que haga referencia a la misma posición. Propiedades de las soluciones con instrucciones de maquina El uso de instrucciones especiales de la maquina para hacer cumplir la exclusión mutua tiene varias ventajas: Es aplicable a cualquier número de procesos en sistemas con memoria compartida, tanto de monoprocesador como de multiprocesador. Es simple y fácil de verificar. Puede usarse para disponer de varias secciones críticas; cada sección crítica puede definirse con su propia variable. Algunas desventajas importantes son las siguientes: SE EMPLEA ESPERA ACTIVA. Así pues, mientras un proceso está esperando para acceder a la sección crítica, continúa consumiendo tiempo del procesador. PUEDE PRODUCIRSE INANICIÓN. Cuando un proceso abandona la sección crítica y hay más de un proceso esperando, la selección es arbitraria. Así pues se podría denegar el acceso a algún proceso indefinidamente. PUEDE PRODUCIRSE INTERBLOQUEO. Supóngase la siguiente escena en un sistema monoprocesador. El proceso “P1″ ejecuta una instrucción especial (sea TS o Intercambiar) y entra su sección crítica. Se interrumpe a “P1″ para dar el procesador a “P2″, que tiene mayor prioridad. Si “P2″ intenta ahora usar el mismo recurso que “P1″, se le negará el acceso por el mecanismo de exclusión mutua. De este modo, “P2″ entrará en un bucle de espera activa. Sin embargo, “P1″ nunca será expedido porque su prioridad es menor que la del proceso listo “p2″. SEMÁFOROS Para solucionar problemas de procesos concurentes, se diseño un S.O. como un conjunto de procesos secuenciales, eficiente y fiables para dar soporte a la cooperación. Los procesos de usuario podrían utilizar estos mecanismos si el procesador y el S.O. los hacían disponible. El principio fundamental es el siguiente, 20+ procesos pueden cooperar por medio de simples señales, de manera que se pueda obligar a un proceso a detener en una posición determinada hasta que reciba una señal específica. Para la señalización se usan variables especiales llamadas semáforos “S”, los procesos ejecutan las primitivas wait(s) si la señal aun no se transmitió, el proceso se suspende hasta que tiene lugar la transmisión. A los semáforos se los contemplan como variables que tienen un N° entero sobre el que se definen las siguientes operaciones: un semáforo puede iniciarse con un valor negativo la operación wait disminuye el valor del semáforo. Si el valor no es positivo el proceso que ejecuta wait se bloquea. las operaciones signal incrementa el N° del semáforo. Si el valor es positivo se desbloquea el proceso bloqueado por una operación wait. No hay forma de examinar o manipular los semáforos aparte de estas tres operaciones. Las primitivas wait y signal se suponen atómicas, es decir no pueden ser interrumpidas y cada rutina puede considerarse como un peso indivisible. Un semáforo solo puede tomar los valores 0 y 1. Son más sencillos de implantar y pueden demostrarse que tienen la misma potencia de expresión que los semáforos generales. En ambos semáforos se emplean una cola para mantener los procesos en espera, la cuestión reside en el orden en que se retiran los procesos de la cola. La política utilizada el la de FIFO; el proceso que estuvo bloqueado durante mas tiempo se libera de la cola, se denomina semáforo robusto (incluye esta estrategia). Un semáforo débil no especifica el orden en que se retiran los procesos de la cola. Los semáforos robustos garantizan la inexistencia de inanición en el algoritmo de exclusión mutua, pero no así en los semáforos débiles, se supone que los semáforos son siempre robustos ya que son los más adecuados y porque son los tipos de semáforos que más incluyen los S.O. Implementación de los semáforos. Como se menciono anteriormente es impredecible que las operaciones wait y signal sean implementadas como primitivas atómicas. La esencia del problema del productor/consumidor, es la exclusion mutua: solo 1 proceso puede manipular un semáforo a la vez, en una operación wait o signal. Se pueden utilizar cualquier esquema de software con los algoritmos de Dekker o Peterson los que suponen una sobrecarga de procesos sustancial. Otra alternativa es usar uno de los esquemas de soporte del hardware p/la exclusion mutua.. En sistemas monoprocesador procesador, se pueden inhibir las interrupciones durante una operación wait o signal. MONITORES Los monitores son estructuras de un lenguaje de programación que ofrecen una funcionalidad equivalente a las de los semáforos pero son más fáciles de controlar. El concepto de monitor fue definido por primera vez en [HOAR 74] . La estructura de monitor se ha implementado en varios lenguajes de programación como: Pascal concurrente, Modulo-2, Java, etc. En concreto, para una lista enlazada se puede necesitar un cierre que bloquee todas las listas enlazadas o bien un cierre por cada elemento de una lista. Monitores con Señales: ( definición de Hoare ) Un monitor es un modulo de software que consta de uno o más procedimientos, una secuencia de inicio y uno datos locales. Sus características son las siguientes: Solo los procedimientos del monitor acceden a variables de datos locales. Un proceso entra en el monitor invocando a uno de sus procedimientos. En el monitor solo un proceso puede ser ejecutado en un momento dado; cualquier otro proceso quedara suspendido esperando la disponibilidad del monitor. Al ser un proceso por vez, el monitor puede ofrecer un servicio de exclusión mutua fácilmente. Así una estructura de datos puede protegerse situándola dentro de un monitor. Los monitores deben ofrecer herramientas de sincronización. Por ejemplo: si un proceso llama a un monitor y una vez dentro de él el proceso queda suspendido esperando alguna condición, hará falta un servicio que libere al monitor y lo deje disponible para el siguiente proceso. Mas tarde cuando la condición se cumpla el proceso suspendido podrá regresar al monitor y ejecutarse desde el momento de la suspensión. El monitor proporciona variables de condición que son accesibles solo desde dentro del monitor. Hay dos funciones para operar variables de condición: cwait ©: suspende la ejecución del proceso que llama bajo la condición “c”. El monitor está ahora disponible para otro proceso. csignal ©: retorna la ejecución de un proceso suspendido después de un cwait, bajo la misma condición. Si hay varios procesos elige uno de ellos. Si un proceso de monitor ejecuta un csignal y no hay tareas esperando entonces el csignal de pierde. Aunque un proceso puede entrar al monitor llamando a cualquiera de sus procedimientos, se puede decir que el monitor tiene un solo punto de acceso, custodiado para que solo un proceso este en el monitor en un instante dado. Si existen otros procesos tratando de entrar al monitor, estos se colocan en una cola de procesos suspendidos esperando la disponibilidad del monitor. Un proceso dentro de un monitor puede suspenderse a sí mismo, temporalmente, bajo la condición X ejecutando cwait(x), entonces se coloca en una cola de procesos que esperan que cambie la condición X entonces ejecuta un csignal(x) que avisa a la cola de condición correspondiente de que la condición a cambiado. En el código se puede apreciar la solución al problema de productor / consumidor usando monitores: El modulo monitor, buffers_acotado, controla el buffer para almacenar y retirar caracteres. El monitor incluye dos variables de condición: No-lleno es verdadero su hay lugar para agregar al menos un carácter. No-vació es verdadero si hay al menos un carácter en el buffer. Un productor solo puede agregar caracteres al buffer mediante el procedimiento añadir del monitor; el productor no tiene acceso directo al buffer. El procedimiento comprueba si hay espacio en el buffer, mediante la condición nolleno, si no lo hay el proceso queda suspendido y cualquier otro proceso (consumidor o productor) puede entrar al monitor. Luego, cuando el buffer ya no esta lleno, el proceso se retira de la cola y es reactivado. Luego de añadir un carácter el proceso activa la condición no-vació. La estructura misma del monitor garantiza la exclusión mutua (solo un proceso por vez puede acceder al buffer). Sin embargo, el programador debe situar correctamente las primitivas cwait( ) y csignal( ) en el monitor para evitar que los procesos depositen elementos en un buffer lleno o los extraigan de uno vació. Un proceso sale del monitor inmediatamente después de ejecutar csignal( ). Si csignal( ) se ejecuta antes del final entonces ese proceso libera el monitor y esta colocado en una lista de procesos suspendidos. Un nuevo proceso puede entrar el monitor. Si no hay procesos esperando en la condición X, la ejecución de csignal (x) no tiene efecto. Es posible cometer errores en la sincronización de los monitores, por ejemplo, si se omite cualquiera de las funciones csignal() en el monitor buffer _ acotado los procesos que entran en la cola de condición permanecen colgados permanentemente. Sin embargo la ventaja de los monitores es que todas las funciones de sincronización están incluidas dentro del monitor, lo que permite una fácil detección y corrección de fallas de sincronización. Monitores con Notificación y Difusión (definición de Lampson y Redell) Son varios los inconvenientes que presenta la solución de Hoare: -Si el proceso que ejecuta el csignal( ) no ha terminado en el monitor, se necesitaran dos cambios de procesos adicionales: uno para suspender el proceso y otro para reanudarlo. -La planificación de procesos asociados con las señales debe ser muy fiable. Si un proceso ejecuta un csignal ( ), el proceso de la cola de condición correspondiente debe activarse de inmediato, antes de que ingrese otro proceso del exterior o cambie la condición bajo la que se activó el proceso. Otro caso seria que un proceso productor escribe un carácter en el buffer y falla antes de dar la señal, entonces la cola de condición no-vacía se colgaría para siempre. Lampson y Redell desarrollaron una definición de monitores para el lenguaje MESA [Lamp 80]. La estructura de mesa reemplaza la primitiva csignal( ) por cnotify( ). Cuando un proceso ejecuta cnotify(x) envía una notificación a la cola de condición X, lo cual no significa que el proceso que esta ocupando el monitor vaya a detenerse, simplemente el cnotify(x) avisa al proceso de la cola de condición correspondiente de que será reanudado en un futuro cercano. Puesto que esta no garantiza que un proceso exterior entre al monitor, el proceso debe comprobar la condición nuevamente. En el código, la sentencia IF se reemplaza por un bucle While, lo cual genera una evaluación extra de la variable, pero sin embargo no hay cambios de procesos extra. Una modificación útil seria asociar un temporizador de guardia a cnotify( ) que permitiría que un proceso que ha esperado durante el intervalo máximo sea situado en estado de listo, independientemente de sí se ha notificado la condición. Con la norma de notificar a los procesos en lugar de reactivarlos, es posible añadir una primitiva de difusión cbroadcast. La difusión provoca que todos los procesos que están esperando por una condición se coloquen en el estado de listo. Esto es útil cuando un proceso no sabe cuantos procesos deben reactivarse. El método Lampson/Redell es menos propenso a errores debido a que cada procedimiento comprueba la condición luego de ser despertado, por medio de la instrucción while, un proceso puede realizar una señal o una difusión incorrectamente sin provocar un error en el programa que la recibe. Además este modelo presta un método mas modular de construcción de programas. Hay dos niveles de condición que deben satisfacerse para los procesos secuenciales cooperantes. Estructura de datos consistentes: significa que el monitor hace cumplir la exclusión mutua y concluye la operación de entrada o salida antes de permitir cualquier otra operación sobre el buffer. La misma condición del nivel 1 y, además disponer de suficiente memoria para que este proceso pueda completar su solicitud de asignación. PASO DE MENSAJES Son 2 los requisitos básicos que deben satisfacerse cuando los procesos interactúan entre si. Ellos son: La sincronización La comunicación Los procesos tienen que sincronizarse para cumplir la exclusión mutua, los procesos cooperantes pueden necesitar intercambiar información. El paso de mensajes es un método que permite que se realice ambas funciones. Este método tiene la ventaja de que es de fácil implementación en sistemas distribuidos y también es sistemas de multiprocesador y monoprocesador de memoria compartida. La funcionalidad real del paso de mensajes, generalmente, se da por medio de un par de primitivas: send(destino, mensaje) receive(origen, mensaje) Este es el conjunto mínimo de operaciones necesarias para que los procesos puedan dedicarse al paso de mensajes. SINCRONIZACION La comunicación de un mensaje entre 2 procesos implica cierto nivel de sincronización entre ambos. El receptor no puede recibir un mensaje hasta que sea enviado por otro proceso. Además hace falta especificar que le sucede a un proceso después de ejecutar una primitiva SEND o RECEIVE. Considérese en primer lugar la primitiva send. Cuando se ejecuta una primitiva send en un proceso, hay 2 posibilidades: o bien el proceso emisor se bloquea hasta que recibe el mensaje o no se bloquea. Igualmente cuando un proceso ejecuta una primitiva RECEIVE, existen 2 opciones: 1) Si previamente se ha enviado algún mensaje, este es recibido y continua la ejecución. 2) Si no hay ningún mensaje esperando entonces: a) el proceso se bloquea hasta que llega un mensaje o, b) el proceso continúa ejecutando, abandonando el intento de recepción. El emisor y el receptor pueden ser bloqueantes o no bloqueantes. Existen 3 tipos de combinaciones pero un sistema solo implementa uno o dos. I) Envío bloqueante, recepción bloqueante: tanto el emisor como el receptor se bloquean hasta que llega el mensaje; esta técnica se conoce como rendezvous. II) Envío no bloqueante, recepción bloqueante: aunque el emisor puede continuar, el receptor se bloquea hasta que llega el mensaje solicitado. Es la combinación más útil. III) Envío no bloqueante, recepción no bloqueante: nadie debe esperar. El send no bloqueante es la forma más natural para muchas tareas de programación concurrente. Un posible riesgo del send no bloquente es que por error puede llevar a una situación en la que el proceso genere mensajes repetidamente. Para el receive, la versión bloqueante es la mas natural para muchas tareas de programación concurrente. En general, un proceso que solicita un mensaje necesitara la información esperada antes de continuar. DIRECCIONAMIENTO Es necesario disponer de alguna forma de especificar en la primitiva send que proceso va a recibir el mensaje. La mayoría de las implementaciones permiten a los procesos receptores indicar el origen del mensaje que se va a recibir. Los distintos esquemas para hacer referencia a los procesos en las primitivas send y receive se encuadran dentro de 2 categorías: 1) Direccionamiento directo: la primitiva send incluye una identificación específica del proceso de destino. La primitiva receive se puede gestionar de 2 formas: Una posibilidad requiere que el proceso designe explícitamente un proceso emisor. El proceso debe conocer de antemano de que proceso espera un mensaje. En otros casos es imposible especificar el proceso de origen por anticipado. 2) Direccionamiento indirecto: los mensajes no se envían directamente del emisor al receptor, sino a una estructura de datos compartidos formada por colas, que pueden guardar los mensajes temporalmente, que se denominan BUZONES (mailboxes). Para que 2 procesos se comuniquen, uno envía mensajes al buzón apropiado y el otro los retira. Una ventaja de este tipo de direccionamiento es que se desacopla a emisor y receptor, asegurando mayor flexibilidad en el uso de mensajes. Relación entre emisores y receptores UNO A UNO: permite que se establezca un enlace privado entre 2 procesos. Aísla su interacción de injerencias erróneas de otros procesos. MUCHOS A UNO: resulta útil para interacciones cliente-servidor. En este caso el buzón se llama puerto. UNO A MUCHOS: permite un emisor y varios receptores. La asociación de procesos a buzones puede ser ESTATICA o DINAMICA. Los puertos suelen estar asociados estáticamente con algún proceso en particular. El puerto se crea y se asigna al proceso permanentemente. Una relación de UNO A UNO se define de forma estática y permanentemente. Cuando hay varios emisores, la asociación a un BUZON puede realizarse dinámicamente. Se pueden utilizar primitivas como CONECTAR o DESCONECTAR. Propiedad del buzón. en el caso de 1 puerto, normalmente pertenece y se crea por el RECPTOR. Entonces cuando se destruye el proceso, también se destruye el puerto. Para los buzones en general el S.O. ofrece un servicio de creación de buzones. Son considerados como propiedad del proceso creador en cuyo caso se destruyen junto con el proceso, o como propiedad del S.O., en este caso se necesita una orden explicita para destruir el buzón. FORMATO DE MENSAJES Para algunos S.O. los diseñadores han elegido mensajes cortos y tamaños fijos para minimizar procesamiento y coste de almacenamiento. Si se van a pasar una gran cantidad de datos, estos pueden ponerse en un archivo y el mensaje simplemente hará referencia a este archivo. Una solución más flexible es utilizar mensajes de longitud variable. DISCIPLINA DE COLA La disciplina de cola más simple es FIFO, pero esta puede no ser suficiente para mensajes más urgentes que otros. Una opción es habilitar la especificación de prioridades de los mensajes, en función del tipo de mensajes o por designación del emisor. Otra es permitir al receptor examinar la cola de mensajes y seleccionar el mensaje a recibir a continuación. EXCLUSION MUTUA Con el siguiente algoritmo de muestra una forma de usar el PASO DE MENSAJES para cumplir la exclusión mutua. Se usan RECEIVE bloqueantes y SEND no bloqueantes. Unos procesos concurrentes comparte un BUZON, EXMUT, que puede ser usado por todos los procesos. EXMUT (buzón) tiene inicialmente un único mensaje nulo. Un proceso que requiere entrar en su sección crítica intenta: 1) recibir el mensaje. Si el EXMUT(buzón) esta vacío, se bloquea el proceso. 2) Una vez que consiguió el mensaje, ejecuta su sección crítica y devuelve el mensaje al buzón. El mensaje funciona como un testigo(TOKEN) que se pasa de proceso a otro. Esta técnica supone que si hay más de un proceso ejecutando la acción RECEIVE concurrentemente, entonces: Si hay un mensaje, se entrega solo a uno de los procesos y los demás se bloquean. Si el buzón esta vacío, todos se bloquean cuando hay un mensaje, solo se activara y tomara el mensaje uno de los procesos bloqueados. PROBLEMA DE LOS LECTORES/ESCRITORES Descripción del problema: Existe un área de datos compartida entre una serie de procesos, algunos sólo leen los datos (lectores) y otros sólo escriben datos (escritores). El problema es satisfacer las siguientes condiciones: 1. Cualquier número de lectores puede leer el archivo simultáneamente. 2. En el archivo sólo puede escribir un escritor en cada instante. 3. Si un escritor está accediendo al archivo, ningún lector puede leerlo. El problema general de exclusión mutua, consiste en permitir a cualquiera de los procesos (lectores o escritores) leer o escribir en el área de datos. Esto hace necesario declarar cualquier acceso como una sección crítica donde los procesos tendrían que acceder uno a uno produciéndose intolerables retrasos, además se debe impedir que los escritores interfieran unos con otros y también que se realicen consultas mientras se llevan a cabo modificaciones Aplicar la solución general al problema de los lectores/escritores sería inaceptablemente lenta. En este caso más restrictivo es posible crear soluciones más eficientes. A continuación se examinan dos soluciones al problema: PRIORIDAD A LOS LECTORES Es una solución que utiliza semáforos para respetar la exclusión mutua. Se permite el acceso a varios lectores, pero mientras que un escritor está accediendo a los datos compartidos, no se permite acceder a ningún escritor o lector. El primer lector que intenta acceder debe esperar en el semáforo, Cuando haya al menos un lector, los lectores siguientes no necesitan esperar para entrar, se les da prioridad. El problema es que un escritor estará esperando mientras que haya al menos un lector leyendo, que luego podría dar paso a otro lector, en este caso el lector estará sujeto a inanición. Se utiliza una variable global contlect para mantener el número de lectores y el semáforo x para que la actualización de contlect sea consistente. El semáforo esem controla el acceso al recurso compartido. /* program lectores_escntores*/ int contlect; semáforo x = 1, esem=1; void lector() { while (cierto) { wait(x); contlect++; If (contlect==1) wait(esem); signal(x); LEER_UNIDAD(); wait(x); contlect—; If (contlect==0) signal(esem); signal(x); } } void escritor() { while (cierto) { wait(esem); ESCRIBIR_UNIDAD(); signal(esem); } } vold main() { contlect = 0; parbegln(tector, escritor); } Una solución al problema de los lectores/escritores por medio de semáforos; los lectores tienen prioridad. 2. PRIORIDAD A LOS ESCRITORES Esta solución garantiza que no se permitirá acceder a los datos a ningún nuevo lector una vez que, al menos, un escritor haya declarado su deseo de escribir. Se utilizan los mismos semáforos que en la solución anterior y se añaden otros más: • Un semáforo Isem que inhibe todas las lecturas cuando al menos un escritor desea acceder • Una variable contesc que controla la activación de Isem • Un semáforo y que controla la actualización de contesc • Un semáforo y que controla la actualización de contesc • Un semáforo z donde se encolan los lectores cuando ya hay uno en lsem /* program lectores_escritores */ Int contlec, contesc; semáforo x=1, y =1, z=1, esem=1, lsem=1; void lector() { while (cierto) { wait(z); wait(lsem); wait(x); contlect++; if (contlect = = 1) { wait(esem); } signal(x); signal(lsem); signal(z); LEER_UNIDAD(); wait(x); contlect—; if (contlect == 0) signal(esem); signal(x); } } void escritor() { while (cierto) { wait(y); contesc++; if (contesc = = 1) wait(lsem) ; signal(y); wait(esem); ESCRIBIR_UNIDAD(); signal(esem); wait(y); contesc—; If (contesc== 0) signal(lsem); signal(y); } } void mailn() { contlect = contesc = 0; parbegin (lector, escritor); } 2.13 Principios del Interbloqueo 2.14 Acciones Realizar en Interbloqueo prevención, detección, predicción y evitar CONDICIONES PARA PRODUCIR INTERBLOQUEO En la política del sistema operativo, deben darse tres condiciones para que pueda producirse un interbloqueo: 1- Condición de exclusión mutua: Cada recurso esta asignado a un único proceso o esta disponible. 2- Condición de posesión y espera: Los procesos que tienen, en un momento dado, recursos asignados con anterioridad, pueden solicitar nuevos recursos. 3- Condición de no apropiación: Los recursos otorgados con anterioridad no pueden ser forzados a dejar un proceso. El proceso que los posee debe liberarlos en forma explicita. En la mayoría de los casos, estas condiciones son bastantes necesarias. La exclusión mutua hace falta para asegurar la consistencia de resultados y la integridad de la base de datos. De forma similar, la apropiación no se puede aplicar arbitrariamente y, cuando se encuentran involucrados recursos de datos, debe estar acompañada de un mecanismo de recuperación y reanulación, que devuelva un proceso y sus recursos a un estado previo adecuado, desde el que el proceso puede finalmente repetir sus acciones. Puede no existir interbloqueo con solo estas tres condiciones. Para que se produzca interbloqueo, se necesita una cuarta condición: 4- Condición de espera circular (o circulo vicioso de espera): Debe existir una cadena circular de dos o mas procesos, cada uno de los cuales espera un recurso poseído por el siguiente miembro de la cadena. Las tres primeras condiciones son necesarias, pero no suficientes, para que exista interbloqueo. La cuarta condición es, en realidad, una consecuencia potencial de las tres primeras. Es decir, dado que se producen las tres primeras condiciones, puede ocurrir una secuencia de eventos que desemboque en un circulo vicioso de espera irresoluble. El circulo de espera de la condición 4 es irresoluble porque se mantienen las tres primeras condiciones. Las cuatro condiciones en conjunto constituyen una condición necesaria y suficiente para el interbloqueo. PREVENCIÓN DEL INTERBLOQUEO La estrategia básica de la prevención del interbloqueo consiste, a grandes rasgos, en diseñar su sistema de manera que esté excluida, a priori, la posibilidad de interbloqueo. Los métodos para prevenir el interbloqueo son de dos tipos: - Los métodos indirectos que consisten en impedir la aparición de alguna de las tres condiciones necesarias para que se de el interbloqeo. - Los métodos directos que consisten en evitar la aparición del circulo vicioso de espera. PREDICCIÓN DEL INTERBLOQUEO Una forma de resolver el problema del interbloqueo, que se diferencia sutilmente de la prevención, es la predicción del interbloqueo. En la prevención de interbloqueo, se obligaba a las solicitudes de recursos a impedir que sucediera , por lo menos, alguna de las cuatro condiciones de interbloqueo. Esto se hace indirectamente, impidiendo la aparición de una de las tres condiciones necesarias (exclusión mutua, retención y espera, no apropiación) o directamente, impidiendo la aparición de un circulo viciosos de espera. Se llega así a un uso ineficiente de los recursos y una ejecución ineficiente de los procesos. Con predicción del interbloqueo, por otro lado, se pueden alcanzar las tres condiciones necesarias, pero se realizan elecciones acertadas para asegurar que nunca se llega al punto de interbloqueo. La predicción, por lo tanto, permite más concurrencia que la prevención. Con predicción del interbloqueo, se decide dinámicamente si la petición actual de asignación de un recurso podría, de concederse, llevar potencialmente a un interbloqueo. La predicción del interbloqueo necesita, por lo tanto, conocer las peticiones futuras de recursos. Enfoques para la predicción del interbloqueo: - - No iniciar un proceso si sus demandas pueden llevar a interbloqueo. - - No conceder una solicitud de incrementar los recursos de un proceso si esta asignación puede llevar a interbloqueo. - DETECCIÓN DEL INTERBLOQUEO - Las estrategias de prevención de interbloqueo son muy conservadoras; resuelven el problema limitando el acceso a recursos e imponiendo restricciones sobre los procesos. En cambio, las estrategias de detección de interbloqueo, no limitan el acceso a recursos ni restringen las acciones del proceso. Con la detección del interbloqueo, se concederán los recursos que los procesos necesiten siempre que sea posible. Periódicamente, el S. O. ejecuta un algoritmo que permite detectar la condición de circulo vicioso de espera. - La detección del interbloqueo es el proceso de determinar si realmente existe un interbloqueo e identificar los procesos y recursos implicados en él. Una posibilidad detectar un interbloqueo es monitorear cada cierto tiempo el estado de los recursos. Cada vez que se solicita o se devuelve un recurso, se actualiza el estado de los recursos y se hace una verificación para observar si existe algún ciclo. - Este método está basado en suponer que un interbloqueo no se presente y que los recursos del sistema que han sido asignados, se liberarán en el momento que otro proceso lo requiera. - Algoritmo de detección del interbloqueo - Una comprobación para interbloqueo puede hacerse con igual o menor frecuencia que cada solicitud de recursos, dependiendo de que tan probable es que ocurra un interbloqueo. Comprobar cada solicitud de recursos tiene dos ventajas: Conduce a la detección temprana y el algoritmo es simple, de manera relativa porque se basa en cambios crecientes al estado del sistema. Además, las comprobaciones frecuentes consumen un tiempo considerable de procesador. - Los algoritmos de detección de bloqueos implican cierta sobrecarga en tiempo de ejecución: - surge el siguiente interrogante: - ¿ compensa la sobrecarga implicita en los algoritmos de detección de bloqueos, el ahorro potencial de localizarlos y romperlos ?. - - El empleo de algoritmos de detección de interbloqueo implica cierto gasto extra durante la ejecución. Así pues, se presenta de nuevo la cuestión de costeabilidad, tan habitual en los sistemas operativos. Los algoritmos de detección de interbloqueo determinan por lo general si existe una espera circular - RECUPERACIÓN DE INTERBLOQUEO - - Cuando se ha detectado que existe un interbloqueo, podemos actuar de varias formas. Una posibilidad es informar al operador que ha ocurrido un interbloqueo y dejar que el operador se ocupe de él manualmente. La otra posibilidad es dejar que el sistema se recupere automáticamente del interbloqueo. Dentro de esta recuperación automática tenemos dos opciones para romper el interbloqueo: Una consiste en abortar uno o más procesos hasta romper la espera circular, y la segunda es apropiar algunos recursos de uno o más de los procesos bloqueados. - La recuperación después de un interbloqueo se complica porque puede no estar claro que el sistema se haya bloqueado. Las mayorías de los Sistemas Operativos no tienen los medios suficientes para suspender un proceso, eliminarlo del sistema y reanudarlo más tarde. - Actualmente, la recuperación se suele realizar eliminando un proceso y quitándole sus recursos. El proceso eliminado se pierde, pero gracias a esto ahora es posible terminar. Algunas veces es necesario, eliminar varios procesos hasta que se hayan liberado los recursos necesarios para que terminen los procesos restantes. - Los procesos pueden eliminarse de acuerdo con algún orden de prioridad, aunque es posible que no existan prioridades entre los procesos bloqueados, de modo que el operador necesita tomar una decisión arbitraria para decidir que procesos se eliminarán. - Recuperación Manual - Está forma de recuperación consiste en avisarle al administrador o al operador del sistema que se ha presentado un interbloqueo, y será el administrador el que solucione dicho problema de la manera más conveniente posible, de modo que su decisión no afecte demasiado a al usuario del proceso en conflicto, y sobre todo que no afecte a los demás usuarios del sistema. Unidad 3 Administración del Procesador 3.1 Planeación Trabajos Job Scheduling 3.2 Conceptos Basicos Job Scheduling El Job Scheduler de Adaptive Server Enterprise (ASE), incluido desde la versión 12.5.1 (12.5.2 en Windows), es un componente que permite crear y programar la ejecución de tareas de base de datos, con el objetivo de automatizar la labores rutinarias del administrador y sin la necesidad de utilizar herramientas externas a ASE, como el cron de UNIX. Conceptos Antes de instalar y configurar el Job Scheduler de ASE, es importante definir y entender los siguientes conceptos: Job – Un job es una tarea de base de datos definida en términos de una secuencia válida de comandos Transact-SQL, como por ejemplo una serie de comandos dump o dbcc. Schedule – Un schedule es un horario de ejecución, definido en términos de atributos particulares, como fecha y hora de inicio, periodicidad, fecha y hora de finalización, etc. Scheduled Job – Un scheduled job es un job que ha sido asociado a un schedule y que, en consecuencia, se ejecutará automáticamente de acuerdo a lo definido en dicho schedule. La ejecución del scheduled job se lleva a cabo en el ASE denominado Target Server. Target Server – Es el servidor ASE en el cual se ejecutará un scheduled job. JS Server – Es el servidor ASE que almacena y administra los componentes del Job Scheduler (jobs, schedules y scheduled jobs) y es el encargado de controlar la ejecución de los scheduled jobs a través de los JS Tasks e interactuando con el JS Agent. En este servidor reside la base de datos sybmgmtdb en donde se almacena toda la información del sistema del Job Scheduler. JS Task – Es una tarea interna que corre en el ASE denominado JS Server, encargada de determinar qué scheduled jobs deben ser ejecutados en cada momento del tiempo. Cuando una tarea JS Task determina que una scheduled job debe ser ejecutado, pasa la información al JS Agent, quien inicia la ejecución en el ASE denominado Target Server. JS Agent – Es un proceso del sistema operativo que corre en la mísma máquina del JS Server. El JS Agent es el encargado de controlar la ejecución de los scheduled jobs en el Target Server, de acuerdo a la información recibida por los JS Task. Arquitectura del Job Scheduler Una vez el Job Scheduler ha sido debidamente instalado y configurado en ASE (en el JS Server), su funcionamiento se puede resumir en tres pasos básicos (la información sobre la instalación del Job Scheduler se encuentra en la siguiente sección): 1. El usuario crea un conjunto de jobs, schedules y scheduled jobs en el JS Server. Para cada scheduled job define también el Target Server en donde éste se ejecutará. Toda esta información es almacenada en la base de datos sybmgmtdb del JS Server. La creación de los jobs, schedules y scheduled jobs se puede hacer por línea de comandos o gráficamente desde Sybase Central. 2. El JS Task, que se ejecuta internamente en el JS Server, determina cuándo se debe ejecutar cada scheduled job y sobre cuál Target Server, leyendo información de la base de datos sybmgmtdb; esta información es pasada al JS Agent. 3. El JS Agent ejecuta de manera automática el scheduled job en el Target Server. La información histórica sobre la ejecución de la tarea se almacena en la base de datos sybmgmtdb del JS Server. Note que para efectos prácticos, el JS Server y el Target Server pueden ser el mismo servidor ASE. La figura 1 resume la arquitectura del JS de ASE: Figura 1 - Arquitectura del Job Scheduler de ASE Instalación y Cofiguración del Job Scheduler Para poder utilizar el Job Scheduler, usted debe completar los siguientes pasos básicos. Asumiremos para los ejemplos de ésta sección que el nombre del JS Server es PROD y que el nombre de la máquina donde éste corre es Jupiter. Note que en Windows usted debe reemplazar $SYBASE por %SYBASE% y $SYBASE_ASE por %SYBASE_ASE%. Instalación 1. En el JS Server cree un dispositivo de datos (database device) llamado sybmgmtdev, con un tamaño mínimo de 50 Mb (ASE 12.5.x) o 75 Mb (ASE 15.x). Por ejemplo: disk init name = "sybmgmtdev", physname = "/home/sybase/ASE-12_5/data/sybmgmtdev.dat", size = "75M" go 2. Ejecute el script installjsdb: isql -Usa -Psa_password -SPROD -i$SYBASE/$SYBASE_ASE/scripts/installjsdb 3. En la máquina donde corre el JS Server cree una entrada para el JS Agent en el archivo $SYBASE/interfaces usando el utilitario dsedit. Una vez creada la entrada, el archivo interfaces en Unix se vería así: PROD_JS master tcp ether Jupiter port_number query tcp ether Jupiter port_number port_number es el número de puerto asignado al JS Agent; este debe ser un puerto que no esté ya en uso. Sybase recomienda que el nombre del JS Agent sea de la forma servername_jsagent. 4. Use sp_addserver para crear un nuevo servidor remoto llamado SYB_JSAGENT en el JS Server: sp_addserver SYB_JSAGENT, null, PROD_JS go 5. En el JS Server habilite el Job Scheduler: sp_configure "enable job scheduler", 1 go 6. Para arrancar el Job Scheduler usted puede reiniciar ASE o ejecutar el siguiente comando: use sybmgmtdb go sp_js_wakeup "start_js", 1 go Definición de Usuarios y Privilegios Usted debe definir un conjuno de usuarios que puedan crear, administrar y ejecutar tareas usando el Job Scheduler. Esos usuarios deben tener un login tanto en el JS Server como en el Target Server. Asumiremos para el ejemplo que hemos creado un login llamado usuario_js, tanto en el JS Server como en el Target Server: 1. Agregue el usuario a la base de datos sybmgmtdb del JS Server: use sybmgmtdb go sp_adduser usuario_js go 2. Otorgue los privilegios adecuados al usuario: sp_role "grant", js_user_role, usuario_js go sp_modifylogin usuario_js, "add default role", js_user_role go Usted también puede otorgar los roles js_admin_role y/o js_client_role. Note, además, que usted debe otorgar los roles y permisos adecuados al usuario, de tal manera que éste pueda ejecutar de manera exitosa los jobs. Por ejemplo, si el usuario va a ejecutar una tarea que incluye un dump database, éste debe ser dueño de la base de datos (dbo) o tener el rol oper_role, de lo contrario la ejecución de la tarea programada fallará. Defina el Acceso a los Servidores Remotos Cuando el JS Server y el Target Server son Diferentes Si usted desea programar tareas sobre el servidor ASE en donde instaló el Job Scheduler, omita los pasos de ésta sección y haga referencia a la siguiente sección, "Cuando el JS Server y el Target Server son el Mismo". Cuando el JS Server y el Target Server son diferentes, usted deberá definir el acceso a los servidores remotos o Target Servers en donde se ejecutarán las tareas programadas. Asumiremos para el ejemplo que vamos a definir un Target Server llamado TEST el cual reside en la máquina Venus: 1. Usando dsedit agregue una entrada al archivo interfaces de la máquina donde reside el JS Server. Esta entrada en Unix se vería así: TEST master tcp ether Venus port_number query tcp ether Venus port_number donde port_number es el puerto del servidor ASE remoto (Target Server). 2. En el JS Server agregue un servidor remoto asociado al Target Server: sp_addserver TEST go 3. Agregue los logins externos para los usuarios del Job Scheduler en el JS Server: sp_addexternlogin TEST, usuario_js, usuario_js, remote_password go Note que en este caso el login usuario_js existe tanto en el JS Server como en el Target Server. Cuando el JS Server y el Target Server son el Mismo Si el JS Server y el Target Server son diferentes, omita los pasos de ésta sección y haga referencia a la sección anterior, "Cuando el JS Server y el Target Server son Diferentes". Cuando el JS Server y el Target Server son el mismo, en otras palabras usted desea programar tareas en el servidor ASE en donde instaló el Job Scheduler, siga estos pasos: 1. En el JS Server agregue un servidor remoto llamado loopback. Por ejemplo: sp_addserver loopback, null, PROD go donde PROD es el nombre del servidor local (el JS Server). 2. Agregue un login externo para el usuario del Job Scheduler en el JS Server: sp_addexternlogin loopback, usuario_js, usuario_js, remote_password go Para mayor información sobre la instalación y configuración del JS por favor consulte en manual Job Scheduler User's Guide, incluido con la documentación de ASE. Creación y Programación de una Tarea de Base de Datos En ésta última sección veremos paso a paso la creación de un Scheduled Job usando Sybase Central. Para efectos del ejemplo, crearemos un job que ejecutará un comando dump todos los días a media noche. Conéctese al JS Server 1. Ejecute Sybase Central y conéctese al servidor ASE en donde instaló el Job Scheduler (el JS Server). Esta conexión la puede hacer usando uno de los usuarios definidos en la sección anterior, por ejemplo usuario_js. 2. Una vez conectado, haga doble click en la carpeta Scheduled Jobs. Usted verá una ventana como la siguiente: La carpeta Scheduled Jobs contiene información sobre los jobs que han sido programados y su estado. También contiene un asistente para la programación de nuevos jobs (Add Scheduled Job) y las carpetas Jobs, Schedules y Job History. La carptea Jobs contiene información sobre los jobs que han sido creados y un asistente para la creación de nuevos jobs; la carpeta Schedules contiene información sobre los schedules que han sido creados y un asistente para la creación de nuevos schedules y la carpeta Job History contiene información histórica sobre la ejecución de las tareas programadas. Cree un Job 1. Haga doble click sobre la carpeta Jobs y luego sobre el ícono Add Job. En éste momento se ejecutará un asistente que lo guiará en la creación del Job. 2. Escriba el nombre del job, por ejemplo dump_master y luego haga click en Next. 3. Escriba la descripción, por ejemplo Dump de master y luego haga click en Next. 4. Escriba la definición del job. Por ejemplo: dump database master to "D:\Sybase\dumps\master.dmp" Recuerde que la definición del job se debe hacer en términos de una secuencia válida de comandos Transact-SQL. Para este caso además, usted se debe asegurar de que la ruta del archivo sea válida en el Target Server. Haga click en Next. 5. En la siguiente ventana defina los atributos adicionales del job y luego haga click en Next (puede dejar los valores predeterminados). 6. En la siguiente ventana defina el timeout y luego haga click en Next (puede dejar el valor predeterminado). 7. En la última ventana usted verá un resumen de su nuevo job; haga click en Finish. Usted verá su nuevo job en la carpeta Jobs: Usted puede cambiar cualquiera de las propiedades del job haciendo click derecho sobre éste y seleccionando el ítem Properties del menú desplegable. Cree un Schedule 1. Haga doble click en la carpeta Schedules y luego en el ícono Add Schedule. En este momento se ejecutará un asistente que lo guiará en la creación del schedule. 2. Especifique el nombre del schedule, por ejemplo todos_los_dias_12am y luego haga click en Next. 3. Escriba una descripción para el schedule, por ejemplo Todos los dias a la media noche y luego haga clock en Next. 4. Defina la hora o rango de horas. Para este caso seleccionaremos la opción At y la hora 00:00. Haga click en Next. 5. Seleccione la fecha de inicio y haga click en Next. 6. Selecionee la fecha de finalización o No end date si no hay fecha de finalización; haga click en Next. 7. Defina otras características del schedule y luego haga click en Next. 8. La última ventana mostrará un resumen de su nuevo schedule; haga click en Finish. Usted verá su nuevo schedule en la carpeta Schedules: Usted puede cambiar cualquiera de las propiedades del schedule haciendo click derecho sobre éste y seleccionando el ítem Properties del menú desplegable. Programe su Job (Creee un Scheduled Job) Una vez creado el job y el schedule, usted puede programar su job para que se ejecute en el horario especificado por el schedule. Una forma de hacerlo es: 1. Haga doble click en la carpeta Jobs. 2. Haga click derecho sobre el job recién creado; del menú desplegable seleccione el ítem Schedule. En este momento se ejecutará el asistente que le permitirá programar dicho job. 3. Confirme que el nombre del job es el que usted desea programar y presione Next. 4. En la siguiente ventana seleccione de la lista el nombre del schedule que usted desea usar, por ejemplo todos_los_dias_12am; haga click en Next. 5. Seleccione de la lista el Target Server que desea utilizar; este es el nombre del ASE en donde se ejecutará la tarea programada o scheduled job. Haga click en Next. 6. Defina atributos adicionales del scheduled job y presione Next. 7. En la siguiente ventana defina las opciones de "post-ejecución" y luego haga click en Next. 8. En la última ventana aparecerá un resumen de su nuevo Scheduled Job. Para terminar haga click en Finish. En la carpeta Scheduled Jobs usted verá su nueva tarea programada: Una vez creado el Scheduled Job éste comenzará a ejecutarse de acuerdo al horario definido por el schedule. Usted tambíén puede probar su ejecución haciendo click derecho sobre el scheduled job y seleccionando el ítem Run Now del menú desplegable: También es posible cambiar cualquiera de las propiedades del scheduled job haciendo click derecho sobre éste y seleccionando el ítem Properties del menú desplegable. Solución a Problemas Log de los scheduled jobs En la carpeta Job History (bajo Scheduled Jobs) usted encontrará la información sobre el estado de los scheduled jobs que se han ejecutado. Al dar click derecho sobre cualquiera de los ítems, usted verá varias opciones, entre las que se destaca la opción View Log. Esta opción le permite ver la salida generada durante la ejecución de la tarea. Log de errores del JS Agent También puede encontrar información adicional sobre los scheduled jobs en el log de errores de ASE y en el log de errores del JS Agent, usualmente ubicado en $SYBASE/$SYBASE_ASE/install/servername.log (donde servername es el nombre del JS Agent, por ejemplo PROD_JS.log). 3.3 Tipos de Planeacion Job Scheduling PLANEACION DE TRABAJOS (JOB SCHEDULING) Objetivo de la planificación: Minimizar el tiempo de espera y minimizar el tiempo de respuesta. La planificación (scheduling) es la base para lograr la multiprogramación. Un sistema multiprogramado tendrá varios procesos que requerirán el recurso procesador a la vez. Esto sucede cuando los procesos están en estado ready (pronto). Si existe un procesador disponible, se debe elegir el proceso que será asignado para ejecutar. La parte del sistema operativo que realiza la elección del proceso es llamada planificador (scheduler). La planificación hace referencia a un conjunto de políticas Y mecanismos incorporados a sistemas operativos que gobiernan el orden en que se ejecutan los trabajos. Un planificador es un módulo del S.O que selecciona el siguiente trabajo que hay que admitir en el sistema y el siguiente proceso que hay que ejecutar En muchos sistemas, la actividad de planificación se divide en tres funciones independientes: planificación a largo, medio, y corto plazo. 3.3.1 First in First Out Job Scheduling (FIFO) FIRST IN FIRST OUT (FIFO) Es muy simple, los procesos se despachan de acuerdo con su tiempo de llegada a la cola de listos. Una vez que el proceso obtiene la cpu, se ejecuta hasta terminar, ya que es una disciplina “no apropiativa”. Puede ocasionar que procesos largos hagan esperar a procesos cortos y que procesos no importantes hagan esperar a procesos importantes. Es más predecible que otros esquemas. No puede garantizar buenos tiempos de respuesta interactivos. Suele utilizarse integrado a otros esquemas, por ejemplo, de la siguiente manera: • Los procesos se despachan con algún esquema de prioridad. • Los procesos con igual prioridad se despachan “FIFO”. 3.3.2 Round Robin Job Scheduling (RR) ROUN ROBIN (RR) Algoritmo apropiativo consistente en determinar un quantum (tiempo de reloj) que marcará el intervalo de CPU que se le cederá al proceso ejecutando. Cuando finalice el quantum al Proceso se le quitará la CPU y pasará a la cola de listo. La cola de listos sigue la estructura FIFO. Si un proceso no consume su quantum libera la CPU y ésta es asignada al siguiente Proceso de la cola de listo. Los procesos se despachan en “FIFO” y disponen de una cantidad limitada de tiempo de cpu, llamada “división de tiempo” o “cuanto”. Si un proceso no termina antes de expirar su tiempo de cpu ocurren las siguientes acciones: 1. La cpu es apropiada. 2. La cpu es otorgada al siguiente proceso en espera. 3. El proceso apropiado es situado al final de la lista de listos. Es efectiva en ambientes de tiempo compartido. La sobrecarga de la apropiación se mantiene baja mediante mecanismos eficientes de intercambio de contexto y con suficiente memoria principal para los procesos. Características: • Fácil de implementar. • Perjudica a los procesos de E/S. • Si el quantum es muy grande se comporta como un FCFS. • El tiempo de respuesta para procesos cortos es bueno. • Trato equitativo entre procesos, bueno para interactividad. • No se produce inanición. • El valor mínimo del quantum debe ser (10 * Tiempo Cambio Contexto ) • El quantum más adecuado es el Tiempo de CPU del proceso más corto. 3.3.3 Shortest Job First Job Scheduling (SJF) SHORTEST JOB FIRST (SJF) Es una disciplina no apropiativa y por lo tanto no recomendable en ambientes de tiempo compartido. El proceso en espera con el menor tiempo estimado de ejecución hasta su terminación es el siguiente en ejecutarse. Los tiempos promedio de espera son menores que con “FIFO”. Los tiempos de espera son menos predecibles que en “FIFO”. Favorece a los procesos cortos en detrimento de los largos. Tiende a reducir el número de procesos en espera y el número de procesos que esperan detrás de procesos largos. Requiere un conocimiento preciso del tiempo de ejecución de un proceso, lo que generalmente se desconoce. Se pueden estimar los tiempos en base a series de valores anteriores. 3.3.4 Shortest Remaining Time Job Scheduling (STR) SHORTEST REMAINING TIME (STR) Esta disciplina elige siempre al proceso que le queda menos tiempo de ejecución estimado para completar su ejecución; de esta forma aunque un proceso requiera mucho tiempo de ejecución, a medida que se va ejecutando iría avanzando en la lista de procesos en estado listo hasta llegar a ser el primero. Para realizar esta elección, es necesario actualizar el PCB de los procesos a medida que se le asigna tiempo de servicio, lo que supone una mayor sobrecarga adicional. Es una disciplina apropiativa ya que a un proceso activo se le puede retirar la CPU si llega a la lista de procesos en estado listo otro con un tiempo restante de ejecución estimado menor. Este algoritmo es la versión no apropiativa o espulsiva del algoritmo Shortest Process Next (SPN) o también llamado Shortest Job First (SJF). En el algoritmo Shortest Remaining Time el planificador selecciona el proceso más corto, al igual que antes, pero en este caso el cambio se controla cada vez que un proceso llega a la cola. Es decir, cuando un proceso se desbloquea o se crea uno nuevo y el tiempo de ráfaga es menor que el tiempo de ráfaga del proceso que se está ejecutando, entonces se realiza un cambio de contexto, el bloqueado se ejecuta y el que se estaba ejecutando pasa a la cola de procesos listos. De este modo cuando se desbloquea o entra un proceso nuevo, se calcula su tiempo de ráfaga. Si el proceso que se está ejecutando le queda más tiempo de ráfaga que nuestro tiempo de ráfaga calculado entonces se procede a realizar el cambio de contexto. . Definición: Algoritmo apropiativo (que en cualquier momento se le puede quitar la CPU para asignársela otro proceso) consistente en elegir de la cola de listos el proceso con menos necesidad de tiempo restante de CPU para cada instante de tiempo. Características: Ofrece un buen tiempo de respuesta. La productividad es alta a cambio de la sobrecarga del sistema (a cada paso debe decidir a que proceso asignarle la CPU). Penaliza los procesos largos. Se puede producir inanición. 3.3.5 Highest Response Ratio Next Job Scheduling (HNR) HIGHEST RESPONSE RATIO NEXT (HRN) Definición: Algoritmo apropiativo parecido al SRT consistente en calcular el Reponse Ratio (Ratio de respuesta) para asignar la CPU a procesos más viejos. (Para evitar la inanición). Características: Es muy productivo pero se sobrecarga el sistema. Ofrece un buen tiempo de respuesta. Equilibra los procesos, aunque da prioridad a los procesos más cortos. Evita la inanición (los procesos que envejecen serán ejecutados). Las prioridades, que son dinámicas, se calculan según la siguiente fórmula, donde pr es la “prioridad”, te es el “tiempo de espera” y ts es el “tiempo de servicio”: • Elige proceso listo con valor mayor de R • Tiene en cuenta edad del proceso • Debe estimarse el tiempo se servicio previamente: en base a historia pasada o valor dado por usuario o administrador. • R= w + s s R= tasa de respuesta w= tiempo consumido esperando al procesador s = tiempo de servicio esperado Que corrige algunas deficiencias de SJF, particularmente el retraso excesivo de trabajos largos y el favoritismo excesivo para los trabajos cortos. HRN es un disciplina de planificación no apropiativa en la cual la prioridad de cada proceso no sólo se calcula en función del tiempo de servicio, sino también del tiempo que ha esperado para ser atendido. Cuando un trabajo obtiene el procesador, se ejecuta hasta terminar. Las prioridades dinámicas en HRN se calculan de acuerdo con la siguiente expresión: Prioridad = (tiempo de espera + tiempo de servicio) / tiempo de servicio Como el tiempo de servicio aparece en el denominador, los procesos cortos tendrán preferencia. Pero como el tiempo de espera aparece en el numerador, los procesos largos que han esperado también tendrán un trato favorable. Obsérvese que la suma tiempo de espera + tiempo de servicio es el tiempo de respuesta del sistema para el proceso si éste se inicia de inmediato. Para cada proceso, basado en el tiempo que va a ocupar el procesador(s) y el tiempo que lleva esperando para ocuparlo (w), Se calcula w+s/s, una vez echo esto el proceso que tenga un valor mayor será asignado al procesador. Este algoritmo es bastante bueno, por que además de dar preferencia a los procesos cortos también tiene en cuenta el envejecimiento de los procesos para evitar así la “inanición”. Cuando el proceso actual termina o se bloquea, se elige el proceso listo con un mayor valor de R. La decisión de planificación se basa en una estimación del tiempo de retorno normalizado. Este método es atractivo porque tiene en cuenta la edad del proceso. Aunque se favorece a los trabajos más cortos (un denominador menor produce una razón mayor), el envejecimiento de un proceso sin que haya sido servido incrementa el valor de la razón, de forma que los procesos más largos puedan pasar, en competición con los más cortos. El tiempo esperado de servicio debe estimarse antes de emplear la técnica de la mayor tasa de respuesta.7 3.4 Multiprocesamiento Procesador MULTIPROCESAMIENTO Generalidades de Multiprocesadores. Un multiprocesador se define como una computadora que contiene dos o más unidades de procesamiento que trabajan sobre una memoria común bajo un control integrado. Si el sistema de multiprocesamiento posee procesadores de aproximadamente igual capacidad, estamos en presencia de multiprocesamiento simétrico; en el otro caso hablamos de multiprocesamiento asimétrico. Si un procesador falla, los restantes continúan operando, lo cual no es automático y requiere de un diseño cuidadoso. Un procesador que falla habrá de informarlo a los demás de alguna manera, para que se hagan cargo de su trabajo . Los procesadores en funcionamiento deben poder detectar el fallo de un procesador Determinado. El Sistema Operativo debe percibir que ha fallado un procesador determinado y ya no podrá asignarlo y también debe ajustar sus estrategias de asignación de recursos para evitar la sobrecarga del sistema que está degradado. Distribución de Ciclos Una “estructura de ciclos o de repetición” implica la repetición de una serie de proposiciones (cuerpo del ciclo) hasta que ocurre alguna condición de terminación, por ejemplo: For i = 1 to 3 Do El procesador secuencial realizará en secuencia lo siguiente: En un sistema de multiprocesamiento con tres procesadores disponibles se podrían Ejecutar concurrentemente. Reducción de la Altura del Arbol Utilizando las propiedades asociativa, conmutativa y distributiva de la aritmética, los Compiladores pueden: 1. Detectar el paralelismo implícito en expresiones algebraicas. 2. Producir un código objeto para multiprocesadores que indique las operaciones que se pueden realizar simultáneamente. 3. Reordenar expresiones para que sean más apropiadas para la computación en paralelo. 3.5 Conceptos Basicos Multiprocesamiento SISTEMAS MULTIPROCESAMIENTO INTRODUCCION A pesar de las grandes mejoras acaecidas en monoprocesadores para algunas aplicaciones no es suficiente. – La solución pueden ser los sistemas multiprocesadores: o Solución más sencilla, natural y con mejor coste-prestaciones. o Las mejoras en microprocesadores cada vez son más complejas: cada avance implica crecer en complejidad, potencia y superficie. o Lenta pero clara mejora en el software, que permite explotar el paralelismo. – Las arquitecturas actuales son muy diversas: hay más investigación que resultados definitivos. – Hablaremos de multiprocesadores de pequeña y median escala Dos factores clave para la extensión de los Multiprocesadores 1. Flexibilidad: El mismo sistema puede usarse para un único usuario incrementado el rendimiento en la ejecución de una única aplicación o para varios usuarios y aplicaciones en un entorno compartido. 2. Coste-rendimiento: Actualmente estos sistemas se basan en procesadores comerciales, por lo que su coste se ha reducido drásticamente. La inversión más fuerte se hace en la memoria y la red de interconexión. Como su nombre indica son aquellos sistemas operativos que están montados sobre ordenadores que están compuestos por más de un procesador, supongamos un PC que en vez de tener un Pentium, tuviera dos o más Pentium conectados entre si dentro de la misma placa base, esto sería un sistema multiprocesador. CLASIFICACION POR USO DE LOS RECURSOS Sistemas monoprogramados: Son los que solo permiten la ejecución de un programa en el sistema, se instalan en la memoria y permanecen allí hasta que termine su ejecución. Sistemas multiprogramados: Son aquellos que se basan en las técnicas de multiprogramación, existen dos tipos: Multitarea apropiativa (preemptive): Se utiliza en sistemas operativos cuya gestión es quitar el control del microprocesador al programa que lo tiene. Multitarea cooperativa: El programa tiene el control del microprocesador, el sistema operativo no puede decidir quien usa el microprocesador. Sistemas de multiprocesamiento: Formado por varios microprocesadores. Depende del tipo de trabajo y los objetivos que debe cumplir cada sistema para dar el mejor servicio al usuario, se clasifican en: Procesamiento por lotes (batch): Cada programa realiza un conjunto de pasos secuenciales relacionados entre s 3.6 Paralelismo Multiprocesamiento PARALELISMO Funcionamiento El paralelismo consiste en ejecutar más instrucciones en menos tiempo, aunque las instrucciones sigan tardando lo mismo en ejecutarse, mediante un simple truco, aunque algo difícil de explicar en detalle. Intentémoslo. un microprocesador ejecuta instrucciones de código máquina. Estas instrucciones le dicen como tiene que ir modificando diferentes posiciones de memoria, y como debe ir modificando el flujo de ejecución. Se tiende a pensar, erróneamente, que un procesador con un reloj a 200 MHz (200 millones de ciclos por segundo) ejecuta 200 millones de estas operaciones por segundo. Esto no es así, por una sencilla razón. Una instrucción no se ejecuta en un solo ciclo de reloj, salvo alguna rara excepción. De hecho, algunas instrucciones tardan bastantes más ciclos, llegando algunas a necesitar 50 o más ciclos para completarse. En cambio, las más rápidas se ejecutan en tan sólo 3 o 4 ciclos de reloj. Aquí es donde entra el paralelismo para solucionar este problema. Se puede dividir cualquier instrucción en fases más o menos comunes a todas: fetch (carga de la instrucción desde la memoria al procesador), decodificación (identificación de qué instrucción nos hemos encontrado), carga de operandos, operación en sí y escritura de resultados. Este esquema, expresamente simplificado, nos da una idea de las fases que todo microprocesador tiene. Vamos a suponer un microprocesador ideal donde todas las operaciones que se pueden ejecutar en él tardan 15 ciclos, correspondientes a tres ciclos por cada una de las 5 fases que hemos descrito. Si ejecutáramos tres de estas operaciones sin ningún tipo de paralelismo, tardaríamos 45 ciclos, según el siguiente esquema: instr.1:111222333444555 instr.2: 111222333444555 instr. 3: 111222333444555 Ahora supongamos que somos capaces de dividir el microprocesador en circuitos separados capaces cada uno de trabajar independientemente y ejecutar cada una de las 5 fases anteriores. Si logramos que sean independientes, cuando la instrucción uno ha acabado ya la fase de fetch y pasa a la decodificación, deja libre el módulo que se encarga del fetch, donde puede ir ya ejecutándose la segunda instrucción. De esta forma, logramos paralelizar las instrucciones. instr.1111222333444555 instr.2: 111222333444555 instr. 3: 111222333444555 Resultado: las tres instrucciones, por separado, siguen ejecutándose en el mismo tiempo, pero en conjunto ya no tardan 45 ciclos, sino solo 21 ciclos. Más de un 45% de incremento en el rendimiento. De esta forma es como algunos procesadores muy paralelizados logran ejecutar, en media, más de una instrucción por ciclo de reloj, aunque estas instrucciones tarden, por sí mismas, más de un ciclo en ejecutarse. En la realidad, como siempre, no todo es tan fácil y hay muchos problemas al diseñar un procesador con paralelismo. Por citar algunos de los problemas más comunes, hay veces que una instrucción no se puede ejecutar ya que requiere un dato que quizás calculaba la operación anterior (cosa muy habitual). Claro, si ante este problema detuviéramos la anterior instrucción, bloquearía el procesador y se acabaría el paralelismo hasta que acabara la primera instrucción y con ella se pudiera reanudar la segunda. Para evitar estos problemas se recurre a cortocircuitos, o lo que es lo mismo, se comunican diferentes fases del microprocesador internamente para pasarse antes los datos. Esto, sin embargo, también nos da otros problemas, ya mucho más complicados, como el encontrarnos con que hay que decidir que datos son los correctos en cada momento. En estos problemas ya no entraremos, y se podrían resumir en que el procesador ha de decidir como paralelizar las instrucciones. Bien, todo lo que hemos visto sobre el paralelismo involucra única y exclusivamente al microprocesador en sí, y más bien a su diseño. El software que se ejecuta sobre él ignora totalmente si hay paralelismo o no. Esto es el paralelismo implícito. Por el contrario, Intel implementa una solución que de hecho ya deriva de ideas de principios de los años 80. En el paralelismo explícito, el procesador ya no es el que decide cómo paralelizar las instrucciones, sino que es el compilador del software el que ha empaquetado las instrucciones para que el microprocesador pueda ejecutarlas paralelamente sin tantos problemas. De hecho, esta manera es mucho más eficiente, porque el compilador tiene todo el tiempo del mundo para decidir cómo paralelizar y por supuesto, la lógica que puede aplicar es infinitamente más potente que la que podemos encontrar implementada en cualquier microprocesador. Esto también redunda en una simplificación de la circuitería de control del microprocesador, lo que permite acelerar aún más las instrucciones. Además, queda libre más espacio para incluir aún más registros y hacer los buses internos más anchos, lo que permite ejecutar aún más instrucciones en paralelo. Paralelismo en software Definamos como paralelismo en software como la ejecución de un programa sin tomar en cuenta el hardware con que va ser ejecutado. El paralelismo en software es considerado como el caso ideal de la ejecución de las instrucciones que forman parte de un programa, ya que no toma en cuenta las limitantes del hardware con que el mismo va ser ejecutado. Paralelismo en hardware Definamos como paralelismo en hardware como la ejecución de un programa tomando en consideración el hardware con que va a ser ejecutado. El diagrama de paralelismo en Software representa el caso ideal con que dicho programa puede ser ejecutado. Nótese que la ejecución de las 8 instrucciones se realiza solamente en tres ciclos de máquina. Por otro lado podemos observar las limitantes que genera la ejecución de este mismo programa con un hardware en particular (procesador Superescalar con capacidad de ejecutar un acceso a la memoria y una operación aritmética simultáneamente) obteniendo 6 ciclos de maquina para ejecutar el programa. Tomando como base este ejemplo, la ejecución paralela de las instrucciones de un programa se mide mediante el parámetro conocido como Promedio de Ejecución Paralela de instrucciones (PEP). Este parámetro se define como la relación entre el número de instrucciones del programa y el número de ciclos de máquina realizados en su ejecución. Su expresión matemática es: PEP = No. de Instrucciones / No. de Ciclos de Máquina Por consiguiente, el promedio de ejecución paralela de instrucciones en software para este ejemplo es: 8/3 = 2,667 y el promedio de ejecución paralela de instrucciones en hardware es: 8/6 = 1,333. El desarrollo de hardware y software es un proceso integral que busca soluciones que permitan satisfacer cada vez más las condiciones de paralelismo con el fin de incrementar el promedio de ejecución paralela de instrucciones. Para lograr este objetivo es necesario detectar y resolver las dependencias entre instrucciones. El proceso de detección y resolución de dependencias entre instrucciones se conoce como el proceso de planificación de instrucciones. Cuando la planificación de instrucciones es llevada a cabo únicamente por el compilador se dice que la planificación de instrucciones es estática. Y cuando la planificación de instrucciones es llevada a cabo únicamente por hardware (Ejemplo: microprocesador) se dice que la planificación de instrucciones es dinámica. La planificación de instrucciones en los microprocesadores súper escalares es un proceso de planificación de instrucciones estático y dinámico. Las técnicas estáticas de planificación de instrucciones están compuestas por tres grupos: Planificación de instrucciones de bloques de un programa, Planificación de instrucciones de lazos iterativos continuos y planificación de instrucciones global. La técnica de bloques consiste en dividir un programa en bloques para luego detectar y resolver solamente las dependencias entre las instrucciones de cada bloque. Esta técnica es la mas utilizada en los últimos 20 años ya que es la más simple de implementar. La técnica de lazos iterativos consiste planificar las instrucciones que forman parte de los lazos continuos de un programa. Esta técnica esta compuesta básicamente por dos técnicas: Unrolling y Software Pipeline. Y por ultimo la técnica global consiste en planificar todas las instrucciones que forman parte de un programa. En la sección 2 del seminario Arquitectura de Microprocesadores se analizan las técnicas más comunes de planificación de instrucciones dinámicas implementadas en los microprocesadores y a su vez se demuestra que el desarrollo de hardware y software es un proceso integral que busca soluciones que permitan satisfacer cada vez más las condiciones de paralelismo con el fin de incrementar el promedio de ejecución paralela de instrucciones. 3.7 Sistemas Multiprocesamiento SISTEMAS MULTIPROCESAMIENTO INTRODUCCION A pesar de las grandes mejoras acaecidas en monoprocesadores para algunas aplicaciones no es suficiente. – La solución pueden ser los sistemas multiprocesadores: o Solución más sencilla, natural y con mejor coste-prestaciones. o Las mejoras en microprocesadores cada vez son más complejas: cada avance implica crecer en complejidad, potencia y superficie. o Lenta pero clara mejora en el software, que permite explotar el paralelismo. – Las arquitecturas actuales son muy diversas: hay más investigación que resultados definitivos. – Hablaremos de multiprocesadores de pequeña y median escala Dos factores clave para la extensión de los Multiprocesadores 1. Flexibilidad: El mismo sistema puede usarse para un único usuario incrementado el rendimiento en la ejecución de una única aplicación o para varios usuarios y aplicaciones en un entorno compartido. 2. Coste-rendimiento: Actualmente estos sistemas se basan en procesadores comerciales, por lo que su coste se ha reducido drásticamente. La inversión más fuerte se hace en la memoria y la red de interconexión. Como su nombre indica son aquellos sistemas operativos que están montados sobre ordenadores que están compuestos por más de un procesador, supongamos un PC que en vez de tener un Pentium, tuviera dos o más Pentium conectados entre si dentro de la misma placa base, esto sería un sistema multiprocesador. CLASIFICACION POR USO DE LOS RECURSOS Sistemas monoprogramados: Son los que solo permiten la ejecución de un programa en el sistema, se instalan en la memoria y permanecen allí hasta que termine su ejecución. Sistemas multiprogramados: Son aquellos que se basan en las técnicas de multiprogramación, existen dos tipos: Multitarea apropiativa (preemptive): Se utiliza en sistemas operativos cuya gestión es quitar el control del microprocesador al programa que lo tiene. Multitarea cooperativa: El programa tiene el control del microprocesador, el sistema operativo no puede decidir quien usa el microprocesador. Sistemas de multiprocesamiento: Formado por varios microprocesadores. Depende del tipo de trabajo y los objetivos que debe cumplir cada sistema para dar el mejor servicio al usuario, se clasifican en: Procesamiento por lotes (batch): Cada programa realiza un conjunto de pasos secuenciales relacionados entre si 3.8 Organizacion del Multiprocesador ORGANIZACIÓN DEL HARDWARE DEL MULTIPROCESADOR El problema clave es determinar los medios de conexión de los procesadores múltiples y los procesadores de Entrada / Salida a las unidades de almacenamiento. Los multiprocesadores se caracterizan por los siguientes aspectos: • Un multiprocesador contiene dos o más procesadores con capacidades aproximadamente comparables. • Todos los procesadores comparten el acceso a un almacenamiento común y a canales de Entrada / Salida, unidades de control y dispositivos. • Todo está controlado por un Sistema Operativo que proporciona interacción entre procesadores y sus programas en los niveles de trabajo, tarea, paso, archivo y elementos de datos. Las organizaciones más comunes son las siguientes: • Tiempo compartido o bus común (conductor común). • Matriz de barras cruzadas e interruptores. • Almacenamiento de interconexión múltiple. Tiempo Compartido o Bus Común (o Conductor Común) Usa un solo camino de comunicación entre todas las unidades funcionales El bus común es en esencia una unidad pasiva. Un procesador o procesador de Entrada / Salida que desee transferir datos debe efectuar los siguientes pasos: 1. Verificar la disponibilidad del conductor y de la unidad de destino. 2. Informar a la unidad de destino de lo que se va a hacer con los datos. 3. Iniciar la transferencia de datos. Las unidades receptoras deben poder reconocer qué mensajes del bus son enviados hacia ellas y seguir y confirmar las señales de control recibidas de la unidad emisora. Es una organización económica, simple y flexible pero con una sola vía de comunicación, por lo cual: • El sistema falla totalmente si falla el bus. • La tasa neta de transmisiones está limitada por la tasa neta de transmisión del conductor. • La contención por el uso del bus en un sistema sobrecargado puede ocasionar una seria degradación. Matriz de Barras Cruzadas e Interruptores En este caso existe un camino diferente para cada unidad de almacenamiento, por lo cual las referencias a dos unidades diferentes de almacenamiento no son bloque antes sino simultáneas y la multiplicidad de caminos de transmisión puede proporcionar tasas de transferencia muy altas Almacenamiento de Interconexión Múltiple Se obtiene al sacar las lógicas de control, de conmutación y de arbitraje de prioridades fuera del interruptor de barras cruzadas se las coloca en la interfaz de cada unidad de almacenamiento Cada unidad funcional puede acceder a cada unidad de almacenamiento, pero sólo en una “conexión de almacenamiento” específica, es decir que hay una conexión de almacenamiento por unidad funcional. El conexionado es más complejo que en los otros esquemas. Se puede restringir el acceso a las unidades de almacenamiento para que no todas las unidades de procesamiento las accedan, en tal caso habrá unidades de almacenamiento “privadas” de determinados procesadores 3.9 Sistemas Operativos del Multiprocesador Sistema Operativo de Multiprocesadores Las capacidades funcionales de los Sistema Operativo de multiprogramación y de multiprocesadores incluyen lo siguiente: • Asignación y administración de recursos. • Protección de tablas y conjuntos de datos. • Prevención contra el ínter bloqueo del sistema. • Terminación anormal. • Equilibrio de cargas de Entrada / Salida. • Equilibrio de carga del procesador. • Reconfiguración. Las tres últimas son especialmente importantes en Sistemas Operativos de multiprocesadores, donde es fundamental explotar el paralelismo en el hardware y en los programas y hacerlo automáticamente. Las organizaciones básicas de los Sistemas Operativos para multiprocesadores son las siguientes: • Maestro / satélite. • Ejecutivo separado para cada procesador. • Tratamiento simétrico (o anónimo) para todos los procesadores. Maestro / Satélite Es la organización más fácil de implementar. No logra la utilización óptima del hardware dado que sólo el procesador maestro puede ejecutar el Sistema Operativo y el procesador satélite sólo puede ejecutar programas del usuario. Las interrupciones generadas por los procesos en ejecución en los procesadores satélites que precisan atención del Sistema Operativo deben ser atendidas por el procesador maestro y por ello pueden generarse largas colas de requerimientos pendientes. Ejecutivos Separados Cada procesador tiene su propio Sistema Operativo y responde a interrupciones de los usuarios que operan en ese procesador. Existen tablas de control con información global de todo el sistema (por ejemplo, lista de procesadores conocidos por el Sistema Operativo) a las que se debe acceder utilizando exclusión mutua. Es más confiable que la organización maestro / satélite. Cada procesador controla sus propios recursos dedicados. La reconfiguración de los dispositivos de Entrada / Salida puede implicar el cambio de dispositivos a diferentes procesadores con distintos Sistemas Operativos. La contención sobre las tablas del Sistema Operativo es mínima. Los procesadores no cooperan en la ejecución de un proceso individual, que habrá sido asignado a uno de ellos. Tratamiento Simétrico Es la organización más complicada de implementar y también la más poderosa y confiable. El Sistema Operativo administra un grupo de procesadores idénticos, donde cualquiera puede utilizar cualquier dispositivo de Entrada / Salida y cualquiera puede referenciar a cualquier unidad de almacenamiento. El Sistema Operativo precisa código reentrarte y exclusión mutua. Es posible equilibrar la carga de trabajo más precisamente que en las otras organizaciones. Adquieren significativa importancia el hardware y el software para resolución de conflictos. Todos los procesadores pueden cooperar en la ejecución de un proceso determinado. El procesador ejecutivo es el responsable (uno sólo) en un momento dado de las tablas y funciones del sistema; así se evitan los conflictos sobre la información global. Unidad 4 Administración de la Memoria 4.1 Gestión de Memoria La memoria es uno de los principales recursos de la computadora, la cual debe de administrarse con mucho cuidado. Aunque actualmente la mayoría de los sistemas de cómputo cuentan con una alta capacidad de memoria, de igual manera las aplicaciones actuales tienen también altos requerimientos de memoria, lo que sigue generando escasez de memoria en los sistemas multitarea y/o multiusuario. La parte del sistema operativo que administra la memoria se llama administrador de memoria y su labor consiste en llevar un registro de las partes de memoria que se estén utilizando y aquellas que no, con el fin de asignar espacio en memoria a los procesos cuando éstos la necesiten y liberándola cuando terminen, así como administrar el intercambio entre la memoria principal y el disco en los casos en los que la memoria principal no le pueda dar capacidad a todos los procesos que tienen necesidad de ella. Los sistemas de administración de memoria se pueden clasificar en dos tipos: los que desplazan los procesos de la memoria principal al disco y viceversa durante la ejecución y los que no. El propósito principal de una computadora es el de ejecutar programas, estos programas, junto con la información que accesan deben de estar en la memoria principal (al menos parcialmente) durante la ejecución. Para optimizar el uso del CPU y de la memoria, el sistema operativo debe de tener varios procesos a la vez en la memoria principal, para lo cual dispone de varias opciones de administración tanto del procesador como de la memoria. La selección de uno de ellos depende principalmente del diseño del hardware para el sistema. A continuación se observarán los puntos correspondientes a la administración de la memoria. Memoria real La memoria real o principal es en donde son ejecutados los programas y procesos de una computadora y es el espacio real que existe en memoria para que se ejecuten los procesos. Por lo general esta memoria es de mayor costo que la memoria secundaria, pero el acceso a la información contenida en ella es de más rápido acceso. Solo la memoria cache es más rápida que la principal, pero su costo es a su vez mayor. Memoria virtual El termino memoria virtual se asocia a dos conceptos que normalmente a parecen unidos: 1. El uso de almacenamiento secundario para ofrecer al conjunto de las aplicaciones la ilusión de tener mas memoria RAM de la que realmente hay en el sistema. Esta ilusión de existe tanto a nivel del sistema, es decir, teniendo en ejecución mas aplicaciones de las que realmente caben en la memoria principal, sin que por ello cada aplicación individual pueda usar mas memoria de la que realmente hay o incluso de forma mas general, ofreciendo a cada aplicación mas memoria de la que existe físicamente en la maquina. 2. Ofrecer a las aplicaciones la ilusión de que están solas en el sistema, y que por lo tanto, pueden usar el espacio de direcciones completo. Esta técnica facilita enormemente la generación de código, puesto que el compilador no tiene porque preocuparse sobre dónde residirá la aplicación cuando se ejecute. Espacio De Direcciones Los espacios de direcciones involucrados en el manejo de la memoria son de tres tipos: Direcciones físicas: son aquellas que referencian alguna posición en la memoria física. Direcciones lógicas : son las direcciones utilizadas por los procesos. Sufren una serie de transformaciones , realizadas por el procesador (la MMU), antes de convertirse en direcciones físicas. Direcciones lineales: direcciones lineales se obtienen a partir de direcciones lógicas tras haber aplicado una transformación dependiente de la arquitectura. Los programas de usuario siempre tratan con direcciones virtuales ; nunca ven las direcciones físicas reales.. Unidad De Manejo De Memoria La unidad de manejo de memoria (MMU) es parte del procesador. Sus funciones son: Convertir las direcciones lógicas emitidas por los procesos en direcciones físicas. Comprobar que la conversión se puede realizar. La dirección lógica podría no tener un dirección física asociada. Por ejemplo, la pagina correspondiente a una dirección se puede haber trasladado a una zona de almacenamiento secundario temporalmente. Comprobar que el proceso que intenta acceder a una cierta dirección de memoria tiene permisos para ello. La MMU se Inicializa para cada proceso del sistema. Esto permite que cada proceso pueda usar el rango completo de direcciones lógicas (memoria virtual), ya que las conversiones de estas direcciones serán distintas para cada proceso. En todos los procesos se configura la MMU para que la zona del núcleo solo se pueda acceder en modo privilegiado del procesador. La configuración correspondiente al espacio de memoria del núcleo es idéntica en todos los procesos. El objetivo del intercambio es dar cabida a la ejecución de mas aplicaciones de las que pueden residir simultáneamente en la memoria del sistema: Consiste en trasladar el código y los datos de un proceso completo de memoria al sistema de almacenamiento secundario , para cargar otro previamente almacenado, no permite a un proceso utilizar mas memoria RAM de la que realmente existe en el sistema. Esta técnica puede ser ineficiente ya que se tiene que hacer el intercambio completo del proceso, aunque éste solo vaya a ejecutar una pequeña porción del código. Durante el intercambio un proceso puede ser sacado temporalmente de memoria y llevado a un lugar especial del disco y posteriormente vuelto a memoria y continuada su ejecución.. El lugar de almacenamiento temporal suele ser un espacio suficientemente grande como para acomodar copias de las imágenes de memoria de todos los usuarios. Asignación Contigua La memoria principal normalmente se divide en dos particiones: Sistema operativo residente, normalmente en la parte baja de memoria con los vectores de interrupción. Procesos de usuario en la parte alta. Asignación de partición simple: Puede utilizarse un esquema de registro de relocalización y limite para proteger un proceso de usuario de otro y de cambios del código y datos del sistema operativo . El registro de relocalización contiene la dirección contiene la dirección física mas pequeña; el registro limite contiene el rango de las direcciones lógicas cada dirección lógica debe ser menor al registro limite 4.1.1 Organizacion de la Memoria Archivo a parte en diapositivas….. 4.1.2 Administrador de la Memoria El Administrador De Memoria se refiere a los distintos métodos y operaciones que se encargan de obtener la máxima utilidad de la memoria, organizando los procesos y programas que se ejecutan de manera tal que se aproveche de la mejor manera posible el espacio disponible. Para poder lograrlo, la operación principal que realiza es la de trasladar la información que deberá ser ejecutada por el procesador, a la memoria principal. Actualmente esta administración se conoce como Memoria Virtual ya que no es la memoria física del procesador sino una memoria virtual que la representa. Entre algunas ventajas, esta memoria permite que el sistema cuente con una memoria más extensa teniendo la misma memoria real, con lo que esta se puede utilizar de manera más eficiente. Y por supuesto, que los programas que son utilizados no ocupen lugar innecesario. Las técnicas que existen para la carga de programas en la memoria son: partición fija, que es la división de la memoria libre en varias partes (de igual o distinto tamaño) y la partición dinámica, que son las particiones de la memoria en tamaños que pueden ser variables, según la cantidad de memoria que necesita cada proceso. Entre las principales operaciones que desarrolla la administración de memoria se encuentran la reubicación, que consiste en trasladar procesos activos dentro y fuera e la memoria principal para maximizar la utilización del procesador; la protección, mecanismos que protegen los procesos que se ejecutan de interferencias de otros procesos; uso compartido de códigos y datos, con lo que el mecanismo de protección permite que ciertos procesos de un mismo programa que comparten una tarea tengan memoria en común. 4.1.3 Jerarquía de la Memoria Se conoce como jerarquía de memoria a la organización piramidal de la memoria en niveles, que tienen los ordenadores. Su objetivo es conseguir el rendimiento de una memoria de gran velocidad al coste de una memoria de baja velocidad, basándose en el principio de cercanía de referencias. Los puntos básicos relacionados con la memoria pueden resumirse en: * Cantidad * Velocidad * Coste La cuestión de la cantidad es simple, cuanto más memoria haya disponible, más podrá utilizarse. La velocidad óptima para la memoria es la velocidad a la que el procesador puede trabajar, de modo que no haya tiempos de espera entre cálculo y cálculo, utilizados para traer operandos o guardar resultados. En suma, el costo de la memoria no debe ser excesivo, para que sea factible construir un equipo accesible. Como puede esperse los tres factores compiten entre sí, por lo que hay que encontrar un equilibrio. Las siguientes afirmaciones son válidas: * A menor tiempo de acceso mayor coste * A mayor capacidad menor coste * A mayor capacidad menor velocidad. Se busca entonces contar con capacidad suficiente de memoria, con una velocidad que sirva para satisfacer la demanda de rendimiento y con un coste que no sea excesivo. Gracias a un principio llamado cercanía de referencias, es factible utilizar una mezcla de los distintos tipos y lograr un rendimiento cercano al de la memoria más rápida. Los niveles que componen la jerarquía de memoria habitualmente son: * Nivel 0: Registros * Nivel 1: Memoria caché * Nivel 2: Memoria principal * Nivel 3: Disco duro (con el mecanismo de memoria virtual) Mc.! 4.1.4 Estrategias para Administración de Memoria De las diversas organizaciones de memoria tratadas en el tema anterior únicamente las que realizan una asignación no contigua (paginación, segmentación y segmentación paginada) del almacenamiento permiten implantar una administración virtual de la memoria. Para cualquiera de las tres formas de organizar esta memoria virtual habrá que determinar: Estrategias de obtención. Determinan cuándo se debe transferir una página o un segmento del almacenamiento secundaria al primario. Las estrategias de obtención por demanda esperan a que un proceso en ejecución haga referencia a una página o a un segmento antes de traerla/lo. Los esquemas de obtención anticipada intentan determinar por adelantado a qué páginas o segmentos hará referencia un proceso. Si la probabilidad de una referencia es alta y hay espacio disponible, entonces se trae al almacenamiento primario la página o segmento antes de que se haga la referencia explícita Estrategias de colocación. Determinan en qué lugar de la memoria principal se debe colocar una página o un segmento entrante. Los sistemas de paginación vuelven trivial la decisión de colocación, porque una página entrante se puede ubicar en cualquier marco de página disponible. Los sistemas con segmentación requieren estrategias de colocación como las tratadas en el contexto de los sistemas de multiprogramación con particiones dinámicas. Estrategias de reemplazo. Sirven para decidir qué página o segmento se debe desplazar para dejar espacio a una página o segmento entrante cuando está completamente ocupada la memoria principal. 4.1.5 Multiprogramación con Particiones Fijas y Variables !! Multiprogramación con particiones fijas Para poder implementar la multiprogramación, se puede hacer uso de particiones fijas o variables en la memoria. En el caso de las particiones fijas, la memoria se puede organizar dividiéndose en diversas partes, las cuales pueden variar en tamaño. Esta partición la puede hacer el usuario en forma manual, al iniciar una sesión con la máquina. Una vez implementada la partición, hay dos maneras de asignar los procesos a ella. La primera es mediante el uso de una cola única que asigna los procesos a los espacios disponibles de la memoria conforme se vayan desocupando. El tamaño del hueco de memoria disponible es usado para localizar en la cola el primer proceso que quepa en él. Otra forma de asignación es buscar en la cola el proceso de tamaño mayor que se ajuste al hueco, sin embargo hay que tomar en cuenta que tal método discrimina a los procesos más pequeños. Dicho problema podría tener solución si se asigna una partición pequeña en la memoria al momento de hacer la partición inicial, el cual sería exclusivo para procesos pequeños. Esta idea nos lleva a la implementación de otro método para particiones fijas, que es el uso de diferentes colas independientes exclusivas para cierto rango en el tamaño de los procesos. De esta manera al llegar un proceso, éste sería asignado a la cola de tamaño más pequeño que la pueda aceptar. La desventaja en esta organización es que si una de las colas tiene una larga lista de procesos en espera, mientras otra cola esta vacía, el sector de memoria asignado para ese tamaño de procesos estaría desperdiciándose. !! Multiprogramación con particiones variables Este esquema fue originalmente usado por el sistema operativo IBM OS/360 (llamado MFT), el cual ya no está en uso. El sistema operativo lleva una tabla indicando cuáles partes de la memoria están disponibles y cuáles están ocupadas. Inicialmente, toda la memoria está disponible para los procesos de usuario y es considerado como un gran bloque o hueco único de memoria. Cuando llega un proceso que necesita memoria, buscamos un hueco lo suficientemente grande para el proceso. Si encontramos uno, se asigna únicamente el espacio requerido, manteniendo el resto disponible para futuros procesos que requieran de espacio. Cuando a un proceso se le asigna un espacio y es cargado a la memoria principal, puede entonces competir para el uso del CPU. 4.2 Memoria Real La memoria real o principal es en donde son ejecutados los programas y procesos de una computadora y es el espacio real que existe en memoria para que se ejecuten los procesos. Por lo general esta memoria es de mayor costo que la memoria secundaria, pero el acceso a la información contenida en ella es de más rápido acceso. Solo la memoria cache es más rápida que la principal, pero su costo es a su vez mayor. Administración de la memoria con mapas de bits Este tipo de administración divide la memoria en unidades de asignación, las cuales pueden ser tan pequeñas como unas cuantas palabras o tan grandes como varios kilobytes. A cada unidad de asignación le corresponde un bit en el mapa de bits, el cual toma el valor de 0 si la unidad está libre y 1 si está ocupada (o viceversa). La figura 6 muestra una parte de la memoria y su correspondiente mapa de bits. Administración de la memoria con listas ligadas Otra forma de mantener un registro de la memoria es mediante una lista ligada de los segmentos de memoria asignados o libres, en donde un segmento puede ser un proceso o un hueco entre dos procesos. La memoria de la figura 7(a) está mostrada como una lista ligada de segmentos en la figura 7(b). Cada entrada de la lista especifica un hueco (H) o un proceso (P), la dirección donde comienza, su longitud y un apuntador a la siguiente entrada. Asignación del hueco de intercambio En algunos sistemas, cuando el proceso se encuentra en la memoria, no hay un hueco en el disco asignado a él. Cuando deba intercambiarse, se deberá asignar un hueco para él en el área de intercambio del disco. Los algoritmos para la administración del hueco de intercambio son los mismos que se utilizan para la administración de la memoria principal. En otros sistemas, al caerse un proceso, se le asigna un hueco de intercambio en el disco. Cuando el proceso sea intercambiado, siempre pasará al hueco asignado, en vez de ir a otro lugar cada vez. Cuando el proceso concluya, se libera el hueco de intercambio. La única diferencia es que el hueco en disco necesario para un proceso debe representarse como un número entero de bloques del disco. Por ejemplo, un proceso de 13.5 K debe utilizar 14K (usando bloques de 1K). 4.2.1 Administración de Memoria con Mapa de Bits La memoria se divide en unidades de asignación, a cada asignación le correspoden un bit en el mapa de bits, un mapa de bits es una forma sencilla para llevar un registro de las palabras de la memoria en una cantidad fija de memoria. 4.2.2 Administración de Memoria con Listas Enlazadas 4.2.3 Distribución del Espacio para Intercambio 4.3 Memoria Virtual 4.3.1 Paginación Memoria Virtual En sistemas operativos de computadoras, los sistemas de paginación de memoria dividen los programas en pequeñas partes o páginas. Del mismo modo, la memoria es dividida en trozos del mismo tamaño que las páginas llamados marcos de página. De esta forma, la cantidad de memoria desperdiciada por un proceso es el final de su última página, lo que minimiza la fragmentación interna y evita la externa. En un momento cualquiera, la memoria se encuentra ocupada con páginas de diferentes procesos, mientras que algunos marcos están disponibles para su uso. El sistema operativo mantiene una lista de estos últimos marcos, y una tabla por cada proceso, donde consta en qué marco se encuentra cada página del proceso. De esta forma, las páginas de un proceso pueden no estar contiguamente ubicadas en memoria, y pueden intercalarse con las páginas de otros procesos. En la tabla de páginas de un proceso, se encuentra la ubicación del marco que contiene a cada una de sus páginas. Las direcciones lógicas ahora se forman como un número de página y de un desplazamiento dentro de esa página. El número de página es usado como un índice dentro de la tabla de páginas, y una vez obtenida la dirección real del marco de memoria, se utiliza el desplazamiento para componer la dirección real. Este proceso es realizado en el hardware del computador. De esta forma, cuando un proceso es cargado en memoria, se cargan todas sus páginas en marcos libres y se completa su tabla de páginas. Paginación en memoria virtual El único inconveniente del sistema de paginación pura es que todas las páginas de un proceso deben estar en memoria para que pueda ejecutar. Esto hace que si los programas son de tamaño considerable, no puedan cargarse muchos a la vez, disminuyendo el grado de multiprogramación del sistema. Para evitar esto, y aprovechando el principio de cercanía de referencias donde se puede esperar que un programa trabaje con un conjunto cercano de referencias a memoria (es decir con un conjunto residente más pequeño que el total de sus páginas), se permitirá que algunas páginas del proceso sean guardadas en un espacio de intercambio (en memoria secundaria) mientras no se necesiten. Cuando la paginación se utiliza junto con memoria virtual, el sistema operativo mantiene además el conocimiento sobre qué páginas están en memoria principal y cuáles no, usando la tabla de paginación. Si una página buscada está marcada como no disponible (tal vez porque no está presente en la memoria física, pero sí en el área de intercambio), cuando la CPU intenta referenciar una dirección de memoria en esa página, la MMU responde levantando una excepción (comúnmente llamada fallo de página). Si la página se encuentra en el área de intercambio, se salta a una rutina que invoca una operación llamada un intercambio de página, para traer a memoria principal la página requerida. La operación lleva varios pasos. Primero se selecciona una página en memoria, por ejemplo una que no haya sido usada recientemente (para más detalles ver algoritmos de reemplazo de páginas). Si la página fue modificada, se escribe la misma en el espacio de intercambio. El siguiente paso en el proceso es leer la información en la página necesitada desde el espacio de intercambio). Cuando esto sucede, las tablas para traducción de direcciones virtuales a reales son actualizadas para reflejar los contenidos de la memoria física. Entonces el intercambio de página sale, y el programa que usó la dirección que causó la excepción es vuelto a ejecutar desde el punto en que se dio la misma y continúa como si nada hubiera pasado. También es posible que: una dirección virtual es marcada como no disponible, porque no fue localizada previamente. En estos casos, una página de memoria es localizada y llenada con ceros, la tabla de paginación es modificada para mostrar los cambios y el programa se reinicia como en el otro caso. 4.3.2 Segmentacion Memoria Virtual Es un esquema de manejo de memoria mediante el cual la estructura del programa refleja su división lógica; llevándose a cabo una agrupación lógica de la información en bloques de tamaño variable denominados segmentos. Cada uno de ellos tienen información lógica del programa: subrutina, arreglo, etc. Luego, cada espacio de direcciones de programa consiste de una colección de segmentos, que generalmente reflejan la división lógica del programa. La segmentación permite alcanzar los siguientes objetivos: 1. Modularidad de programas: cada rutina del programa puede ser un bloque sujeto a cambios y recopilaciones, sin afectar por ello al resto del programa. 2. Estructuras de datos de largo variable: ejm. Stack, donde cada estructura tiene su propio tamaño y este puede variar. 3. Protección: se puede proteger los módulos del segmento contra accesos no autorizados. 4. Comparición: dos o más procesos pueden ser un mismo segmento, bajo reglas de protección; aunque no sean propietarios de los mismos. 5. Enlace dinámico entre segmentos: puede evitarse realizar todo el proceso de enlace antes de comenzar a ejecutar un programa. Los enlaces se establecerán solo cuando sea necesario. Ventajas de la segmentación El esquema de segmentación ofrece las siguientes ventajas: * El programador puede conocer las unidades lógicas de su programa, dándoles un tratamiento particular. * Es posible compilar módulos separados como segmentos el enlace entre los segmentos puede suponer hasta tanto se haga una referencia entre segmentos. * Debido a que es posible separar los módulos se hace más fácil la modificación de los mismos. Cambios dentro de un modulo no afecta al resto de los módulos. * Es fácil el compartir segmentos. * Es posible que los segmentos crezcan dinámicamente según las necesidades del programa en ejecución. * Existe la posibilidad de definir segmentos que aun no existan. Así, no se asignara memoria, sino a partir del momento que sea necesario hacer usos del segmento. Un ejemplo de esto, serian los arreglos cuya dimensión no se conoce hasta tanto no se comienza a ejecutar el programa. En algunos casos, incluso podría retardar la asignación de memoria hasta el momento en el cual se referencia el arreglo u otra estructura de dato por primera vez. Desventajas de la segmentación * Hay un incremento en los costos de hardware y de software para llevar a cabo la implantación, así como un mayor consumo de recursos: memoria, tiempo de CPU, etc. * Debido a que los segmentos tienen un tamaño variable se pueden presentar problemas de fragmentación externas, lo que puede ameritar un plan de reubicación de segmentos en memoria principal. * Se complica el manejo de memoria virtual, ya que los discos almacenan la información en bloques de tamaños fijos, mientras los segmentos son de tamaño variable. Esto hace necesaria la existencia de mecanismos más costosos que los existentes para paginación. * Al permitir que los segmentos varíen de tamaño, puede ser necesarios planes de reubicación a nivel de los discos, si los segmentos son devueltos a dicho dispositivo; lo que conlleva a nuevos costos. * No se puede garantizar, que al salir un segmento de la memoria, este pueda ser traído fácilmente de nuevo, ya que será necesario encontrar nuevamente un área de memoria libre ajustada a su tamaño. * La comparticion de segmentos permite ahorrar memoria, pero requiere de mecanismos adicionales da hardware y software. 4.3.3 Algoritmos de Sustitucion de Paginas 4.3.4 Aspectos de Diseño para el Sistema 4.3.5 Liberacion de Paginas Unidad 5 Administracion de Dispositivos de E/S 5.1 Principios de Hardware de I O Funciones del Sistema de Archivos Los usuarios deben poder crear, modificar y borrar archivos. Se deben poder compartir los archivos de una manera cuidadosamente controlada [7, Deitel]. El mecanismo encargado de compartir los archivos debe proporcionar varios tipos de acceso controlado: Ej.: “Acceso de Lectura”, “Acceso de Escritura”, “Acceso de Ejecución”, varias combinaciones de estos, 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 prevenirse contra: La pérdida accidental de información. La 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”. El sistema de archivos debe brindar una interfase 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: Los dispositivos particulares. Dónde serán almacenados los datos. El formato de los datos en los dispositivos. Los medios físicos de la transferencia de datos hacia y desde los dispositivos. Inicio: Fin: El Sistema de Archivos Un “Archivo” es un conjunto de registros relacionados [23, Tanenbaum]. El “Sistema de Archivos” es un componente importante de un S. O. y suele contener [7, Deitel]: “Métodos de acceso” relacionados con la manera de acceder a los datos almacenados en archivos. “Administración de archivos” referida a la provisión de mecanismos para que los archivos sean almacenados, referenciados, compartidos y asegurados. “Administración del almacenamiento auxiliar” para la asignación de espacio a los archivos en los dispositivos de almacenamiento secundario. “Integridad del archivo” para garantizar la integridad de la información del archivo. El sistema de archivos está relacionado especialmente con la administración del espacio de almacenamiento secundario, fundamentalmente con el almacenamiento de disco. Una forma de organización de un sistema de archivos puede ser la siguiente: Se utiliza una “raíz ” para indicar en qué parte del disco comienza el “directorio raíz ”. El “directorio raíz ” apunta a los “directorios de usuarios”. Un “directorio de usuario” contiene una entrada para cada uno de los archivos del usuario. Cada entrada de archivo apunta al lugar del disco donde está almacenado el archivo referenciado. Los nombres de archivos solo necesitan ser únicos dentro de un directorio de usuario dado. El nombre del sistema para un archivo dado debe ser único para el sistema de archivos. En sistemas de archivo “jerárquicos” el nombre del sistema para un archivo suele estar formado como el “nombre de la trayectoria” del directorio raíz al archivo. Inicio: Fin: Archivos Se considerará el punto de vista del usuario. Nombre de los Archivos Las reglas exactas para los nombres de archivos varían de sistema a sistema [23, Tanenbaum]. Algunos sistemas de archivos distinguen entre las letras mayúsculas y minúsculas, mientras que otros no. Muchos S. O. utilizan nombres de archivo con dos partes, separadas por un punto: La parte posterior al punto es la extensión de archivo y generalmente indica algo relativo al archivo, aunque las extensiones suelen ser meras convenciones. Inicio: Fin: Estructura de un Archivo Los archivos se pueden estructurar de varias maneras, las más comunes son [23, Tanenbaum]: “Secuencia de bytes”: El archivo es una serie no estructurada de bytes. Posee máxima flexibilidad. El S. O. no ayuda pero tampoco estorba. “Secuencia de registros”: El archivo es una secuencia de registros de longitud fija, cada uno con su propia estructura interna. “Árbol ”: El archivo consta de un árbol de registros, no necesariamente de la misma longitud. Cada registro tiene un campo key (llave o clave) en una posición fija del registro. El árbol se ordena mediante el campo de clave para permitir una rápida búsqueda de una clave particular. Inicio: Fin: Tipos de Archivos Muchos S. O. soportan varios tipos de archivos, por ej.: archivos regulares, directorios, archivos especiales de caracteres, archivos especiales de bloques, etc., donde [23, Tanenbaum]: Los Archivos Regulares son aquellos que contienen información del usuario. Los Directorios son archivos de sistema para el mantenimiento de una estructura del sistema de archivos. Los Archivos Especiales de Caracteres: Tienen relación con la e / s. Se utilizan para modelar dispositivos seriales de e / s (terminales, impresoras, redes, etc.). Los Archivos Especiales de Bloques se utilizan para modelar discos. Inicio: Fin: Acceso a un Archivo Los tipos de acceso más conocidos son: Acceso Secuencial: el proceso lee en orden todos los registros del archivo comenzando por el principio, sin poder: Saltar registros. Leer en otro orden. Acceso Aleatorio: el proceso puede leer los registros en cualquier orden utilizando dos métodos para determinar el punto de inicio de la lectura: Cada operación de lectura (read) da la posición en el archivo con la cual iniciar. Una operación especial (seek) establece la posición de trabajo pudiendo luego leerse el archivo secuencialmente. Inicio: Fin: Atributos de Archivo Cada archivo tiene: Su nombre y datos. Elementos adicionales llamados atributos, que varían considerablemente de sistema a sistema. Algunos de los posibles atributos de archivo son [23, Tanenbaum]: “Protección”: quién debe tener acceso y de qué forma. “Contraseña”: contraseña necesaria para acceder al archivo. “Creador”: identificador de la persona que creó el archivo. “Propietario”: propietario actual. “Bandera exclusivo - para - lectura”: 0 lectura / escritura, 1 para lectura exclusivamente. “Bandera de ocultamiento”: 0 normal, 1 para no exhibirse en listas. “Bandera de sistema”: 0 archivo normal, 1 archivo de sistema. “Bandera de biblioteca”: 0 ya se ha respaldado, 1 necesita respaldo. “Bandera ascii / binario”: 0 archivo en ascii, 1 archivo en binario. “Bandera de acceso aleatorio”: 0 solo acceso secuencial, 1 acceso aleatorio. “Bandera temporal”: 0 normal, 1 eliminar al salir del proceso. “Banderas de cerradura”: 0 no bloqueado, distinto de 0 bloqueado. “Longitud del registro”: número de bytes en un registro. “Posición de la llave”: ajuste de la llave dentro de cada registro. “Longitud de la llave”: número de bytes en el campo llave. “Tiempo de creación”: fecha y hora de creación del archivo. “Tiempo del último acceso”: fecha y hora del último acceso al archivo. “Tiempo de la última modificación”: fecha y hora de la última modificación al archivo. “Tamaño actual”: número de bytes en el archivo. “Tamaño máximo”: tamaño máximo al que puede crecer el archivo. Inicio: Fin: Operaciones con Archivos Las llamadas más comunes al sistema relacionadas con los archivos son [23, Tanenbaum]: Create (crear): el archivo se crea sin datos. Delete (eliminar): si el archivo ya no es necesario debe eliminarse para liberar espacio en disco. Ciertos S. O. eliminan automáticamente un archivo no utilizado durante “n” días. Open (abrir): antes de utilizar un archivo, un proceso debe abrirlo. La finalidad es permitir que el sistema traslade los atributos y la lista de direcciones en disco a la memoria principal para un rápido acceso en llamadas posteriores. Close (cerrar): cuando concluyen los accesos, los atributos y direcciones del disco ya no son necesarios, por lo que el archivo debe cerrarse y liberar la tabla de espacio interno. Read (leer): los datos se leen del archivo; quien hace la llamada debe especificar la cantidad de datos necesarios y proporcionar un buffer para colocarlos. Write (escribir): los datos se escriben en el archivo, en la posición actual. El tamaño del archivo puede aumentar (agregado de registros) o no (actualización de registros). Append (añadir): es una forma restringida de “write”. Solo puede añadir datos al final del archivo. Seek (buscar): especifica el punto donde posicionarse. Cambia la posición del apuntador a la posición activa en cierto lugar del archivo. Get attributes (obtener atributos): permite a los procesos obtener los atributos del archivo. Set attributes (establecer atributos): algunos atributos pueden ser determinados por el usuario y modificados luego de la creación del archivo. La información relativa al modo de protección y la mayoría de las banderas son un ejemplo obvio. Rename (cambiar de nombre): permite modificar el nombre de un archivo ya existente. Inicio: Fin: Archivos Mapeados a Memoria Algunos S. O. permiten asociar los archivos con un espacio de direcciones de un proceso en ejecución [23, Tanenbaum]. Se utilizan las llamadas al sistema “map” y “unmap”: “Map”: utiliza un nombre de archivo y una dirección virtual y hace que el S. O. asocie al archivo con la dirección virtual en el espacio de direcciones, por lo cual las lecturas o escrituras de las áreas de memoria asociadas al archivo se efectúan también sobre el archivo mapeado. “Unmap”: elimina los archivos del espacio de direcciones y concluye la operación de asociación. El mapeo de archivos elimina la necesidad de programar la e / s directamente, facilitando la programación. Los principales problemas relacionados son: Imposibilidad de conocer a priori la longitud del archivo de salida, el que podría superar a la memoria. Dificultad para compartir los archivos mapeados evitando inconsistencias, ya que las modificaciones hechas en las páginas no se verán reflejadas en el disco hasta que dichas páginas sean eliminadas de la memoria. Inicio: Fin: Directorios Generalmente son utilizados por los S. O. para llevar un registro de los archivos [23, Tanenbaum]. En muchos sistemas son a su vez también archivos. Sistemas Jerárquicos de Directorios El directorio contiene un conjunto de datos por cada archivo referenciado. Una posibilidad es que el directorio contenga por cada archivo referenciado [7, Deitel]: El nombre. Sus atributos. Las direcciones en disco donde se almacenan los datos. Otra posibilidad es que cada entrada del directorio contenga: El nombre del archivo. Un apuntador a otra estructura de datos donde se encuentran los atributos y las direcciones en disco. Al abrir un archivo el S. O.: Busca en su directorio el nombre del archivo. Extrae los atributos y direcciones en disco. Graba esta información en una tabla de memoria real. Todas las referencias subsecuentes al archivo utilizarán la información de la memoria principal. El número y organización de directorios varía de sistema en sistema: Directorio único: el sistema tiene un solo directorio con todos los archivos de todos los usuarios (ver Figura 4.1 [23, Tanenbaum]). Un directorio por usuario: el sistema habilita un solo directorio por cada usuario (ver Figura 4.2 [23, Tanenbaum]). Un árbol de directorios por usuario: el sistema permite que cada usuario tenga tantos directorios como necesite, respetando una jerarquía general (ver Figura 4.3 [23, Tanenbaum]). Inicio: Fin: Nombre de las Rutas de Acceso Cuando el sistema de archivos está organizado como un árbol de directorios se necesita una forma de determinar los nombres de los archivos. Los principales métodos para nombres de los archivos son [23, Tanenbaum]: 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 carácter llamado “separador”. Ruta de Acceso Relativa: Se utiliza junto con el concepto de directorio de trabajo o directorio activo. 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. Inicio: Fin: Operaciones con Directorios Las llamadas al sistema permitidas para el manejo de los directorios tienen variación de sistema a sistema [23, Tanenbaum]. Las más comunes son las siguientes: Create (crear): se crea un directorio vacío. Delete (eliminar): se elimina un directorio, que debe estar vacío. Opendir (abrir directorio): se pueden leer los directorios: Antes de poder leer un directorio, éste debe ser abierto. Closedir (cerrar directorio): cuando se ha leído un directorio, éste debe ser cerrado para liberar el espacio correspondiente de la tabla interna. Readdir (leer directorio): regresa la siguiente entrada en un directorio abierto, sin importar el tipo de estructura de directorios que se utilice. Rename (cambiar de nombre): cambia el nombre de un directorio de manera similar al cambio para archivos. Link (ligar): es una técnica que permite que un archivo aparezca en más de un directorio: Especifica un archivo existente y el nombre de una ruta de acceso. Crea un enlace del archivo ya existente con el nombre especificado en la ruta de acceso. Unlink (desligar): se elimina una entrada del directorio: Si el archivo que se desea desligar aparece solo en un directorio (el caso normal): Se elimina del sistema de archivos. Si el archivo que se desea desligar, está presente en varios directorios: Solo se elimina la ruta de acceso especificada. Las demás rutas permanecen. Inicio: Fin: Implantación del Sistema de Archivos y sus Relaciones con la Asignación y Liberación de Espacio Se consideran aspectos tales como [7, Deitel]: La forma de almacenamiento de archivos y directorios. La administración del espacio en disco. La forma de hacerlo de manera eficiente y confiable. Se deben tener presentes problemas tales como la “fragmentación” creciente del espacio en disco: Ocasiona problemas de performance al hacer que los archivos se desperdiguen a través de bloques muy dispersos. Una técnica para aliviar el problema de la “fragmentación” consiste en realizar periódicamente: “Condensación”: se pueden “reorganizar” los archivos expresamente o automáticamente según algún criterio predefinido. “Recolección de basura o residuos”: se puede hacer fuera de línea o en línea, con el sistema activo, según la implementación. Inicio: Fin: Implantación de Archivos El aspecto clave de la implantación del almacenamiento de archivos es el registro de los bloques asociados a cada archivo [7, Deitel]. Algunos de los métodos utilizados son los siguientes: Asignación contigua o adyacente: Los archivos son asignados a áreas contiguas de almacenamiento secundario. Las principales ventajas son: Facilidad de implantación, ya que solo se precisa el número del bloque de inicio para localizar un archivo. Rendimiento excelente respecto de la e / s. Los principales defectos son: Se debe conocer el tamaño máximo del archivo al crearlo. Produce una gran fragmentación de los discos. Asignación no contigua: Son esquemas de almacenamiento más dinámicos, destacándose los siguientes: Asignación encadenada orientada hacia el sector: El disco se considera compuesto de sectores individuales. Los archivos constan de varios sectores que pueden estar dispersos por todo el disco. Los sectores que pertenecen a un archivo común contienen apuntadores de uno a otro formando una “lista encadenada”. Una “lista de espacio libre” contiene entradas para todos los sectores libres del disco. Las ampliaciones o reducciones en el tamaño de los archivos se resuelven actualizando la “lista de espacio libre” y no hay necesidad de condensación. Las principales desventajas son: Debido a la posible dispersión en el disco, la recuperación de registros lógicamente contiguos puede significar largas búsquedas. El mantenimiento de la estructura de “listas encadenadas” significa una sobrecarga en tiempo de ejecución. Los apuntadores de la estructura de lista consumen espacio en disco. Asignación por bloques: Es más eficiente y reduce la sobrecarga en ejecución. Es una mezcla de los métodos de asignación contigua y no contigua. Se asignan bloques de sectores contiguos en vez de sectores individuales. El sistema trata de asignar nuevos bloques a un archivo eligiendo bloques libres lo más próximos posible a los bloques del archivo existentes. Las formas más comunes de implementar la asignación por bloques son: Encadenamiento de bloques. Encadenamiento de bloques de índice. Transformación de archivos orientada hacia bloques. Encadenamiento de bloques o lista ligada: Las entradas en el directorio de usuarios apuntan al primer bloque de cada archivo. Cada uno de los bloques de longitud fija que forman un archivo contiene dos partes: Un bloque de datos. Un apuntador al bloque siguiente. Cada bloque contiene varios sectores. Frecuentemente el tamaño de un bloque se corresponde con el de una pista completa del disco. Localizar un registro determinado requiere: Buscar en la cadena de bloques hasta encontrar el bloque apropiado. Buscar en el bloque hasta encontrar el registro. El examen de la cadena desde el principio puede ser lento ya que debe realizarse de bloque en bloque, y pueden estar dispersos por todo el disco. La inserción y el retiro son inmediatos, dado que se deben modificar los apuntadores del bloque precedente. Se pueden usar “listas de encadenamiento doble”, hacia adelante y hacia atrás, con lo que se facilita la búsqueda (ver Figura 4.4 [23, Tanenbaum]). Encadenamiento de bloques de índices: Los apuntadores son colocados en varios bloques de índices separados: Cada bloque de índices contiene un número fijo de elementos. Cada entrada contiene un identificador de registros y un apuntador a ese registro. Si es necesario utilizar más de un bloque de índices para describir un archivo, se encadena una serie de bloques de índices. La gran ventaja es que la búsqueda puede realizarse en los propios bloques de índices. Los bloques de índices pueden mantenerse juntos en el almacenamiento secundario para acortar la búsqueda, pero para mejor performance podrían mantenerse en el almacenamiento primario. La principal desventaja es que las inserciones pueden requerir la reconstrucción completa de los bloques de índices: Una posibilidad es dejar vacía una parte de los bloques de índices para facilitar inserciones futuras y retardar las reconstrucciones. Es suficiente que el dato del directorio contenga el número de bloque inicial para localizar todos los bloques restantes, sin importar el tamaño del archivo (ver Figura 4.5 [7, Deitel]). Transformación de archivos orientada hacia bloques: Se utilizan números de bloques en vez de apuntadores. Los números de bloques se convierten fácilmente a direcciones de bloques gracias a la geometría del disco. Se conserva un mapa del archivo, conteniendo una entrada para cada bloque del disco. Las entradas en el directorio del usuario apuntan a la primera entrada al mapa del archivo para cada archivo. Cada entrada al mapa del archivo contiene el número del bloque siguiente de ese archivo. La entrada al mapa del archivo correspondiente a la última entrada de un archivo determinado se ajusta a algún valor “centinela” (“nil”) para indicar que se alcanzó el último bloque de un archivo. El sistema puede mantener una lista de bloques libres. La principal ventaja es que las cercanías físicas del disco se reflejan en el mapa del archivo (ver Figura 4.6 [7, Deitel]). Nodos-i (nodos índices): Se asocia a cada archivo una pequeña tabla, llamada nodo-i (nodo índice): Contiene los atributos y direcciones en disco de los bloques del archivo. Se traslada del disco a la memoria principal al abrir el archivo. En rigor, almacena solo las primeras direcciones en disco: o Si el archivo es pequeño, toda la información está en el nodo-i. o Si el archivo es grande, una de las direcciones en el nodo-i es la dirección de un bloque en el disco llamado bloque simplemente indirecto: Contiene las direcciones en disco adicionales. Si resulta insuficiente, otra dirección en el nodo-i, el bloque doblemente indirecto, contiene la dirección de un bloque que presenta una lista de los bloques simplemente indirectos: Cada bloque simplemente indirecto apunta a un grupo de bloques de datos. De ser necesario se pueden utilizar bloques triplemente indirectos (ver Figura 4.7 [23, Tanenbaum]). Inicio: Fin: Implantación de Directorios Para abrir un archivo el S. O. utiliza información del directorio: El directorio contiene la información necesaria para encontrar los bloques en el disco. El tipo de información varía según el sistema. La principal función del sistema de directorios es asociar el nombre del archivo con 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 es almacenarlos en forma directa dentro del dato del directorio. Otra posibilidad es almacenar los atributos en el nodo-i en vez de utilizar la entrada del directorio. Inicio: Fin: Archivos Compartidos Frecuentemente conviene que los archivos compartidos simultáneamente en distintos directorios de distintos usuarios. aparezcan El propio sistema de archivos es una gráfica dirigida acíclica en vez de un árbol [23, Tanenbaum]. La conexión entre un directorio y un archivo de otro directorio al cual comparten se denomina enlace. Si los directorios realmente contienen direcciones en disco: Se debe tener una copia de las direcciones en disco en el directorio que accede al archivo compartido al enlazar el archivo. Se debe evitar que los cambios hechos por un usuario a través de un directorio no sean visibles por los demás usuarios, para lo que se consideraran dos soluciones posibles. Primer solución: Los bloques del disco no se enlistan en los directorios, sino en una pequeña estructura de datos asociada al propio archivo. Los directorios apuntarían solo a esa pequeña estructura de datos, que podría ser el nodo-i. Segunda solución: El enlace se produce haciendo que el sistema cree un nuevo archivo de tipo “link”. El archivo “link”: Ingresa al directorio del usuario que accede a un archivo de otro directorio y usuario. Solo contiene el nombre de la ruta de acceso del archivo al cual se enlaza. Este criterio se denomina enlace simbólico. Desventajas de la primer solución: La creación de un enlace: No modifica la propiedad respecto de un archivo. Aumenta el contador de enlaces del nodo-i: El sistema sabe el número de entradas de directorio que apuntan en cierto momento al archivo. Si el propietario inicial del archivo intenta eliminarlo, surge un problema para el sistema: Si elimina el archivo y limpia el nodo-i, el directorio que enlazo al archivo tendrá una entrada que apunta a un nodo-i no válido. Si el nodo-i se reasigna a otro archivo el enlace apuntará al archivo incorrecto. El sistema: Puede ver por medio del contador de enlaces en el nodo-i que el archivo sigue utilizándose. No puede localizar todas las entradas de directorio asociadas a ese archivo para eliminarlas. La solución podría ser: Eliminar la entrada del directorio inicialmente propietario del archivo. Dejar intacto el nodo-i: Se daría el caso que el directorio que posee el enlace es el único que posee una entrada de directorio para un archivo de otro directorio, para el cual dicho archivo ya no existe. Esto no ocurre con los enlaces simbólicos ya que solo el propietario verdadero tiene un apuntador al nodo-i: Los usuarios enlazados al archivo solo tienen nombres de rutas de acceso y no apuntadores a nodo-i. Cuando el propietario elimina un archivo, este se destruye. Desventajas de la segunda solución: El principal problema es su costo excesivo, especialmente en accesos a disco, puesto que se debe leer el archivo que contiene la ruta de acceso, analizarla y seguirla componente a componente hasta alcanzar el nodo-i. Se precisa un nodo-i adicional por cada enlace simbólico y un bloque adicional en disco para almacenar la ruta de acceso. Los archivos pueden tener dos o más rutas de acceso, debido a lo cual, en búsquedas genéricas se podría encontrar el mismo archivo por distintas rutas y tratárselo como si fueran archivos distintos. Los enlaces simbólicos tienen la ventaja de que se pueden utilizar para enlazar archivos en otras máquinas, en cualquier parte del mundo; se debe proporcionar solo la dirección de la red de la máquina donde reside el archivo y su ruta de acceso en esa máquina. Inicio: Fin: Administración del Espacio en Disco Existen dos estrategias generales para almacenar un archivo de “n” bytes [23, Tanenbaum]: Asignar “n” bytes consecutivos de espacio en el disco: Tiene el problema de que si un archivo crece será muy probable que deba desplazarse en el disco, lo que puede afectar seriamente al rendimiento. Dividir el archivo en cierto número de bloques (no necesariamente) adyacentes: Generalmente los sistemas de archivos utilizan esta estrategia con bloques de tamaño fijo. Tamaño del bloque: Dada la forma en que están organizados los bloques, el sector, la pista y el cilindro son los candidatos obvios como unidades de asignación. Si se tiene una unidad de asignación grande, como un cilindro, esto significa que cada archivo, inclusive uno pequeño, ocupará todo un cilindro; con esto se desperdicia espacio de almacenamiento en disco. Si se utiliza una unidad de asignación pequeña, como un sector, implica que cada archivo constará de muchos bloques; con esto su lectura generará muchas operaciones de e / s afectando la performance. Lo anterior indica que la eficiencia en tiempo y espacio tienen un conflicto inherente. Generalmente se utilizan como solución de compromiso bloques de 1/2 k, 1k, 2k o 4k. (ver Figura 4.8 [23, Tanenbaum]). Hay que recordar que el tiempo de lectura de un bloque de disco es la suma de los tiempos de: Búsqueda. Demora rotacional. Transferencia. Registro de los bloques libres: Se utilizan por lo general dos métodos: La lista de bloques libres como lista ligada. Un mapa de bits. Lista ligada de bloques de disco: Cada bloque contiene tantos números de bloques libres como pueda. Los bloques libres se utilizan para contener a la lista de bloques libres. Mapa de bits: Un disco con “n” bloques necesita un mapa de bits con “n” bits. Los bloques libres se representa con “1” y los asignados con “0” (o viceversa). Generalmente este método es preferible cuando existe espacio suficiente en la memoria principal para contener completo el mapa de bits. Disk quotas: Para evitar que los usuarios se apropien de un espacio excesivo en disco, los S. O. multiusuario proporcionan generalmente un mecanismo para establecer las cuotas en el disco. La idea es que: Un administrador del sistema asigne a cada usuario una proporción máxima de archivos y bloques. El S. O. garantice que los usuarios no excedan sus cuotas. Un mecanismo utilizado es el siguiente: Cuando un usuario abre un archivo: Se localizan los atributos y direcciones en disco. Se colocan en una tabla de archivos abiertos en la memoria principal. Uno de los atributos indica el propietario del archivo; cualquier aumento del tamaño del archivo se carga a la cuota del propietario. Una segunda tabla contiene el registro de las cuotas para cada uno de los usuarios que tengan un archivo abierto en ese momento, aún cuando el archivo lo haya abierto otro usuario. Cuando se escribe una nueva entrada en la tabla de archivos abiertos: Se introduce un apuntador al registro de la cuota del propietario para localizar los límites. Cuando se añade un bloque a un archivo: Se incrementa el total de bloques cargados al propietario. Se verifica este valor contra los límites estricto y flexible (el primero no se puede superar, el segundo sí). También se verifica el número de archivos. Inicio: Fin: Confiabilidad del Sistema de Archivos Es necesario proteger la información alojada en el sistema de archivos, efectuando los resguardos correspondientes [23, Tanenbaum]. De esta manera se evitan las consecuencias generalmente catastróficas de la pérdida de los sistemas de archivos. Las pérdidas se pueden deber a problemas de hardware, software, hechos externos, etc. Manejo de un bloque defectuoso: 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 por primera vez: Lee la “lista de bloques defectuosos”. Elige un bloque (o pista) de reserva para reemplazar los defectuosos. Registra la asociación en la lista de bloques defectuosos. En lo sucesivo, las solicitudes del bloque defectuoso utilizarán el de repuesto. 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”. Se crea un “archivo de bloques defectuosos”: Esta constituido por los bloques defectuosos. No debe ser leído ni escrito. 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. 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: Se desperdicia la mitad del almacenamiento de datos en disco para respaldo. 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 : Se obtiene una copia de respaldo periódicamente (por ej.: 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. 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. 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. 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. Generalmente los utilitarios utilizan dos tablas: Tabla de bloques en uso. Tabla de bloques libres. Cada bloque debe estar referenciado en una de ellas. Si un bloque no aparece en ninguna de las tablas se trata de una falla llamada bloque faltante: No produce daños pero desperdicia espacio en disco. Se soluciona añadiendo el bloque a la tabla de bloques libres. También podría detectarse la situación de falla debida a un bloque referenciado dos veces en la tabla de bloques libres: Esta falla no se produce en los sistemas de archivos basados en mapas de bits, sí en los basados en tablas o listas. La solución consiste en depurar la tabla de bloques libres. Una falla muy grave es que el mismo bloque de datos aparezca referenciado dos o más veces en la tabla de bloques en uso: Como parte del mismo o de distintos archivos. Si uno de los archivos se borra, el bloque aparecería en la tabla de bloques libres y también en la de bloques en uso. Una solución es que el verificador del sistema de archivos: Asigne un bloque libre. Copie en el bloque libre el contenido del bloque conflictivo. Actualice las tablas afectando el bloque copia a alguno de los archivos. Agregue el bloque conflictivo a la tabla de bloques libres. Informe al usuario para que verifique el daño detectado y la solución dada. Otro error posible es que un bloque esté en la tabla de bloques en uso y en la tabla de bloques libres: Se soluciona eliminándolo de la tabla de bloques libres. Las verificaciones de directorios incluyen controles como: Número de directorios que apuntan a un nodo-i con los contadores de enlaces almacenados en los propios nodos-i; en un sistema consistente de archivos deben coincidir. Una posible falla es que el contador de enlaces sea mayor que el número de entradas del directorio: Aunque se eliminaran todos los archivos de los directorios el contador sería distinto de cero y no se podría eliminar el nodo-i. No se trata de un error serio pero produce desperdicio de espacio en disco con archivos que no se encuentran en ningún directorio. Se soluciona haciendo que el contador de enlaces en el nodo-i tome el valor correcto; si el valor correcto es 0, el archivo debe eliminarse. Otro tipo de error es potencialmente catastrófico: Si dos entradas de un directorio se enlazan a un archivo, pero el nodo-i indica que solo existe un enlace, entonces, al eliminar cualquiera de estas entradas de directorio, el contador del nodo-i tomará el valor 0. Debido al valor 0 el sistema de archivos lo señala como no utilizado y libera todos sus bloques. Uno de los directorios apunta hacia un nodo-i no utilizado, cuyos bloques se podrían asignar entonces a otros archivos. La solución es forzar que el contador de enlaces del nodo-i sea igual al número de entradas del directorio. También se pueden hacer verificaciones heurísticas, por ej.: Cada nodo-i tiene un modo, pero algunos modos son válidos aunque extraños: Ej.: Se prohibe el acceso al propietario y todo su grupo, pero se permite a los extraños leer, escribir y ejecutar el archivo. La verificación debería detectar e informar de estas situaciones. Se debería informar como sospechosos aquellos directorios con excesivas entradas, por ej., más de mil. Inicio: Fin: Desempeño del Sistema de Archivos El acceso al disco es mucho más lento que el acceso a la memoria: Los tiempos se miden en milisegundos y en nanosegundos respectivamente. Se debe reducir el número de accesos a disco. La técnica más común para reducir los accesos a disco es el bloque caché o buffer caché[23, Tanenbaum]: Se utiliza el término ocultamiento para esta técnica (del francés “cacher”: ocultar). Un caché es una colección de bloques que pertenecen desde el punto de vista lógico al disco, pero que se mantienen en 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: ¿ Es posible que el bloque modificado se vuelva a necesitar muy pronto ?: 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 ?: Si es esencial (generalmente lo será si no es bloque de datos) y ha sido modificado, debe escribirse en el disco de inmediato: Se reduce la probabilidad de que una falla total del sistema haga naufragar al sistema de archivos. Se debe elegir con cuidado el orden de escritura de los bloques críticos. 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 (por ej. 30). Otra solución consiste en escribir los bloques modificados (del caché) al disco, tan pronto como haya sido escrito (el caché): Se dice que se trata de cachés de escritura. Requiere más e / s que otros tipos de cachés. 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. Inicio: Fin: Descriptor de Archivos El descriptor de archivos o bloque de control de archivos es un bloque de control que contiene información que el sistema necesita para administrar un archivo [7, Deitel]. Es una estructura muy dependiente del sistema. Puede incluir la siguiente información: Nombre simbólico del archivo. Localización del archivo en el almacenamiento secundario. Organización del archivo (método de organización y acceso). Tipo de dispositivo. Datos de control de acceso. Tipo (archivo de datos, programa objeto, programa fuente, etc.). Disposición (permanente contra temporal). Fecha y tiempo de creación. Fecha de destrucción. Fecha de la última modificación. Suma de las actividades de acceso (número de lecturas, por ejemplo). Los descriptores de archivos suelen mantenerse en el almacenamiento secundario; se pasan al almacenamiento primario al abrir el archivo. El descriptor de archivos es controlado por el sistema de archivos; el usuario puede no hacer referencia directa a él. Inicio: Fin: Seguridad Los sistemas de archivos generalmente contienen información muy valiosa para sus usuarios, razón por la que los sistemas de archivos deben protegerla [23, Tanenbaum]. El Ambiente de Seguridad Se entenderá por seguridad a los problemas generales relativos a la garantía de que los archivos no sean leídos o modificados por personal no autorizado; esto incluye aspectos técnicos, de administración, legales y políticos. Se consideraran mecanismos de protección a los mecanismos específicos del sistema operativo utilizados para resguardar la información de la computadora. La frontera entre seguridad y mecanismos de protección no está bien definida. Dos de las más importantes facetas de la seguridad son: La pérdida de datos. Los intrusos. Algunas de las causas más comunes de la pérdida de datosson: Actos y hechos diversos, como incendios, inundaciones, terremotos, guerras, revoluciones, roedores, etc. Errores de hardware o de software, como fallas en la cpu, discos o cintas ilegibles, errores de telecomunicación, errores en los programas, etc. Errores humanos, por ej., entrada incorrecta de datos, mal montaje de cintas o discos, ejecución incorrecta de programas, pérdida de cintas o discos, etc. La mayoría de estas causas se pueden enfrentar con el mantenimiento de los respaldos (back-ups) adecuados; debería haber copias en un lugar alejado de los datos originales. Respecto del problema de los intrusos, se los puede clasificar como: Pasivos: solo desean leer archivos que no están autorizados a leer. Activos: desean hacer cambios no autorizados a los datos. Para diseñar un sistema seguro contra intrusos: Hay que tener en cuenta el tipo de intrusos contra los que se desea tener protección. Hay que ser consciente de que la cantidad de esfuerzo que se pone en la seguridad y la protección depende claramente de quién se piensa sea el enemigo. Algunos tipos de intrusos son los siguientes: Curiosidad casual de usuarios no técnicos. Conocidos (técnicamente capacitados) husmeando. Intentos deliberados por hacer dinero. Espionaje comercial o militar. Otro aspecto del problema de la seguridad es la privacía: Protección de las personas respecto del mal uso de la información en contra de uno mismo. Implica aspectos legales y morales. También debe señalarse la posibilidad del ataque del caballo de Troya: Modificar un programa normal para que haga cosas adversas además de su función usual. Arreglar las cosas para que la víctima utilice la versión modificada. Además debe considerarse la posibilidad de ataques al estilo del gusano de Internet: Fue liberado por Robert Tappan Morris el 02/11/88 e hizo que se bloquearan la mayoría de los sistemas Sun y Vax de Internet (fue descubierto y condenado). Constaba de un programa arrancador y del gusano propiamente dicho. Utilizaba fallas se seguridad del Unix y de los programas Finger y Sendmail de Internet. Una forma de probar la seguridad de un sistema es contratar un grupo de expertos en seguridad, conocido como el equipo tigre o equipo de penetración, cuyo objetivo es intentar penetrar el sistema de seguridad para descubrir sus falencias y proponer soluciones. Otro aspecto importante de la seguridad consiste en no subestimar los problemas que puede causar el personal. Inicio: Fin: Virus Los virus computacionales: Constituyen una categoría especial de ataque. Son un enorme problema para muchos usuarios. Son fragmentos de programas que se añaden a programas legítimos con la intención de infectar a otros. Un virus difiere de un gusano en lo siguiente: Un virus está a cuestas de un programa existente. Un gusano es un programa completo en sí mismo. Los virus y los gusanos intentan diseminarse y pueden crear un daño severo. Generalmente se propagan a través de copias ilegítimas de programas. Comúnmente los virus se ejecutan e intentan reproducirse cada vez que se ejecuta el programa que los aloja. Frecuentemente los problemas con los virus son más fáciles de evitar que de curar: Utilizar software original adquirido en comercios respetables. No utilizar copias “piratas”. Efectuar controles rigurosos y frecuentes con programas antivirus actualizados. Trabajar con metodología y disciplina rigurosa en el intercambio de discos y en las copias a través de redes de comunicación de datos. Inicio: Fin: Principios del Diseño Para la Seguridad El diseño del sistema debe ser público, ya que pensar que el intruso no conocerá la forma de funcionamiento del sistema es un engaño. El estado predefinido debe ser el de no acceso, dado que los errores en donde se niega el acceso válido se reportan más rápido que los errores en donde se permite el acceso no autorizado. Verificar la autorización actual : El sistema no debe: Verificar el permiso. Determinar que el acceso está permitido. Abandonar esta información para su uso posterior. El sistema tampoco debe: Verificar el permiso al abrir un archivo y no después de abrirlo, pues un acceso habilitado permanecería como válido aunque haya cambiado la protección del archivo. Dar a cada proceso el mínimo privilegio posible, lo que implica un esquema de “protección de grano fino”. El mecanismo de protección debe ser simple, uniforme e integrado hasta las capas más bajas del sistema: Dotar de seguridad a un sistema inseguro es casi imposible. La seguridad no es una característica que se pueda añadir fácilmente. El esquema de seguridad debe ser sicológicamente aceptable: Los usuarios no deben sentir que la protección de sus archivos les implica demasiado trabajo: Podrían dejar de proteger sus archivos. Se quejarían en caso de problemas. No aceptarían fácilmente su propia culpa. Inicio: Fin: Autentificación del Usuario Muchos esquemas de protección se basan en la hipótesis de que el sistema conoce la identidad de cada usuario. La identificación de los usuarios se conoce como la autentificación de los usuarios. Muchos métodos de autentificación se basan en: La identificación de algo conocido por el usuario. Algo que posee el usuario. Algo que es el usuario. Inicio: Fin: Contraseñas Son la forma de autentificación más utilizada. Son de fácil comprensión e implementación. Deben almacenarse cifradas (encriptadas). Se deben prever intentos de penetración consistentes en pruebas de combinaciones de nombres y contraseñas. Si las contraseñas fueran de 7 caracteres elegidos al azar de los 95 caracteres ASCII que se pueden imprimir: El espacio de búsqueda sería de 95 7 , alrededor de 7 x 10 13 . A 1.000 ciframientos por segundo tomaría 2.000 años construir la lista a verificar contra el archivo de contraseñas. Una mejora al esquema de contraseñas consiste en: Asociar un número aleatorio de “n” bits a cada contraseña. El número aleatorio se modifica al cambiar la contraseña. El número se guarda en el archivo de contraseñas en forma no cifrada. Se concatenan la contraseña y el número aleatorio y se cifran juntos. El resultado cifrado se almacena en el archivo de contraseñas. Se aumenta por 2 n el espectro de búsqueda: a esto se llama salar el archivo de contraseñas. Una protección adicional consiste en hacer ilegible el archivo de contraseñas encriptadas. Otra protección adicional consiste en que el sistema sugiera a los usuarios contraseñas generadas según ciertos criterios; con esto se evita que el usuario elija contraseñas muy sencillas. También es conveniente que el sistema obligue al usuario a cambiar sus contraseñas con regularidad; se puede llegar a la contraseña de una sola vez. Una variante de la idea de contraseña es solicitar al usuario respuestas sobre información de contexto que debe conocer. Otra variante es la de reto-respuesta: Se acuerdan con el usuario algoritmos (por ejemplo formulas matemáticas) que se utilizarán según el día y / o la hora. Cuando el usuario se conecta: El sistema suministra un argumento. El usuario debe responder con el resultado correspondiente al algoritmo vigente ese día a esa hora. Inicio: Fin: Identificación Física Una posibilidad es la verificación de si el usuario tiene cierto elemento (generalmente una tarjeta plástica con una banda magnética), que generalmente se combina con una contraseña. Otro aspecto consiste en la medición de características físicas difíciles de reproducir: Huellas digitales o vocales. Firmas. Longitud de los dedos de las manos. Inicio: Fin: Medidas Preventivas Limitar los intentos de acceso fallidos y registrarlos. Registrar todos los accesos. Tender trampas para atrapar a los intrusos. Inicio: Fin: Mecanismos de Protección Dominios de Protección Muchos objetos del sistema necesitan protección, tales como la cpu, segmentos de memoria, unidades de disco, terminales, impresoras, procesos, archivos, bases de datos, etc. [23, Tanenbaum]. Cada objeto se referencia por un nombre y tiene habilitadas un conjunto de operaciones sobre él. Un dominio es un conjunto de parejas (objeto, derechos): Cada pareja determina: Un objeto. Un subconjunto de las operaciones que se pueden llevar a cabo en él. Un derecho es el permiso para realizar alguna de las operaciones. Es posible que un objeto se encuentre en varios dominios con “distintos” derechos en cada dominio. Un proceso se ejecuta en alguno de los dominios de protección: Existe una colección de objetos a los que puede tener acceso. Cada objeto tiene cierto conjunto de derechos. Los procesos pueden alternar entre los dominios durante la ejecución. Una llamada al S. O. provoca una alternancia de dominio. En algunos S. O. los dominios se llaman anillos. Una forma en la que el S. O. lleva un registro de los objetos que pertenecen a cada dominio es mediante una matriz : Los renglones son los dominios. Las columnas son los objetos. Cada elemento de la matriz contiene los derechos correspondientes al objeto en ese dominio, por ej.: leer, escribir, ejecutar. Inicio: Fin: Listas Para Control de Acceso Las “matrices de protección” son muy grandes y con muchos lugares vacíos [23, Tanen-baum]: Desperdician espacio de almacenamiento. Existen métodos prácticos que almacenan solo los elementos no vacíos por filas o por columnas. La lista de control de acceso (ACL: access control list): Asocia a cada objeto una lista ordenada con: Todos los dominios que pueden tener acceso al objeto. La forma de dicho acceso (ej: lectura ®, grabación (w), ejecución (x)). Una forma de implementar las ACL consiste en: Asignar tres bits (r, w, x) para cada archivo, para: El propietario, el grupo del propietario y los demás usuarios. Permitir que el propietario de cada objeto pueda modificar su ACL en cualquier momento: Permite prohibir accesos antes permitidos. Inicio: Fin: Posibilidades La matriz de protección también puede dividirse por renglones [23, Tanenbaum]: Se le asocia a cada proceso una lista de objetos a los cuales puede tener acceso. Se le indican las operaciones permitidas en cada uno. Esto define su dominio. La lista de objetos se denomina lista de posibilidades y los elementos individuales se llaman posibilidades. Cada posibilidad tiene: Un campo tipo: Indica el tipo del objeto. Un campo derechos: Mapa de bits que indica las operaciones básicas permitidas en este tipo de objeto. Un campo objeto: Apuntador al propio objeto (por ej.: su número de nodo-i). Las listas de posibilidades son a su vez objetos y se les puede apuntar desde otras listas de posibilidades; esto facilita la existencia de subdominios compartidos. Las listas de posibilidades o listas-c deben ser protegidas del manejo indebido por parte del usuario. Los principales métodos de protección son: Arquitectura marcada: Necesita un diseño de hardware en el que cada palabra de memoria tiene un bit adicional: Indica si la palabra contiene una posibilidad o no. Solo puede ser modificado por el S. O. Lista de posibilidades dentro del S. O.: Los procesos hacen referencia a las posibilidades mediante su número. Lista de posibilidades cifrada dentro del espacio del usuario: Cada posibilidad está cifrada con una clave secreta desconocida por el usuario. Muy adecuado para sistemas distribuidos. Generalmente las posibilidades tienen derechos genéricos aplicables a todos los objetos, por ej.: Copiar posibilidad: Crear una nueva posibilidad para el mismo objeto. Copiar objeto: Crear un duplicado del objeto con una nueva posibilidad. Eliminar posibilidad: Eliminar un dato dentro de la lista-c sin afectar al objeto. Destruir objeto: Eliminar en forma permanente un objeto y una posibilidad. Muchos sistemas con posibilidades se organizan como una colección de módulos con módulos administradores de tipos para cada tipo de objeto y entonces es esencial que el módulo administrador de tipos pueda hacer más cosas con la posibilidad que un proceso ordinario. Se utiliza la técnica de amplificación de derechos: Los administradores de tipo obtienen una plantilla de derechos que les da más derechos sobre un objeto de los que permitía la propia lista de posibilidades. Inicio: Fin: Modelos de Protección Las matrices de protección no son estáticas sino dinámicas[23, Tanenbaum]. Se pueden identificar seis operaciones primitivas en la matriz de protección: Crear objeto. Eliminar objeto. Crear dominio. Eliminar dominio. Insertar derecho. Eliminar derecho. Las primitivas se pueden combinar en comandos de protección, que pueden ser ejecutados por los programas del usuario para modificar la matriz de protección. En cada momento, la matriz de protección determina lo que puede hacer un proceso en cualquier momento; no determina lo que no está autorizado a realizar. La matriz es impuesta por el sistema. La autorización tiene que ver con la política de administración. Inicio: Fin: Control de Acceso Por Clases de Usuarios Una matriz de control de acceso puede llegar a ser tan grande que resulte impráctico mantenerla [7, Deitel]. Una técnica que requiere menos espacio es controlar el acceso a varias clases de usuarios. Un ejemplo de esquema de clasificación es el siguiente: Propietario: Suele ser el usuario que creó el archivo. Usuario especificado: El propietario especifica quién más puede usar el archivo. Grupo o proyecto: Los diferentes miembros de un grupo de trabajo sobre un proyecto, acceden a los diferentes archivos relacionados con el proyecto. Público: Un archivo público puede ser accedido por cualquier usuario de la computadora. Generalmente permite leer o ejecutar pero no escribir sobre el archivo. Inicio: Fin: Respaldo y Recuperación La destrucción de la información, ya sea accidental o intencional, es una realidad y tiene distintas causas [7, Deitel]: Fallas de hardware y de software. Fenómenos meteorológicos atmosféricos. Fallas en el suministro de energía. Incendios e inundaciones. Robos, vandalismo (incluso terrorismo). Etc. Esta posible destrucción de la información debe ser tenida en cuenta por: Los sistemas operativos en general. Los sistemas de archivos en particular. Una técnica muy usada para asegurar la disponibilidad de los datos es realizar respaldos periódicos: Hacer con regularidad una o más copias de los archivos y colocarlas en lugar seguro. Todas las actualizaciones realizadas luego del último respaldo pueden perderse. Otra técnica es pasar todas las transacciones a un archivo, copiándolas en otro disco: Genera una redundancia que puede ser costosa. En caso de fallas en el disco principal, puede reconstruirse todo el trabajo perdido si el disco de reserva no se dañó también. También existe la posibilidad del respaldo incremental : Durante una sesión de trabajo los archivos modificados quedan marcados. Cuando un usuario se retira del sistema (deja de trabajar), un proceso del sistema efectúa el respaldo de los archivos marcados. Se debe tener presente que es muy difícil garantizar una seguridad absoluta de los archivos. 5.1.1 Dispositivos de Io 5.1.2 Controladores de Dispositivos 5.2 Principios de Software de I O 5.2.1 Objetivos del Software de IO 5.2.2 Manejadores de Interrupciones 5.2.3 Manejador de Dispositivos 5.2.4 Software de Io independiente de Dispositivos 5.2.5 Espacio del Usuario para Software de IO 5.3 Discos Ram 5.4 Discos Duros 5.4.1 Hardware de Discos 5.4.2 Software para Discos 5.5 Relojes 5.5.1 Hardware de discos 5.5.2 Software Reloj 5.5.3 Manejador del Reloj 5.6 Terminales 5.6.1 Hardware de Terminales 5.6.2 Manejadores Unidad 6 Administrador de archivos 6.1 Sistema de Archivos 6.2 Jerarquia de Datos 6.3 Tipos de Archivos 6.4 Interfase con Usuario 6.4.1 El Sistema de Archivo visto por el usuario 6.4.2 Diseño del Sistema de Archivos 6.4.3 Servidor de Archivos 6.4.4 Seguridad en Archivos 6.4.5 Mecanismos de Proteccion de Archivos 6.4.6 Implementacion Sistemas de Archivos 6.5 Llamadas al Sistema System Calls 6.6 Tipos de Interfaz 6.7 Lenguaje de Comunicacion (comandosdecontrol) y pipelines o conductos Unidad 7 Desempeño y seguridad 7.1 Medicion Desempeño Performance Sistemas Operativos , monitoreo y evaluación 7.2 Seguridad de Sistemas Operativos 7.2.1 Conceptos Fundamentales de Seguridad Sistemas Operativos 7.2.2 Vigilancia Sistemas Operativos 7.2.3 Proteccion Sistemas Operativos 7.2.4 Auditoria Sistemas Operativos 7.2.5 Controles de Acceso Sistemas Operativos 7.2.6 Nucleos de Seguridad Sistemas Operativos 7.2.7 Seguridad por Hardware y Software 7.2.8 Criptografia Sistemas Operativos 7.2.9 Penetracion Sistema Operativo