Ingeniero en Informática


Sistemas Operativos


SISTEMAS OPERATIVOS

TEMA 1: INTRODUCCIÓN AL SISTEMA OPERATIVO ONION

TEMA 1: INTRODUCCIÓN AL SISTEMA OPERATIVO ONION

Descripción general

El sistema operativo (S.O.) ONION posee las siguientes características:

  • Multiusuario: al ser multiusuario necesitaremos proteger los datos de cada usuario. Para esto necesitaremos los comandos de login y logout los cuales, dependiendo de un username y un password tendrán unos privilegios u otros.

  • Multiprogramado: esto quiere decir que se pueden tener n procesos ejecutándose al mismo tiempo, pero si el ordenador solo tiene un procesador se ejecuta un solo proceso y el resto estará bloqueado o preparados para ser ejecutados. Por lo tanto los estados de un proceso son: run (ejecutándose), preparado y bloqueado.

  • Estructurado en capas: Esto quiere decir que se diferencian diferentes capas según la cercanía al hardware. Las capas del ONION son la de sistema la BFS (Basic file System) y el núcleo.

Si se modifica el hardware solo se debería modificar la capa del núcleo ya que es la capa más cercana al hardware. Las llamadas al sistema (system calls) llaman a la capa del sistema, y la forma de comunicarse esta con las demás capas es mediante llamadas especiales, pero si se quiere comunicar con la capa del núcleo antes debe pasar la información por la capa de BFS. Cada capa resuelve un determinado problema y si una capa no es capaz de resolver dicho problema se baja a la capa siguiente.

Visión externa

La visión externa de un sistema operativo es lo que ofrece este al exterior.

Dispositivos

Los dispositivos que nos ofrece ONION son de dos tipos, físicos (consola, terminal, impresora y el disco) i lógicos (mailbox y nulo).

  • Consola: la consola esta formada por un teclado y una pantalla por donde se comunica el usuario y el ordenador de forma interactiva. La consola es donde trabaja el usuario principal. A la consola se le asocia un interprete de comandos al igual que a los terminales.

  • Terminal: igual que la consola pero que está conectado a la lincea serie.

  • Mailbox: a diferencia con UNIX, donde un mailbox es un archivo temporal, en ONION es un buffer de memoria que sirve para comunicar y sincronizar procesos.

  • Nulo: este dispositivo sirve para borrar, ya que la información que se inserte en dicho dispositivo ya no se puede recuperar, porque no podemos leer de este dispositivo.

La conexión entre ordenadores con el sistema operativo ONION queda reflejada en el siguiente gráfico:

Como se puede observar la conexión de los ordenadores se realiza mediante los terminales, donde cada terminal tiene su intérprete de comandos el cual no enviará ningún prompt que indique que están esperando una orden nueva.

Ahora veremos las características de los dispositivos:

  • ECHO/NO ECHO: solo tiene sentido en dispositivos de entrada/salida. Significa que lo que se introduce por la entrada sale por la salida. Por defecto la característica echo está activada (consola y terminal).

  • COOKED/RAW: cooked significa que el dispositivo es capaz de interpretar caracteres especiales (return, delete, tabulador, etc.). La consola y el terminal tienen por defecto la característica de cooked y el mailbox, la impresora y los ficheros la raw.

  • EOT/NOEOT: característica de fin de transmisión. Indica si un proceso ha dejado de trabajar con algún dispositivo o se cierra un canal de escritura en el terminal. El terminal y el mailbox tienen por defecto la característica de Noeot.

  • SPOOL/NOSPOOL: solo tiene sentido en la impresora, donde por defecto esta en nospool. La impresora es un dispositivo no compartible, es decir, solo un proceso puede acceder, y además es lenta. Debido a este motivo y para poder permitir que varios procesos puedan acceder a la impresora existe un dispositivo intermedio que permite que varios procesos puedan imprimir a la vez gracias a que crean un fichero temporal. Aquí interviene SPOOL el cual va cogiendo estos ficheros temporales y los envía a la impresora uno tras otro para que sean impresos. De este modo la impresora pasa a ser compartible.

  • PERMANENTE/NO PERMANENTE: característica de los mailbox. Si un mailbox es permanente este guarda la información que se le introduce aunque el proceso que lo invoca termine, en cambio en un mailbox no permanente cuando el proceso que lo invoca termina la información de dicho mailbox se pierde. Por defecto los mailbox son permanentes.

Interprete de comandos

El interprete de comandos muestre el prompt formado solo por el símbolo del dólar. Además acepta los caracteres de redireccionamiento de entrada, salida y error.

redireccionar salida >

redireccionar entrada <

redireccionar error ^

nom hará referencia a un fichero o a un dispositivo y nom_fitxer a un fichero.

El interprete de comandos reconoce los siguientes comandos:

  • Comandos de manejo de ficheros y dispositivos:

$ dir [>nom]

$ copy [<nom] [>nom]

$ del nom_fitxer

$ mou nom_fitxer_vell nom_fitxer_nou (cambio del nombre del fichero)

$ prot nom [prot] [uid] (modifica las protecciones y el ususario del nom)

$ set nom característica (característica de los dispositivos)

  • Gestión de procesos:

$ run nom_fitxer [<nom][>nom][^nom][“parámetres”]

Ejecuta un proceso pero espera a que termine el proceso hijo.

$ spawn nom_fitxer [<nom][>nom][^nom][“parámetres”]

Ejecuta el proceso hijo y el padre de forma concurrente.

$ stop id_proces.

$ sys [>nom]

Da información sobre el sistema (tiempo des de la puesta en marcha del sistema, y indica que fichero ejecuta el proceso y el identificador de usuario, uid).

$ login [nmbre usuario]

$ logout

Visión interna y Llamadas al sistema

Llamadas sobre la capa sistema

La única puerta de acceso a los sistemas operativos son la llamadas al sistema. Estas no son mas que un conjunto de llamadas a funciones las cuales implementan los servicios ofrecidos por el sistema. En el caso de ONION el conjunto de llamadas serán las primitivas que el nivel más externo (nivel sistema) ofrecerá a los usuarios.

Como hemos dicho el único modo de comunicar las diferentes capas del S.O. es a través de llamadas.

El nivel sistema es el que servirá de interprete entre los usuarios del S.O. y el mismo sistema. Las primitivas constituirán lo que se llama conjunto de llamadas al sistema. Este nivel ofrecerá a los programas de usuario una máquina virtual de alto nivel que garantizará una serie de servicios a los usuarios. Estos servicios pueden agruparse de la siguiente manera:

  • Entrada/salida: ONION ofrece E/S independencia del dispositivo sobre el que trabajan (la E/S se realiza con dispositivos virtuales, los cuales se identifican por un canal). Para garantizar esta independencia del dispositivo las llamadas

Llegir (canal, buffer, longitud)

Escriure (canal, buffer, longitud)

Actuaran sobre canales que podrán representar cualquier dispositivo o fichero. Estas operaciones aran un acceso secuencial i serán asíncronas. Estas llamadas no se bloquearán aunque no hayan terminado su trabajo y se les invoque de nuevo. Las llamadas

Esperar (id_io) -> nos indica como ha terminado un dispositivo

Cancel-lar (id_io2)

Ofrecerán la posibilidad de anular una operación de E/S i de sincronizar la finalización con la ejecución del resto del programa.

Hay dos formas de asociar el canal al dispositivo:

· Mediante la herencia: el proceso hijo hereda la tabla de canales del padre. Por ejemplo:

executar_programa(fitxer, input, output, error, “p1”,..., (char *)0

donde input, output y error indica que canal se quiere para la entrada, salida y error del proceso, si aquí se indica (char *)0, quiere decir que los canales son los mismos que los del padre que lo ha invocado.

· Mediante las llamadas

obrir (canal, nom, modo, protecciones)

tancar (canal)

las cuales asocian un dispositivo a o un fichero a un canal, y liberan un canal determinado permitiendo así una futura asociación con otro dispositivo o fichero, respectivamente.

En ONION solo disponemos de 5 dispositivos virtuales, esto quiere decir que solo tenemos cinco entradas a la tabla de canales. Cada dispositivo apunta a un solo descriptor de dispositivos.

  • Ficheros: ofreceran a los usuarios tres funciones sobre ficheros heredados del nivel BFS. Seran las de leer directorio del disquete, borrar un fichero i cambiar el nombre a un fichero.

  • Control de procesos: dos de las tareas fundamentales de todo el sistema operativo son lacreación i destrucción de procesos. En el caso de un sistema multiprogramado como ONION la creación de procesos implicrá una ejecución concurrente del proceso crador y del proceso creado.

Executar_programa (fitxer, input, output, error, “p1”,..., (char *)0

Crea un proceso nuevo, creando su PCB con su tabla de canales correspondiente. En el nivel BFS se hace la llamada

Crear_pcb_bfs (pid, prioritat, codi, pila)

Y se crea otra PCB en este nivel y se hace una llamada en el nivel de núcleo

Crear_pcb_nuc (codi, pila, prioritat, quantum, id_proc)

La cual crea otra PCB en este nivel y donde quantum es el tiempo de CPU asignado a este proceso.

Fi_programa (codi_retorn)

Stop (id_proces)

Esperar_fill (&codi_retorn)

Esta última llamada realiza una sincronización entre el proceso padre e hijo, donde el proceso padre se bloquea hasta que termina el proceso hijo. Con stop lo que realizamos es que el proceso indicado finalice por lo tanto es como si lo destruyéramos, en cambio fi_programa hace que el proceso que invoca esta llamada termine, seria como si se autodestruyera. Según como se termine un proceso codi_retorn de esperar_fill tendrá dos valores diferentes, -2 si el proceso que esperaba se ha terminado con stop y codi_retorn si el proceso se ha terminado con fi_programa.

  • Temporización: permiten que los procesos de usuario puedan bloquearse durante un cierto número de segundos en espera de algún acontecimiento.

  • Protección: será en este nivel donde se hará la gestión de usuarios i todas las comprobaciones necesarias para garantizar la protección de procesos y de dispositivos. Existen dos usuarios en un sistema con ONION: super-Onion (que es el sistema y que tiene todos los privilegios) y el ordinario. Los procesos solo podrán ser destruidos por su propietario o por el super-ONION. También será posible para el super-Onion cambiar el propietario de un proceso. Al haber el usuario ordinario y el resto de usuarios se definen dos dominios de protección, para el usuario y para el resto de usuarios. La protección puede ser de lectura, escritura o lectura/escritura.

Tener permiso de ejecución sobre un dispositivo es tener la posibilidad de modificar las características del dispositivo. Cuando un usuario abre un dispositivo, y es el primero en abrirlo, este se convierte en el propietario y el resto, por lo tanto no podrá acceder si no se cambian las protecciones.

Llamadas sobre la capa BFS

El BFS será un nivel intermedio entre la independencia de dispositivos que ofrece el nivel sistema i la dependencia respecto al hardware de la maquina que tendremos en el nivel inferior (núcleo).

Este nivel ofrecerá primitivas especificas para cada dispositivo que permitirán implementar las E/S que el nivel sistema ofrecía a los usuarios. DE toda forma estas primitivas diferenciadas para cada dispositivo serán independientes del hardware de la maquina ya que utilizaran las funciones que el nivel mas bajo les ofrecerá. Las funciones de este nivel pueden agruparse de la siguiente forma:

  • Acceso i manejo de ficheros (la mas importante en este nivel).

  • Entrada/salida de dispositivos.

  • Control de procesos.

  • Temporización.

  • Planificación de la utilización de recursos.

  • Protección de ficheros.

En este nivel tenemos dos tablas diferentes:

  • TFA (tabla de ficheros abiertos): aquí hay una entrada abierta por cada fichero diferente abierto. Tiene un campo que indica cuantas veces se ha abierto el mismo fichero y un puntero a la FAT.

  • FAT (File Allocation Table): indica a cada registro lógico correspondiente a un fichero donde se encuentra el registro físico. Esta tabla nos dice para todos los ficheros en que bloque físico se encuentra sus registros lógicos.

Con cada lectura, en este nivel, se leen 512 bytes que es lo mide un bloque entero.

Llamadas sobre la capa núcleo

Es el nivel más próximo al hardware de la maquina. Por debajo de este encontraremos los controladores de los dispositivos, la memoria y el procesador. Este nivel depende totalmente de la maquina sobe la qual estemos trabajando y se habrá de modificar cada vez que queramos transportar nuestro S.O. a un nuevo computador.

La función del núcleo será la de liberar a los niveles superiores del manejo de los dispositivos. Des de este nivel programaremos todos los controladores de la maquina de tal forma que los niveles superiores puedan excluirse de esta función. Así mismo será el único nivel que gestionará los registros del procesador i peor lo tanto, se habrá de utilizar rutinas en ensamblador. Des de este nivel ofreceremos al nivel BFS una maquina virtual con los siguientes conjuntos de servicios:

  • Gestión de procesos (la más importante en este nivel).

  • Gestión del tiempo.

  • Sincronización i comunicación entre procesos.

  • Entrada/salida sobre dispositivos.

  • Gestión de memoria.

Para cada nuevo proceso tendremos una pila en el espacio de memoria del usuario, y una PCB en el nivel del núcleo o espacio de direcciones del sistema, aunque en este nivel también seria bueno tener una pila.

Los procesos se pueden encontrar en diversos estados (solo se puede ejecutar un proceso y el resto de procesos activos estarán en estado ready o bloqueados). La política para ver que proceso se ejecuta és la Round-Robin por prioridades.

Esta política significa que la CPU siempre la ocupará el proceso mas prioritario, y si hay varios con la misma prioridad entonces cuando se acaba el quantum del proceso este pasa a estado ready (pasa a la cola ordenada por prioridades) y el primero de esta cola es el que se ejecuta. En la cola se inserta, el proceso, en el último lugar de los procesos con la misma prioridad. Dentro de esta política de Round-Robin existen diversas opciones:

  • Apropiación inmediata: los procesos se pueden bloquear, y por lo tanto pasa a ejecutarse el siguiente proceso en estado ready. Si se desbloquea y este es más prioritario que el que se está ejecutando, entonces la rutina que desbloquea el proceso hace que se ejecute inmediatamente y el que se estaba ejecutando pase a estado ready.

  • Apropiación diferida: la rutina de desbloqueo hace que el proceso desbloqueado pase a ready y que se ejecute, si es mas prioritario que el que se está ejecutando, en la siguiente interrupción de reloj. La rutina que se encarga de ver si se le acaba el quantum al proceso será la encargada de hacer el intercambio de procesos.

TEMA 2: PROCESOS

Planificadores

El objetivo de los planificadores es maximizar la utilización de la CPU referido a procesos del usuario. Existen tres tipos de planificadores:

  • Planificadores a largo plazo (Large term): estos planificadores deciden que procesos entran en el sistema. Decide el grado de multiprogramación. Este planificador carga el ejecutable, asigna un PCB i las pilas de usuario y de sistema y por último asigna los recursos.

  • Planificadores a corto plazo (Short term): en la CPU solo se puede ejecutar un proceso y por lo tanto hay procesos en estado ready o bloqueados. Este planificador decide que proceso, entre uno de los que están listos y el que se esta ejecutando, se ejecutará en la CPU.

  • Planificador a medio plazo (Medium term): este planificador se utiliza en sistemas con memoria virtual. Un fallo de pagina se produce cuando la CPU da una dirección de memoria que no se encuentra en la memoria física. Si la memoria se divide en muchas paginas se puede producir Trashing, que consiste en que se ejecutan más fallos de pagina que procesos.

La utilización de la CPU aumenta con el aumento de la multiprogramación pero puede llegar a un momento en que la utilización de la CPU desciende por los fallos de paginas. El planificador de medio plazo entra en acción en este punto de Trashing. Este planificador decide que procesos se quitan de la CPU en este punto, lo que hace es sacar, normalmente, los procesos bloqueados que pasan a un área de swap (disco), aunque también puede sacar procesos en estado ready.

Planificador a corto plazo

Los criterios de este planificador son lo siguientes:

· Justicia: se intenta aumentar la justicia, es decir, que todos los procesos tendrían que recibir algún tiempo de CPU. Lo contrario a este criterio es la inanición, haciendo que procesos nunca se ejecuten.

· Eficiencia: este criterio tiene que ser lo más eficiente posible, por lo tanto se intenta maximizar. Este criterio mantiene la CPU el tiempo máximo con procesos de usuarios.

· Productividad: (Throughput) este criterio intenta maximizar el número de trabajos procesados por unidad de tiempo. Tiene prioridad los procesos cortos frente a los largos.

· Tiempos de retorno (Turnaround): este criterio intenta minimizar este tiempo, que consiste en el intervalo desde que mandamos que se ejecute un proceso hasta que termina. (Característico de los sistemas batch).

· Tiempo de respuesta: criterio que minimiza el tiempo que transcurre desde que hay una petición E/S hasta la respuesta del dispositivo E/S. Se utiliza en sistemas interactivos.

· Tiempo de espera: minimiza el tiempo de espera en la cola de ready.

Políticas/Algoritmos de planificación

Políticas

Existen dos tipos de políticas:

  • Apropiativa: un proceso que ocupa la CPU, abandona la CPU de forma no voluntaria, ni termina ni se bloquea, ya que el sistema se la quita. Esta ploítica tiene dos variantes la apropiativa inmediata y la diferido (estas se basan si se introduce en la siguiente interrupción de reloj o se introduce inmediatamente).

  • No apropiativa: el proceso se bloquea o termina por su voluntad.

Algoritmos

• FCFS: el primero en legar es el primero servido. Sigue una política no apropiativa. SI un proceso se bloquea, entra en la CPU otro proceso, pero si el bloqueado se desbloquea hay dos opciones: ponerlo en la cola de ready o ejecutarlo, pero al tener una política no apropiativa el proceso bloqueado pasará a ready pero como si hubiera acabado de llegar.

• SJF: se ejecuta el proceso más corto. Sigue una política no apropiativa. Este tiene una variante que consiste en la versión apropiativa.

SJF (Shortetst Job First) es útil en sistemas batch. El propio usuario es el que da el tiempo que va a durar el proceso. En sistemas interactivos nos interesa saber cuanto va a durar la siguiente ráfaga de CPU, pero no se calcula el tiempo exacto sino que se hace una aproximación con la siguiente fórmula:

n+1= tn +(1-)n

donde: n+1: es el tiempo de la siguiente ráfaga.

n: es el tiempo de la ráfaga.

tn: es el tiempo de la ráfaga anterior.

: es un valor entre 0 i 1.

Des de que le asignamos el tiempo hasta que finaliza este tiempo, el proceso se ejecutará y si el proceso necesita mas tiempo este seguirá ejecutándose en la CPU hasta que lo abandone por voluntad (finalice o se bloquee).

• SRT (Shortest remaining Time) és la versión apropiativa del algoritmo SJF y lo que hace es que cuando un proceso se desbloquea o se crea un proceso y el tiempo de ráfaga es menor que el tiempo de ráfaga del proceso que se esta ejecutando, se hace un cambio de contexto, el bloqueado se ejecuta y el que se estaba ejecutando pasa a ready.

Cuando se desbloquea o entra un proceso nuevo, se calcula su tiempo próximo de ráfaga. Si el proceso que se está ejecutando le queda mas tiempo de ráfaga que nuestro tiempo de ráfaga calculado hacemos el cambio de contexto.

• Por prioridades: es un algoritmo con política apropiativa. Ejecuta el proceso con la mayor prioridad.

Estos algoritmos pueden producir inanición y por lo tanto no cumplen el criterio de Justicia.

•Round-Robin: asignamos un determinado tiempo a los procesos (quantum). EL proceso ocupa la CPU el número de ciclos o interrupciones de reloj que se le indique en el quantum. Este algoritmo sigue una política apropiativa, ya que expulsamos al proceso cuando se acaba su quantum. Todos los procesos en ready tienen la misma prioridad.

Colas multinivel

Con estas colas se tienen diferentes colas de ready para procesos batch y para procesos interactivos, a las cuales se le asocia un porcentaje de uso de la CPU.

Hay colas multinivel realimentadas, en estas colas los procesos entran en una cola donde se asigna un quantum, si no tiene suficiente quantum, el proceso pasa a otra cola con un quantum mayor.

En estas colas multinivel realimentadas, se ejecutan los procesos de la cola que asigna menor quantum, en la última cola habría un algoritmo de planificación FCFS.

Ejemplo: UNIX

• Diagrama de estados en UNIX.

User runnig: ejecutando código de usuario.

Kernel runnig: ejecutando código de sistema (llamadas al sistemas, servicio a las interrupciones, etc.).

Preempted: se ha sacado un proceso de la CPU, porque se ha acabado el quantum o aparece un proceso mas prioritario que el que se estaba ejecutando.

El algoritmo de planificación que utiliza es el Round-Robi con colas multinivel autoalimentdas. Unix distingue entre procesos expulsados y procesos bloqueados. Si un proceso se ha expulsado va a la parte de arriba de una pila de prioridades, donde hay menos prioridad, y no podrá bajar del umbral, a partir del cual de guardan los procesos que se han bloqueado voluntariamente. La parte de arriba de dicha pila esta realimentada, es decir, cuando un proceso de esta parte pasa a ejecutarse, los que están por encima descienden.

TEMA 3: MECANISMOS DE ENTRADA EN EL S.O.

Introducción

Aquí veremos como una llamada al sistema entra en el sistema operativo (entra a ejecutar código del S.O.). Hay tres métodos de acceso:

  • Interrupciones: creadas por el hardware de dispositivos externos y que se producen de forma asíncrona. Estas se miran siempre después de ejecutar una instrucciones máquina.

  • Excepciones: interrupciones creadas de forma involuntaria en el proceso (fallo de pagina, división por cero, overflow, etc.). Hay excepciones que destruyen el proceso y otras que después de ejecutar un código vuelve al proceso como el fallo de página. Una excepción se mira entre microinstrucciones (fetch del operando 1, fetch del operando 2, etc.).

  • Traps: mediante llamadas al sistema. Un trap es una interrupción software provocada por la instrucción de lenguaje máquina INT.

Llamadas al sistema

Las llamadas tendrán que salvar el contexto del proceso, cambiar a modo privilegiado (este cambio se hace por hardware y es el modo en el cual se puede ejecutar código del sistema, nosotros simularemos el cambio por software), restaurar al modo usuario y por último restaurar el contexto.

Desde el proceso de usuario se puede hacer la llamada a la librería interna del S.O. pero deberíamos saber:

  • El trap de la llamada al sistema.

  • Como se hace el paso de parámetros.

  • Como se devuelve el resultado.

Pero con las librerías del S.O. no hace falta que sepamos estos tres puntos. Se puede hacer una llamada pero gracias a las librerías del S.O. La librería del S.O. es quién ejecuta el trap correspondiente a la acción que pedimos.

Hacemos que todas las llamadas al sistema generen la misma interrupción software, ya que no hay espacio en el vector de interrupción para guardar las direcciones de todas las rutinas de servicio (la interrupción que utilizaremos será la 64).

En el vector de interrupciones tenemos guardada la rutina intermedia (Trap()) que comprobará que llamada se ha ejecutado y ha provocada la interrupción, una vez hecha esta comprobación se ejecutará la rutina interna adecuada a la llamada al sistema. Para comprobar que llamada se ha ejecutado se utilizan los registros de la CPU, en ONION utilizaremos el registro AX del proceso que ha hecho la llamada, ahora cada llamada moverá al AX un código que indicará que llamada ha ejecutado el trap.

Paso de parámetros

Los parámetros se introducen de forma inversa a como se han escrito en la pila, una vez pasados los parámetros se pasa la dirección de retorno. Al producirse un trap se guardan los flags y la dirección de retorno a la librería del S.O., donde se produjo la interrupción. La rutina TRAP salvará el contexto del proceso que se estaba ejecutando, mira el contenido del registro AX y ejecuta la rutina interna del S.O. correspondiente al código que contenía el registro AX.

El acceso a los parámetros se realiza en la pila pero no encima del la última dirección de retorno sino al principio de la pila. Para solucionar esto, se pueden tener dos pilas, una pila del usuario y la pila del sistema que tendrá copias de los parámetros del usuario, esta es la mejor forma. También se podría reestructurar la pila para que los parámetros estén después de la última dirección de retorno, pero, en este caso, también se debería reestructurar por segunda vez, para dejar la pila como estaba. Pero nosotros declararemos un parámetros que tendrá la longitud desde la última dirección de retorno hasta los parámetros, este parámetro será un struct llamado dummy.

Struct dummy

{

int p[16];

}

Resultados

Cuando termina la ejecución de la llamada volvemos a la dirección de retorno 3, restauramos el contexto, y se vuelve al lugar donde se produjo la interrupción, y por último se sacan los parámetros de la pila.

La llamada al sistema normalmente devuelven un entero, este resultado se devuelve en el registro AX, si es un carácter o un entero, o con el DX y AX si el resultado es un long o un far pointer. Todos devuelven en AX el resultado, pero en la rutina TRAP se hace un restaurar y por lo tanto el contenido de AX se podría perder con el contenido de la pila. Para devolver correctamente el resultado se debería:

  • Acceder a la posición de la pila correspondiente a AX e introducir el resultado dentro de la llamada al sistema.

sci (dummy, par1, par2)

{ .

*(run->sssp+3)=res;

.

}

  • O en la rutina de TRAP hacer que devuelva un resultado que se almacena en la pila.

TRAP()

{ .

*(run->sssp+3)=sci();

.

}

  • O utilizar el parámetro dummy donde se introducirá el resultado.

Typedef struct

{ int bp, es, ...

} dummy;

dins de la rutina interna: dummy.ax=res.

Niveles de interrupción

Ejecutando código de las rutinas de servicio a las interrupciones, ejecutando el TRAP o el código de la llamada, las interrupciones están inhibidas y por tanto no nos llega ninguna interrupción.

Ejecutando una llamada de sistema, haremos un desinhibir interrupciones y así nos llegan interrupciones hardware (desinhibir(0x200)).

Al poder llegar interrupciones hardware el puntero sssp podría ser que nos apuntara a la segunda vez que se ha salvado el contexto producido por una interrupción y al hacer el restaurar del TRAP podría ser que no se restaurara bien ya que sssp apunta a otra posición (final de la segunda salvación del contexto). Por tanto tendremos dos niveles de interrupciones y por tanto podríamos tener dos apuntadores de pila sssp[0] que apunte a la primera vez que salvamos y sssp[1] que apunte a la segunda vez que salvamos. Para implementar esto necesitaremos otro campo que indica en que nivel nos encontramos:

  • Modo 0= interrupción cuando se ejecuta código de usuario.

  • Modo 1= interrupción cuando se ejecuta TRAP o una llamada al sistema. O ejecutando una RSI si venimos de interrumpir código de usuario.

  • Modo 2= ejecutando una RSI si venimos de interrumpir el código de sistema.

Struct pcb{

.

int *sssp[2];

int modo;

}

Salvar = run->sssp[run->modo]=salvar();

run -> modo++;

Restaurar = run -> modo--;

Restaurar(run->sssp[run->modo]);

TEMA 4: SISTEMA DE FICHEROS

Visión Estática

Los soportes físicos están estructurados en bloques. Existen bloques libres y ocupados por el fichero.

Gestión de bloques libres

Existen diversas formas de gestión:

• Gestión con un mapa de bits donde 0 indica que el bloques está libre i 1 si está ocupado. Necesitamos un espacio fijo para esta gestión (para guardar el mapa de bits).

• También existe la lista de bloques libres. Esta lista puede estar encadenada donde los lugares libres se encadenan entre ellos.

La lista encadenada tiene el defecto que si tenemos N bloques libres necesitaremos N lecturas para encadenar estos lugares libres.

Otra versión de las listas es la que consiste en el agrupamiento (listas de agrupamiento). En esta versión se guarda en el primer bloque libre la lista de todos los bloques libres. Si en un bloque no cogen todos los punteros de las posiciones libres, se utilizaría otro bloque, pero donde el último elemento del primer bloque incidiría el bloque siguiente donde se sigue guardando la información (lugares libres).

La última versión consiste en el recuento, donde se introduce en un bloque el número de bloques libres consecutivos que le siguen.

Gestión del espacio ocupado

• Asignación contigua: obligamos a que todos los ficheros tengan todos loa bloques en forma contigua dentro del disco, esto permite acceso secuencial y acceso directo.

Esta asignación es problemática por la asignación de espacio al fichero, ya que crea fragmentación externa (existe lugar libre pero no hay los suficientes bloques consecutivos para insertar un fichero).

Existen 3 métodos para asignar espacio:

  • First Fit: primer espacio libre en el que coja el fichero.

  • Best Fit: buscamos el espacio libre que mas se ajusta a nuestro fichero.

  • Worst Fit: se coloca en el peor espacio, así en el espacio libre que queda pueda coger otro fichero.

• Asignación encadenada/enlazada: los bloques de los ficheros están encadenados (al igual que con las listas encadenadas de los espacios libres). Ahora no se produce fragmentación externa (se puede insertar en cualquier bloque) y por lo tanto no existen problemas de asignación de espacio.

Pero ahora el acceso solo se podrá realizar de una forma: de forma secuencial ya que no sabemos donde está el bloque i-esimo de u fichero y se tendría que recorrer todos los bloques encadenados. Necesitamos por lo tanto un puntero para hacer la encadenación y estos se pueden perder si se cae el sistema.

Un caso particular seria la FAT del DOS (File Allocation Table). Esta tabla tiene en la posición i la información del bloque i (la dirección del siguiente bloque del fichero, estado del bloque y si es el final del fichero -eof-). Esta FAT debería estar en memoria ya que si no esta deberíamos acceder siempre primero al disco. Con la FAT aún tenemos el acceso secuencial ya que se recorrería la FAT en busca del bloque deseado. Por lo tanto el mayor problema es que la FAT no nos cogiera en memoria, y por lo tanto no tendría ninguna ventaja. La FAT es más grande conforme más grande sea la capacidad del disco. Si no nos cogiera la FAT podríamos dividir el disco en cluster y no en bloques, donde un cluster tendría 1, 2 o 4 bloques (siempre múltiple de dos), así cada entrada a la FAT representa un cluster (que será la unidad de asignación de espacio).

La unidad de asignación de espacio es la cantidad de memoria que se le asigna a un fichero cuando necesita mas espacio y lo que se libera cuando el fichero no necesita ese espacio.

La unidad de transferencia es la cantidad de información que se puede transportar de disco a RAM, esta cantidad es de 1 sector (1 bloque).

Otra solución es el volume, que consiste en dividir el disco en particiones donde cada una tiene su propio sistema de ficheros, así se reduce el espacio de FAT ya que cada partición tiene su propia FAT.

Para acceder a los ficheros necesitamos el fichero directorio, el cual tiene información sobre otros ficheros (nombre, número de bloques que ocupa en disco). Este directorio en asignación contigua contiene la posición del primer bloque del fichero. Y en asignación encadenada contiene la posición del primer bloque del fichero, pero para acceder, por ejemplo, al tercer bloque se debería pasar por la entrada que indique el directorio referente a ese fichero, después por la siguiente hasta de la FAT hasta que se llega al bloque deseado. Si deseamos el primer bloque, o los ficheros ocupan un solo bloque, entonces no hace falta pasar a la FAT ya que el directorio contiene el primer bloque de los ficheros.

En cada partición se tiene un superbloque que indica donde se encuentra la FAT, donde se encuentra el directorio raíz, etc.

• Asignación indexada: ahora tenemos un bloque por fichero (bloque índice). En este bloque índice tendremos la dirección de los bloques que pertenecen al fichero. Para ficheros muy pequeños necesitaremos un bloque índice pero donde se utilizarán muy pocas entradas.

Ahora cada fichero ocupará n+1 bloques. Si los índices de los bloques no cogen en el bloque índice se pueden tener diferentes bloques índices encadenados o bien tener diferentes niveles de bloques de índice.

Con esta asignación se puede tener acceso directo pero siempre se ha de pasar por los diferentes bloques índices que tenga el archivo.

Un caso particular son los Inodes de UNIX. Cada fichero tiene un inode que contiene la información de los ficheros, dentro de cada inode hay 10 apuntadores directos a bloques del fichero. Si con los 10 apuntadores no tenemos suficientes para representar el archivo, se crearía un apuntador indirecto (apuntador a un bloque de índice donde caven aproximadamente 256 apuntadores a bloques de datos), si todavía no tuviéramos suficientes apuntadores se crea un apuntador doblemente indirecto (este apunta a un bloque y las posiciones de este bloque apuntan a otros bloques de índices), y si todavía no tenemos suficientes se pueden crear apuntadores triplemente indirectos (donde cada puntero del bloque apuntado por cada puntero directo, apunta a otros bloques y las posiciones de estos a bloques de índices).

Con inodes en el directorio solo tendremos el nombre del fichero y el número de inode. Así pues existe una tabla de inodes, indexada por el número de inode, donde está toda la información de los ficheros (nombre del dueño, protecciones, etc.).

Ahora para acceder al directorio raíz, tenemos que pasar por el inode en lugar de por el superbloque. Cada inode está en el disco y ocupa 1 bloque de espacio. Por lo tanto para acceder al tercer bloque de un fichero tendríamos que:

  • Obtener el inode raíz del disco.

  • Obtener el directorio raíz a partir del inode raíz.

  • Obtener el inode j correspondiente al directorio donde se encuentre el fichero.

  • Obtener el directorio correspondiente.

  • Obtener el inode k correspondiente al fichero deseado.

  • Acceder al tercer bloque.

  • Compartición de ficheros

    Cualquier modificación se podrá ver por otras vías si el fichero está siendo compartido. Hay dos formas de compartir ficheros:

    • Hard link: entrada de directorio que apunta al fichero el cual queremos compartir. Por lo tanto apuntan al mismo inode en el caso de UNIX.

    En MS-DOS no se puede compartir ficheros ya que al modificar un fichero se modifica la información de la entrada al directorio, pero si estuviera compartido otra entrada de directorio tendría la información antigua que ya no sería válida.

    • Soft link: creamos un fichero que contendrá el camino de acceso al fichero compartido (pathname). El pathname puede ser absoluto, dando la dirección des del directorio raíz, o puede ser relativo si se da la dirección a partir del directorio de trabajo. En este caso se crea un inode diferente ya que se crea un nuevo fichero, aunque contenga solo la dirección de otro fichero.

    Directorios

    Los directorios son ficheros con una estructura interna especial donde guardan la dirección de todos los ficheros que contiene dicho directorio. Cada directorio tiene su propio inode.

    Sistema de ficheros (S.F.)

    Hay diferentes sistemas de ficheros:

    • 1 nivel: un directorio raíz, donde se encuentran todos los ficheros del ordenador. El directorio tendrá tantas entradas como ficheros diferentes tenga. Todos los nombres de los ficheros tienen que ser diferentes.

    • 2 niveles: un directorio raíz, donde se encuentran los directorios/usuarios que corresponden, uno a cada usuario. Pero todos los ficheros de cada directorio/usuario tienen que ser diferentes en cada directorio.

    • Jerárquico: (DOS) un directorio raíz a partir del cual se pueden “colgar” diferentes ficheros y directorios. Para cada fichero se tiene su pathname (absoluto si se especifica a partir del directorio raíz o relativo si se especifica a partir del directorio de trabajo). No se pueden compartir ficheros, ya que solo existe un camino para cada fichero.

    • En grafos: acíclicos (Unix) son los que no permiten bucles, es decir, que no permiten que un directorio llame a otro directorio superior a él. Permiten compartir ficheros y por lo tanto se puede acceder a un fichero por diferentes caminos.

    Cíclicos o general permiten los bucles dentro del sistema de ficheros donde un directorio puede apuntar a un directorio anterior a él

    Ejemplos

    1 nivel

    CP/m.

    Jerárquico

    DOS: no permite hard link. Entrada de directorio.

    Bytes

    8

    3

    1

    10

    2

    2

    2

    Filename

    ext

    Tamaño

    Grafo acíclico

    UNIX: la información se encuentra en la tabla de inode

    Inode

    Dueño

    Grupo

    Rwxrwxrwx

    Protecciones

    Tipo

    Ocd, directorio, link

    Nº links

    Número de hard links

    Tamaño

    Tiempos

    Actualización, creación

    10

    Punteros directos a los bloques

    Puntero indirecto

    Puntero doblemente indirecto

    Puntero triplemente indirecto

    Nº de opens. Se incrementa cada vez que se abre este fichero.

    En todo directorio hay dos entradas que son la entrada actual y la entrada al directorio padre (. y .. respectivamente)

    En todo S.O. existe el superbloque o bloque 0 de los archivos. Este bloque indica donde está el directorio raíz, lo que ocupa cada bloque o cluster, etc. El superbloque en MS-DOS es el siguiente:

    Super-bloque

    FAT

    FAT copia

    Dir. Raíz

    Ficheros

    -bytes que ocupa cada sector.

    -sectores por cluster.

    -nº de FATS

    -nº de sectores.

    En UNIX el superbloque índica el número de inodes, el número de bloques y el índice del primer bloque libre. Los inodes 1 y 2 en UNIX son especiales ya que el 1 índica los bloques defectuosos y el 2 índica el índice del directorio raíz.

    Protección de ficheros

    Por proteger los ficheros se entiende proteger los ficheros contra accesos inadecuados. Las formas de proteger los ficheros son las siguientes:

    • Todo.

    • Nada.

    • Acceso controlado: lectura, escritura, ejecución, renombrar, eliminar, etc.

    Hay cuatro métodos de protección:

    • Nominación: el resto de los usuarios no conoce el nombre de los ficheros, por lo tanto no puede hacer un listado de un directorio del cual no sea el usuario.

    • Contraseña: cada fichero tiene su contraseña que se pide cada vez que se quiere utilizar el fichero en cuestión. Por lo tanto si el fichero esta dentro de algún/os directorios se tendrá que dar la clave para cada directorio por el que se tenga que pasar hasta llegar al fichero en cuestión, el cual tendrá su propia clave.

    • Lista de accesos: índica los usuarios y operaciones que pueden realizar estos sobre el fichero.

    • Grupo de acceso: se definen grupos y para cada grupo las operaciones que pueden realizar. Los grupos podrían ser: dueño, grupo al que pertenece, y el resto de usuarios.

    Fiabilidad/Consistencia

    Si se produce una caída del sistema se ha de comprobar que el sistema de ficheros aún este en buenas condiciones (consistencia).

    Cuando se produce dicha caída del sistema y se reinicializa el ordenador, en UNIX se comprueba la consistencia e la siguiente forma:

    • Bloques:

    • o- contador de lugares ocupados: recorre los inodes y cuenta los inodes donde se encuentra definido el bloque.

    • F- contador de libres: recorre la lista de lugares libres y se cuenta las veces que se ha encontrado el bloque.

    La consistencia viene dada si O"F=1. Solo se puede encontrar una vez en la lista de libres o una vez en un inode.

    · Si o+F=0 si no se encuentra en ningún lugar se incluye en la lista de libres.

    · Si o=0; F>1, se van eliminado.

    · Si o>1 se van coge un bloque libre, se copia la información y se hace que uno de esos apuntadores apunte al nuevo bloque escrito.

    •Ficheros:

    • link- contador de número de links (inode)

    • cont- contador que cuenta los directorios que apunta a este inode del fichero.

    La consistencia viene dada si link = cont.

    · Si cont<link, se actualiza el campo de links del inode con la cifra de cont.

    · Si cont>link, se actualiza el campo de links con el número de cont.

    Scheduling Disco

    El tiempo que se tarda en alguna operación de lectura o escritura está compuesto por:

    • Tiempo de búsqueda (tiempo en encontrar una pista, seek).

    • Tiempo de latencia (tiempo en encontrar el sector).

    • Tiempo de transferencia.

    Los algoritmos para hacer N accesos a disco son los siguientes:

    1.- FCFS: el primero que llega es el primero que se sirve.

    2.- SSTF (Shortest Seek Time First) busca el que requiera un posicionamiento del cabezal mínimo. Puede producir inanición.

    3.- SCAN: método del ascensor; se atienden todas las peticiones, primero la que requiera un posicionamiento más cercano a la posición de inicio, después la siguiente más cercano pero siempre aumentado la posición, una vez se llega al final de la pista se retrocede de la misma forma. Es decir, mientras el cabezal se mueve en un sentido y después en el otro.

    4.- C/SCAN: igual que el anterior pero el cabezal solo se mueve en un sentido.

    5.- LOOK: igual que SCAN pero si hay peticiones solo llega hasta la última pista que tiene petición sin necesidad de llegar al final.

    6.- C/LOOK: combinación de C/SCAN y LOOK.

    Visión dinámica

    Cuando abrimos un fichero para trabajar, necesitamos de estructuras de datos auxiliares.

    UNIX

    Se comprueba si el UID coincide con el dueño, si no es así, se comprueba el GID para saber que operaciones podemos realizar con el fichero mirando las protecciones, y si tampoco se puede, se mira las protecciones para el resto de usuarios. Las protecciones, solo se comprueban cuando se abre un fichero, por lo tanto si se modifican las protecciones, mientras esta abierto, y las protecciones nuevas afectan a quién tiene abierto el fichero, las modificaciones de estas protecciones no influyen en el usuario actual.

    ONION

    Cada vez que se crea un fichero se crea un puntero de lectura/escritua diferente.

    TEMA 5: GESTIÓN DE ENTRADA/SALIDA

    (apuntes Entrada/Salida de ONION del CPET)

    Introducción

    El subsistema de E/S gestiona los mecanismos de comunicación entre CPU i usuario. El usuario no puede acceder directamente a los periféricos, lo que hace es una petición al S.O. que es el que se encarga de hacer la gestión del periférico. Ahora el usuario no se preocupa de cómo se hace. Esto de diferenciar el subsistema de entrada/salida es para proteger el hardware i la información, esta protección es un solo PC no haría falta pero si que hace falta si tenemos una red, ya que un usuario podría perjudicar a otro.

    Los objetivos son:

    • Independizar el usuario del subsistema de E/S. Esto se hace ocultando las diferencias de los periféricos haciendo operaciones comunes para leer i escribir ya sea en disco, impresora, pantalla, etc. para hacer esto necesitamos dispositivos virtuales (en UNIX los canales). Los dispositivos virtuales ofrecen un elemento que representa el dispositivo físico. Una ventaja es la portabilidad, ya que de esta forma se puede cambiar un dispositivo físico sin tener que cambiar el dispositivo virtual.

    • Eficiencia.

    Cada S.O. tiene su forma de organizar el subsistema de E/S, por lo tanto no hay una forma general de gestionar los dispositivos.

    Problemas por la E/S

    Las diferencias entre dispositivos físicos son las siguientes:

    • la velocidad de cada dispositivo es diferente entre ellos, y por lo tanto no podemos saber a priori la velocidad del dispositivo.

    • La unidad de transferencia del dispositivo es diferente, si utilizamos el teclado se transmite un byte pero si utilizamos el disco, leemos o escribimos bloques.

    • La codificación de los datos también es diferente según el dispositivo. Si escribimos a la pantalla el dato se compone por los atributos y el carácter, en cambio al disco se guardan bytes.

    • Las operaciones permitidas también son diferentes, en un disco se puede leer y escribir pero en la pantalla solo se puede escribir y del teclado solo se puede leer. Pero aunque estas operaciones no tengan sentido han de estar para crear la independencia de dispositivos, por lo tanto si un usuario quisiera escribir en el teclado podríamos avisar que la operación no es correcta, o permitir la acción pero que el código de la acción fuera nulo.

    • Modalidades de trabajo: algunos dispositivos de la misma familia pueden tener diferente forma de trabajar. Por ejemplo la impresora puede trabajar en SPOOL o NOSPOOL, y el terminal en ECHO o NOECHO.

    • Código de error: el dispositivo si falla crea un código de error, pero al haber dispositivos diferentes los errores también serán diferentes. Los S.O. en este aspecto dan información bastante genérica sobre el error que se halla producido.

    Diseño

    Haremos operaciones comunes para los dispositivos, pero para aprovechar todo el dispositivo también necesitaremos operaciones específicas de cada dispositivo.

    Una operación genérica será:

    -Obrir: en esta operación la estructura de datos se carga en memoria, inicializamos el dispositivo.

    A un dispositivo físico se le ha de asignar un dispositivo lógico (virtual). Este dispositivo virtual puede ser un canal lógico (devuelto por el S.O.) o un nombre lógico (definido por el usuario, el cual asigna el nombre a un dispositivo físico i desde su programa utilizará el nombre definido por él).

    Nombre lógico: ASSIGN SYS$OUTPUT f.dat

    Canal lógico: ls >f.dat

    Para un S.O. es más fácil gestionar canales que nombres y para un usuario es mas fácil utilizar nombres lógicos que no canales.

    El redireccionamiento es abrir un dispositivo y cambiar el dispositivo virtual con el que se trabaja, los canales o nombres lógicos han de estar previamente abiertos.

    Implementación

    Necesitamos una tabla que traduzca las operaciones sobre dispositivos virtuales (operaciones genéricas) a operaciones sobre dispositivos físicos.

    También tenemos que traducir las operaciones genéricas sobre operaciones especificas para un dispositivo concreto. Esta tabla de traducción es la Tabla de Canales.

    En la tabla de canales debemos buscar que canal se pide y debemos buscar el tipo de dispositivo. Este mecanismo es la identificación del dispositivo por programa, donde una modificación del dispositivo nos haría cambiar una rutina que no toca este dispositivo.

    Identificación a través de una estructura de datos (DE)

    El descriptor de dispositivo: la DE es quien describirá el comportamiento del dispositivo, este comportamiento se indica mediante las operaciones que el dispositivo puede realizar. La DE tendrá el nombre del dispositivo, operaciones que realiza, características del dispositivo, semáforos para implementar el mutex, etc. Cada dispositivo tendrá unas operaciones comunes, más las operaciones especificas de cada dispositivo. Las operaciones comunes son:

    Struct{

    Char nom[];

    Int (* p_llegir)();

    Int (* p_escriure)();

    Int (* p_obrir)();

    Int (* p_tancar)();

    Int (* p_posicionar)();

    Int (* p_cancelar)();

    Int (* p_esperar)();

    Semf mutex;

    Información específica

    }

    Ahora en la función de llegir_generic miraremos la tabla de canales indexada por canal, la tabla de canales apuntará al descriptor de dispositivo, y en el descriptor de dispositivo buscaremos el campo llegir que apuntará a una rutina de llegir particular del dispositivo concreto.

    Un driver es el descriptor de dispositivo mas el código de las funciones. Ahora si se modifica un dispositivo solo tendremos que modificar el descriptor de dispositivo i el código de las funciones.

    Ret=run->TC[c]->p_llegir(b, n);

    Debemos de comprobar siempre que el número de canal por si nos pasamos del número máximo de canales.

    El llegir_especific (b, N) no manipula el dispositivo, solo consulta los buffers i si no tiene suficiente realiza una llamada al nivel BFS i esta al nivel núcleo si no puede resolver el problema esta capa. Los dispositivos físicos estarán gestionados por la capa BFS pero los dispositivos lógicos (nul, mailbox) son gestionados por la capa SISTEMA. Por lo tanto no siempre tendremos que llegar al nivel BFS.

    Esto sirve para hacer llamadas sincronas, pero en ONION las llamadas son asincronas, las cuales ya admiten otras llamadas mientras se esté ejecutando las funciones de las capas del S.O.

    Le pedimos al S.O. que nos haga unas determinadas funciones y mientras las ejecuta, nosotros podemos realizar otras operaciones, pero si llega un momento en que necesitamos alguna información de la que le pedimos al S.O. tendremos que esperar(id_io), si el dato esta disponible la sirve i si no está disponible esta llamada se bloqueará hasta que obtiene los datos deseados.

    Ahora en el nivel sistema tendremos que permitir ejecutar el código de los niveles del S.O. i el código del usuario. Para realizar esto tenemos los gestores. Ahora el llegir_especific hará un paquete con la información pasada por el usuario y la pondrá en el gestor. Cuando el gestor acaba su trabajo este avisa al usuario mediante la llamada esperar. Este paquete realizado es el IORB (Input/Output Request Block) bloque de entrada/salida. La estructura de este paquete IORB es:

    Strcut{

    Operación a realizar;

    Buffer_usuario; // de donde se extrae la inf. Y done se pone

    N_bytes;

    Información especifica

    }

    En llegir_especific preparamos el IORB y lo enviamos al gestor, encolamos el IORB y avisamos al gestor que tiene trabajo por realizar (esto aviso se hace realizando un signal).

    Ahora al modificar o poner una nueva función al dispositivo la función especifica dentro de la capa SISTEMA normalmente no se modificará, se podría modificar el gestor pero no la función genérica.

    Las ventajas de los gestores pueden ser las siguientes:

    • Al tener la cola de IORB hace que el gestor sepa cuantas peticiones de un dispositivo tiene i de esta forma optimizar el servicio para cada demanda de un dispositivo en concreto (Ordenar las peticiones de disco por la pista o cilindro que quieren cada proceso). Normalmente hay un gestor por dispositivo pero pueden haber mas gestores.

    • Los gestores pueden estar tanto a nivel SISTEMA como a nivel BFS (este último es el que hace la optimización y además es único).

    El trabajo sucio la hace el gestor y no la rutina genérica.

    Hay diferentes tipos de descriptores de dispositivos:

    • El descriptor único aunque haya mas de un dispositivo: (terminal, consola, teclado, etc.) los datos que llegan al puerto serie son independientes del proceso. Cuando un proceso quiere abrir un canal para el terminal se le devuelve el puntero a este único descriptor de dispositivo.

    • Descriptor por cada dispositivo: (MBX) Si un proceso quiere escribir, escribe a partir del puntero in. Cuando un proceso lee coge los caracteres a partir de la posición del puntero out i lo modifica. Si otro quiere leer del mismo MBX leerá a partir de la nueva posición del puntero out (dando información inconsistente), pero si quiere leer de otro MBX se creará otro mailbox. Por lo tanto tenemos un D.D. para cada MBX diferentes para diferenciar los datos de cada MBX. Ahora si un proceso abre un MBX ja abierto no se creará un nuevo DD sino que se utiliza el mismo, de esta forma se compartirán los datos de dicho MBX.

    • Un descriptor por cada fichero abierto: (ficheros) ahora tenemos diferentes DD para cada fichero. Si diversos procesos acceden al mismo fichero se compartirían los punteros in, out del buffer, y por lo tanto habría incoherencia a la hora de leer o escribir del fichero, por lo tanto tendrá un DD nuevo cada vez que se abra el mismo fichero y para cada fichero diferente.

    Ficheros

    Al tener un solo gestor puede pasar que un proceso pida una lectura de 2 o 3 Mb, y otro que quiera leer 4 Kb, si este se pone detrás del fichero largo, tardará mucho, para evitar esto tendremos mas de un gestor en el nivel sistema para los ficheros i así cuando uno este ocupado trabajará otro. La información de cada uno de estos gestores irá a parar a una cola de la cual saldrán al gestor de la capa BFS que será el encargado de hacer la optimización por bloques.

    Al abrir un mismo fichero tendremos diferentes DD con sus buffers pero si uno lee en una posición i otro escribe en la misma posición, tendremos información inconsistente a los buffers, para solucionar esto tenemos la buffer caché que será una tabla global que contendrá todos los buffers de todos los DD abiertos.

    Si cambiamos de registro lógico antes de ir a disco, miraremos si ya estaba en la buffer caché. La información se escribe al disco cuando se ha escrito un sector (llegue al final de disco) o cuando necesitemos buffers nuevos o la buffer caché esté llena. Con este método ahorramos espacio.

    Id_fitx

    Opens

    Reg. Lògic

    Mov

    Binary

    Sem

    buffer

    Esquema de la buffer caché

    Mailbox

    Un MBX interconecta dos o más procesos. Uno escribe i otro/s leen del MBX. Lo que lee un proceso no lo lee otro ya que suprimir la información, a no ser que el proceso que escribe ponga los datos por duplicado.

    El MBX no tiene dispositivo físico por lo tanto se implementa completamente en el nivel de Sistema.

    ONION ofrece 100 MBX del MBX00 al MBX99.

    El descriptor de dispositivo (DD) de un MBX es el siguiente:

    Struct DD_MBX{

    Struct DD part_comuna;

    Char buffer[1024];

    Int in, out;

    Int tamany; /* # bvytes ocupados del MBX

    Struct cua *q_iorb;

    Struct cua *q_iofin;

    Int sem_mutex;

    Int sem_gestor;

    }

    Para los MBX necesitamos un gestor por MBX, ya que todos los MBX se comportan de la misma forma, por lo tanto podríamos utilizar un solo gestor. Cuando un proceso lee de un MBX vacío el proceso se bloquea.

    Si en el gestor, primero hacemos una lectura de un MBX vacío el gestor se bloquea, aunque después le llegue un proceso que quiera escribir al mismo MBX, por lo tanto utilizaremos un gestor especial o dos gestores uno para lectura y otro para escritura. Pero con este método puede pasar que lleguen dos procesos con lectura y uno solo de escritura por lo tanto todavía seguiría bloqueado.

    La solución es utilizar funciones que no sea totalmente síncronas con un solo gestor.

    Gestor_mbx{

    For(;;){

    Wait(sem_gestor_mbx):

    Wait(sem_mutes_mbx);

    Iorb=primer(q_iorb);

    Signal (sem_mutex_mbx);

    Operacio(iiorb)

    }

    }

    typedef struct IORB_MBX{

    struct item link;

    struct DD_mbx *dd;

    int id_mbx;

    int id_io

    int operacio;

    .}

    operacio(iorb){

    si (iorb->op=LECTURA){

    si hi_ha_prous_bytes{

    passar_dades_usuari;

    acabar();

    }

    si no_hi_ha_prous_bytes{

    encuar(iorb, q_espera);

    }

    si (iorb->op=ESCRITURA){

    si hi_ha_espai{

    escriure;

    buscar_pet(q_espera);

    operacio(iorb);

    }

    sino encuar(iorb, q_espera);

    }

    }

    TEMA 6: GESTIÓN DE MEMORIA

    Introducción

    En un proceso se diferencian las zonas de código, de datos inicializados, de datos no inicializados y la pila, estos dos últimos no tienen una zona definida, ya que aumentan a la vez que se necesita la memoria. A medida que pedimos memoria dinámica el SO da memoria hacia abajo, i si queremos memoria de la pila, da memoria hacia arriba, por lo tanto el limite no esta definido, solo lo sabemos cuando llegamos a una dirección ya ocupada.

    Hoy en día el espacio lógico esta más mezclado, y la situación de cada parte del proceso la podemos situar donde queramos. Pero nos asaremos en el modelo antiguo donde el espacio lógico es continuo, pero que en el espacio físico puede no estarlo.

    Acciones del gestor de memoria

    Este espacio lógico se ha de llevar al espacio físico (memoria) permitiendo las siguientes características.

    Reubicación:

    • Dinámica: permite que el proceso se mueva en la memoria (con un registro que guarda la dirección base).

    • Estática: se lleva el proceso a la memoria en el tiempo de ejecución i mientras se ejecuta no se mueve.

    • Que permita un sistema multiproceso, por lo tanto debemos de controlar que no haya interferencias entre la zona de memoria de un proceso y la zona de memoria de otro proceso, es decir que estas zonas sean independientes. Para conseguir esto necesitaremos:

    • Tablas de traducción por proceso (están a memoria) son estructuras de datos grandes i cambian cada vez que hay un cambio de contexto.

    • Tabla de traducción para el SO.

    • Al haber diversos procesos pudiéndose ejecutar, necesitaremos proteger los datos de cada proceso, para evitar manipulación de los datos por un usuario que no controla los datos de dicho proceso.

    • El espacio lógico ha de ser no residente, es decir, que los datos pueden estar a disco o memoria, ya sea porque no tenemos suficiente memoria o porque otro proceso necesite la memoria. Este hecho se llama swapping (cuando se echa un proceso de memoria). Cuando se echa, un proceso, fuera de la memoria se echa entero, es decir que se saca de memoria el código, todos los datos y la pila.

    • Nos interesara también que la memoria lógica sea móvil, es decir, que la memoria lógica se pueda mover dentro de la memoria física. Por ejemplo al sacar un proceso (swap) i volver a introducirlo en la memoria, puede ser que las direcciones físicas que ocupaba antes, ahora estén ocupadas por otro proceso y por lo tanto se ha de poner en direcciones físicas diferentes.

    • La memoria ha de ser no contigua. Es decir, que el proceso tenga el código en unas direcciones físicas diferentes a las direcciones físicas de la pila y diferentes a las direcciones físicas de los datos, en cambio el espacio lógico de memoria si ha de ser contiguo. Para conseguir esto necesitaremos el hardware MMU (memory management unit) que traduce las direcciones lógicas a direcciones físicas, y las tablas de paginas contenidas en la MMU.

    La tabla de paginas (TP) es una estructura de datos bastante grande, por lo tanto, no siempre la tendremos en el hardware, por este motivo este proceso se traducción de dirección lógica a dirección física se hace, habitualmente, por software. Normalmente se implementa con listas, colas, etc. para ocupar la memoria estrictamente suficiente.

    También utilizaremos un TLB (Translation Lokkaside Buffer) que es un TP pequeña con pocas entradas y donde ponemos las direcciones lógicas del proceso que hace menos tiempo que hemos utilizado. Es como una caché de la TP. Tiene unas 64 entradas, entradas suficientes ya que es muy poco habitual ver un proceso utilizando estas 64 entradas. La TLB permite disminuir el tiempo de acceso a memoria.

    Ahora al hacer un cambio de contexto, se cambia la TP i se invalida la TLB ya que ahora hay otro proceso, por lo tanto ahora al principio el acceso a memoria se hace por TP i no por la TLB ya que ahora esta vacía.

    Una pagina hace referencia al espacio lógico, y un marco o frame hace referencia a una pagina en el espacio físico, una pagina tiene el mismo tamaño que un marco o frame.

    • El proceso no ha de tener que estar entero, es decir la memoria ha de ser no entera, ya que el proceso no utiliza al mismo tiempo todas las paginas de su memoria. Por lo tanto al ser no entero liberamos mas memoria para que sea utilizable por otros procesos, pero cuando un proceso necesita mas memoria, podemos saturar el rendimiento.

    • También necesitaremos memoria virtual.

    Memoria virtual

    La memoria virtual consiste en que el espacio lógico puede ser mucho más grande que el espacio físico de memoria. La memoria virtual puede llegar a los 4 gigas de espacio lógico (número máximo de direcciones direccionables por el procesador), y normalmente tenemos 64 Mb de memoria física. Por lo tanto la memoria que falta se implementa en el disco duro o área de swap.

    A las TP tendremos unos bits de información:

    • Bit de validez: que indica si la pagina está o si accedemos a espacio lógico no mapeado.

    • Bit de protección de la pagina con los cuales podemos hacer que una pagina solo se pueda leer, escribir, modificar, etc.

    • Bit de presencia que indica si la pagina está o no en memoria física (si no es así se produce un fallo de pagina, que trae la pagina lógica a memoria física).

    • Bits de modificación de la pagina.

    • Sticky bit es un bit de fijación que indica que la pagina lógica no se puede sacar de memoria física.

    La memoria virtual se basa en el principio de localidad, al igual que la memoria caché. Hay dos tipos de localidad:

    • Temporal: dice que lo que se utiliza en un tiempo se seguirá utilizando en el tiempo siguiente (bucle).

    • Espacial: si en un momento accedemos a una pagina lógica en instantes cercanos accederemos a paginas consecutivas (acceso a un array). El Prefetch trae las paginas lógicas a memoria física antes de que se utilicen.

    Creación de un proceso con MV

    Primero reservamos una zona de memoria, solo para cargar el código de la rutina main(), y la pila. Utilizamos poco espacio i solo tenemos lo que necesitamos, por lo tanto la carga de un proceso se hace más rápido. Las paginas lógicas modificadas se guardan en el área de swap (disco), esta área son bloques sin sistema de ficheros, para que el acceso a esta zona sea mas deprisa. En el área de swap se guardan las paginas lógicas modificadas. El SO va aumentando el área de swap cuando los procesos necesitan esta memoria en lugar de dar una cierta cantidad predeterminada a priori de memoria swap.

    Ejecución de un proceso con MV

    Una vez comenzada la ejecución se accede a memoria. Si la pagina no esta a memoria física, se produce el fallo de pagina que cargará la pagina deseada.

    Asignación de espacio

    -Paginación bajo demanda: vamos ejecutando el proceso y cuando se produce un fallo de pagina se lleva la pagina a memoria y se continua ejecutando el proceso. Al crear el proceso no tendremos nada a memoria. Sin ninguna optimización, por lo tanto el comienzo del proceso comienza con un fallo de pagina para llevar el código de la rutina main().

    -Prepaginación: si sabemos que cosas necesitaremos las cargamos antes y por lo tanto no hacemos los primeros fallos de pagina. Cargamos el código main, pila y los datos que utilizaremos al crear el proceso. Después tendremos paginación bajo demanda normal y corriente.

    -Prefetch dinámico: consiste en llevar las paginas que necesitamos y las paginas siguientes.

    MMU (Memory Management Unit)

    LA MMU es un hardware que ayuda a gestionar la memoria.

    La CPU calcula la dirección lógica que entran en la MMU además de una señal que indica si estamos en modo privilegiado o no. La MMU tiene la tabla de paginas del S.O. y la TP de las aplicaciones a partir de las cuales genera la dirección física.

    La MMU según el valor del modo escogerá la TP del S.O. o la de las aplicaciones. Si el modo es privilegiado se accede a la TP del S.O.

    Solo hay una TP de aplicaciones por lo tanto es tabla se vacía cada vez que hay un cambio de contexto (cambio de proceso ejecutándose).

    El bit de bypass hace que si modo es privilegiado y este bit es 0 se utiliza la TP del S.O. y si el bit es 1 se utiliza la TP de las aplicaciones para poder poner información en algún buffer o variable del usuario des de una llamada al sistema.

    Si la MMU no puede traducir la dirección lógica puede ser debido a alguno de los siguientes motivos.

    • La pagina lógica no está en memoria física y por lo tanto se crea una excepción de fallada de pagina.

    • Dirección ilegal, es decir, ha habido un intento de acceder a una pagina no valida, intento de acceder a una dirección lógica de otro proceso, o que esta fuera de rango de la memoria del proceso actual. Este caso crea una excepción, pero esta excepción hace que el proceso termine.

    • Debido a protecciones. Puede ser que la dirección lógica sea legal pero que el acceso no sea legal (escribir en una pagina que no tiene permiso para escribir), esto provoca una excepción. Estos fallos se utilizan en la gestión de l apila, para saber cuando se ha provocado un desbordamiento de dicha pila.

    Para controlar las excepciones las tablas de paginas tienen bits informativos:

    • Bit de validez: que indica si la pagina está o si accedemos a espacio lógico no mapeado. Indica si la pagina pertenece al espacio lógico del proceso o no.

    • Bit de protección de la pagina con los cuales podemos hacer que una pagina solo se pueda leer, escribir, modificar, etc.

    • Bit de presencia que indica si la pagina está o no en memoria física (si no es así se produce un fallo de pagina, que trae la pagina lógica a memoria física).

    • Bit de modificación de la pagina: indica si la pagina se ha modificado o no. Este bit es útil para los algoritmos de reemplazamiento.

    • Bits de acceso: indica si la pagina ha sido accedida o no, y cuantas veces se ha accedido.

    • Sticky bit es un bit de fijación que indica que la pagina lógica no se puede sacar de memoria física, como puede ser la pagina que gestione los fallos de pagina.

    Algoritmo de gestión de fallo de pagina

    Identificar la excepción producida.

    Si ¬ es fallo de pagina entonces se acabó

    Sino identificar la pagina lógica que ha producido el FP;

    Localizarla en el área de swap (disco);

    Buscar frame o marco libre;

    Si ¬ hay marco libre entonces

    Buscar pagina lógica que este a memoria física como víctima;

    Si ha estado modificada entonces guardarla al disco;

    Fsi

    Fsi

    Llevar la pag. lógica a memoria física;

    Reiniciar la instrucción que había provocado el FP.

    Fsi

    Gestión de la asignación de memoria física a los procesos

    Como se reparte la memoria física:

    • Por prioridad del proceso: según su prioridad.

    • Según estudios de la localidad del proceso: un proceso con mucha localidad necesita menos espacio de memoria.

    Existen dos mecanismos de asignación de memoria:

    • Asignación local: se trata de coger unas paginas físicas determinadas, por proceso. Cada vez que cree un fallo de pagina la pagina lógica se ha de introducir en alguna de estas paginas físicas determinadas a priori, y no se puede introducir en otras paginas diferentes a las asignadas al proceso.

    La ventaja de este método es que elimina las interferencias entre aplicaciones, por lo tanto tenemos un aumento del rendimiento de la maquina.

    La desventaja es el desequilibrio, es decir que el proceso necesita mas memoria que la asignada y haya memoria física disponible.

    • Asignación global: cada aplicación puede acceder a toda la memoria, por lo tanto ahora si el proceso necesita mas memoria la puede coger y si necesita menos espacio la puede liberar, pero en cambio puede producir interferencias entre los procesos (un proceso puede quitar de la memoria física las paginas de otro proceso).

    Si hay pocos fallos de pagina indica que tenemos memoria disponible y por lo tanto pueden haber procesos con sobrecarga de fallo de paginas, habiendo suficiente memoria física.

    Algoritmos de reemplazamiento

    Supondremos que tenemos asignación global.

    Estos algoritmos se aplican cuando tenemos todas las paginas físicas ocupadas y debemos llevar una pagina lógica a memoria física. Los algoritmos son los siguientes:

    Algoritmo optimo: garantiza el mínimo de número de fallos de paginas. Escoge como víctima la pagina que tardará mas tiempo en ser usada, pero saber esto es imposible. Sirve para hacer evaluaciones de otros algoritmos comparándolos con este.

    Aleatorio: escogemos una pagina víctima al azar. Este algoritmo es fácil de implementar pero no es muy eficiente comparándolo con el optimo, ya que puede escoger las paginas que utilizaremos a continuación. Por lo tanto este algoritmo también sirve de comparación.

    FIFO: aquí tenemos una tabla de paginas que se comporta como una cola, por lo tanto se van insertando paginas hasta que se ocupa todo el espacio, en este momento se vuelve a comenzar.

    0

    1

    2

    3

    0

    1

    4

    0

    1

    2

    3

    4

    0

    1

    2

    3

    0

    0

    0

    1

    4

    4

    0

    1

    2

    3

    0

    1

    1

    1

    4

    2

    2

    0

    1

    2

    3

    0

    1

    4

    4

    4

    2

    3

    3

    *

    *

    *

    *

    *

    *

    *

    *

    *

    Este mecanismo es muy sencillo, pero no tiene en cuenta la localidad y además presenta una anomalía (anomalía de Belady), que consiste en que si aumentamos la memoria física todavía podemos provocar mas fallos de paginas que con menos memoria.

    FIFO con 2ª oportunidad: en este caso necesitamos el bit de referencia o bit de acceso. Consiste en que cuando hemos leído una pagina el bit de referencia se pone a 1, y cuando debemos de quitar una pagina miramos dicho bit, si es 1 la pagina se vuelve a encolar y dicho bit se pone a 0, así hasta que encontramos la primera pagina con el bit de acceso a 0 la cual se quita de la cola.

    Existe una segunda versión que consiste en que cada vez que hay un fallo de pagina todos los bits de referencia se ponen a 0 y no solo uno como se hacia anteriormente.

    0

    1

    2

    3

    0

    1

    4

    0

    1

    2

    3

    4

    0

    0l

    0l

    3

    3

    3

    0l

    2

    0

    0

    1

    1

    1l

    0

    0l

    0l

    1l

    0

    1

    0

    1

    2

    2

    2

    1

    1

    1l

    4

    1

    3

    0

    1

    2

    3

    3

    3

    4

    4

    4

    2

    3

    4

    *

    *

    *

    *

    *

    *

    *

    *

    NRU (Not Recently Used): Utiliza dos bits, el bit de referencia y el bit de modificación, entonces ordenamos las paginas según estos bits. Escogemos como víctima la pagina que tenga el valor mas bajo codificado con estos bits, y si hay empate se utiliza al algoritmo FIFO. La paginas modificadas son las que mas tiempo tardan en salir, y así se reduce el tiempo de ejecución de la rutina de atención a un fallo de pagina, ya que una pagina modificada, primero se ha de leer y después escribir en disco.

    El bit de referencia se puede ir poniendo a cero si hace mucho tiempo que no accedemos, y se puede poner a cero con cada interrupción de reloj.

    LRU (Least Recently Used): escogemos como víctima la pagina que hace mas tiempo que no es referenciada. Vamos encolando las paginas lógicas y si se referencia una pagina en medio de la cola, lo que se hace es sacarla y volverla a encolar.

    1

    0

    2

    4

    3

    2

    3

    4

    5

    1

    0

    2

    4

    4

    2

    3

    1

    0

    2

    4

    3

    2

    3

    4

    1

    0

    2

    4

    3

    2

    3

    4

    5

    *

    *

    *

    *

    *

    *

    Normalmente tenemos una matriz de nxn bits donde n es el número de frames, entonces cuando hay una fallo de pagina se limpia la columna k (se pone toda a cero) y después se activa la fila k (se pone a 1), y entonces para escoger la víctima se escoge la que tiene valor mas bajo respecto las filas.

    NFU (Non Frequently Used): tiene un contador por cada frame que existe, entonces cuando se van referenciando los frames, y cada cierto tiempo se incrementa el contador con el bit de referencia y se pone a 0. Este algoritmo tiene el problema de la inercia (del contador), para evitarlo cada cierto tiempo o periodo se va reseteando el contador.

    Aging: tenemos un registro de bits (registro de historia) por cada pagina. El bit de mas peso es el de referencia. El algoritmo consiste en poner a 1 el bit de referencia cuando se referencia una pagina y cada cierto tiempo se hace un desplazamiento a la derecha del bit, entonces el algoritmo escoge como víctima la pagina con el valor del registro de historia mas pequeño.

    Políticas de gestión

    Working Set: ayuda a decidir cuantas paginas se asignan a un proceso. El working set es en concreto el conjunto de paginas lógicas que un proceso utiliza en un instante determinado. El periodo se llama Ventana (Finestra).

    Finestra 4

    1

    2

    3

    2

    3

    2

    4

    5

    6

    7

    Tamaño WS

    {1,2,3}

    {3,2}

    {4,5,6,7}

    Si el WS tiene 3 paginas lógicas el SO intentará asignar 3 paginas físicas al proceso para que no haya fallos de paginas. Si el tamaño del WS disminuye el SO puede desasignarle paginas físicas, etc.

    Esto haría que el SO estaría asignado y desasignando paginas físicas constantemente, por lo tanto el SO espera un momento antes de desagniar paginas físicas, por si en el futuro el proceso mas paginas físicas.

    Un tamaño pequeño de la Ventana (finestra), provoca que no sepamos cuantas paginas lógicas utiliza realmente un proceso, y por tanto estaría creando fallos de paginas constantemente.

    Un tamaño grande puede enganchar dos WS consecutivos y por lo tanto asignar mas paginas de la que realmente utiliza el proceso.

    PFF (Page Fault Frequency): contamos el % de fallos de paginas, esto se guarda en el PCB. La idea es que a partir de esta información el SO asigne las paginas que se necesitan. Se fijan dos límites (superior y inferior), si el % esta por encima del límite superior se asignan paginas físicas, y si esta por debajo del límite inferior se desasignan paginas.

    Hiperpaginación (trashing): un proceso tienen menos memoria física que la que necesita para trabajar (constantemente crea fallos de paginas). Es decir, crea un fallo de pagina y después de llevar la pagina a memoria física, se crea otro fallo de pagina, y esto provoca que el gestor se colapse y al aumentarle el número de paginas el espacio de memoria se reduce para otros procesos que pueden provocar fallos de paginas, los cuales también se encolan, y por lo tanto casi no se utilizaría la CPU. El trashing consiste en que los procesos están mas tiempo llevando paginas de disco a memoria (paginando) que no ejecutándose.

    Optimización de la gestión de memoria

    · Tener un pool de frames libres. Utilizamos el disco antes que la memoria se agote. Normalmente cuando hay fallo de pagina se salvar la víctima y se lleva una pagina nueva a memoria, con el pool de frames libres se lleva la pagina nueva a un frame libre y después se salva la víctima en paralelo con la ejecución del proceso.

    · Tener un conjunto de paginas semilibres: lo que hace es que si una pagina esta al pool de frames libres y se crea un fallo depagina que requiere esta pagina lo que se hace es marcar la pagina comom utiizable y deja de ser una pagina del pool de frames libres, y escogemos una pagina libre para asignar al pool de frames libres.

    Sistemas Operativos: 1

    Sistema

    BFS

    Basic File System (donde se implementa el sistema de ficheros).

    Núcleo

    IC

    S.O.

    IC

    IC

    IC

    S.O.

    Consola

    Consola

    Terminal

    Terminal

    Esquema de la conexión de dos ordenadores a través de la linea serie (terminal)

    Spooler

    F1.tmp

    F2.tmp

    F3.tmp

    PCB

    0

    4

    D.D.

    T.C (tabla de canales)

    Esquema de la conexión entre la PCB (proces control block) y el descriptor de dispositivo (D.D.) cuando se abre algún dispositivo.

    PROT

    UID

    OPENS

    SISTEMA

    Obrir(canal,nom, mode, prote)

    Llegir(canal, buff, lon)

    BFS

    TFA: tabla de ficheros abiertos

    Obrir_fitxer_bs (nom, crear, uid, mode, lon, Id_fitxer)

    Llegir_fitxer_bfs (id_ fitxer, buff, n_reg)

    File

    FAT Allocation

    Table

    NUCLEO

    Psocionar_pista_nuc (pista, drive)

    Llegir_sector_nuc (cara, sector, buff)

    Esquema de lo que hace el nivel BFS

    Q=8

    Q=16

    Q=32

    FCFS

    Esquema de las colas multinivel

    1

    9

    8

    7

    6

    5

    4

    3

    2

    User runnig

    Preemted

    created

    Ready to run

    in memory

    Sleep

    Swapped

    Ready to run swapped

    Aseelp in

    memory

    zombie

    Kernel

    running

    exit

    fork

    Trashing

    Llamada

    sleep

    Q=0

    + prioritario

    Int o S.C.

    int

    Esquema de los estados de UNIX

    -Prioritario

    +Prioritario

    Umbral

    3

    7

    realimentadas

    fijas

    Wait(hijo)

    Esquema de la pila de procesos bloqueados y expulsados

    Lib C

    Lib Pasc

    Lib Dos

    Lib Unix

    Lib VMS

    C

    Pas-cal

    UNIX

    VMS

    Proc_user

    Ret1=sci (par1, par2)

    Lib S.O.

    _sci proc far

    mov ax, i

    INT 64

    .

    .

    ret

    _sci endp

    TRAP()

    {

    run->sssp=salvar();

    switch (ax de run)

    { *(run->sssp+3);

    case 1: ..

    case i: sci();

    .

    .

    }

    restaurar(run->sssp)

    }

    Sci(dummy, par1, par2)

    {

    }

    1

    3

    2

    Par2

    Par1

    CS

    1.- @ retorno

    IP

    FLAGS

    CS

    2.- @ retorno

    IP

    Reg's

    CS

    3.- @ retorno

    IP

    BP

    dummy

    PILA

    Pcb

    sssp

    run

    i

    j

    k

    I

    J

    k

    i

    j

    k

    DIR

    A

    l

    i

    j

    k

    l

    I

    J

    K

    

    Esquema del bloque índice

    Esquema de acceso a través de la asignación indexada

    c

    c

    e

    e

    E

    i

    F

    C

    i

    C

    i

    D

    E

    i

    d

    f

    inodes

    i

    Esquema de compartición de un archivo

    Bytes

    1

    8

    3

    1

    2

    1

    16

    Filename

    Ext.

    Bloques que pueden contener el fichero. Si no coge en 16 bloques se necesitará otra entrada como esta.

    Indica cuantas entradas de estas se utilizan para guardar el fichero.

    Extert. Indica en que entrada nos encontramos

    Nombe del fichero

    Usuario (User)

    Índice del primer cluster

    Fecha de la última actualización

    Hora de la última actualización

    1

    2

    Rwxwx

    Propietario

    ...

    En ONION

    Atributos

    7

    6

    5

    4

    3

    2

    1

    0

    1:protección contra escritura

    1:Hidder, fichero oculto

    1:System, fichero de sistema

    1:Directorio

    0: fichero de datos

    Tabla de canales

    1 por proceso

    Con el UID, GID

    Cont

    ! l/e

    R/w

    Tabla de fichero abierto

    (TFA)

    inode

    Dueño

    1 por fichero

    Cont indica desde cuantas tablas de canales está siendo apuntado el fichero

    rw

    Tabla de Canales

    Id_fitxer

    ! l/e

    Descriptor de dipsositivo

    TFA

    Prot

    Dueño

    1 cluster

    FAT

    BFS

    F1

    .

    llegir (c, b, n)

    .

    Trap(){

    Switch ()

    Case llegir: llegir_generic(c, b, n)

    }

    Llegir_generic (c, ....){

    Switch (TC[c]->tipos)

    Case fitxer: llegir_fitxer(b, n)

    }

    SISTEMA

    Llegir_generic (c, b, )

    TC

    M

    DD

    P_llegir

    canal

    Llegir_especific (b, n){

    Llegir_esp_bfs;

    }

    Llegir_esp_bfs

    Llegir_esp_nuc

    SISTEMA

    BFS

    NUCLEO

    Independiente del dispositivo

    Dependiente del dispositivo

    .

    id_io=llegir (c, b, n)

    .

    esperar (id_io)

    .

    Llegir_especific (c, b, n){

    Perparar_iorb;

    Enviar_iorb (encolando)

    Signal_gestor

    Devolver id_ret

    }

    Gestor(){

    For(;;){

    Wait(sem_gestor);

    Iorb=desencuar();

    Procesar(iorb);

    Encolar (io_fin) // código de error

    Signal (id_io);

    }

    Esperar(id_io){

    Wait(id_io) //semafor associat al proces

    Ret=obtenier_resulatdo(io_fin);

    Devolver(ret)

    }

    Proceso

    Codigo

    Datos inicializados

    Datos no inicializados

    pila

    cpu

    Pagina

    offset

    TP

    10

    3 marco/

    frame

    24

    @ logica

    Frame

    offset

    MF

    Esquema de la traducción de @ lógica a @ física

    CPU

    TP S.O.

    TP Aplicacion

    MMU

    By pass

    modo

    @ logica

    R/W

    @ física

    Excepción

    Esquema de las entradas y salidas de la MMU

    Modif

    Ref

    0

    0

    0

    1

    1

    0

    1

    1




    Descargar
    Enviado por:Alberto Seco
    Idioma: castellano
    País: España

    Te va a interesar