Sistemas Operativos
Prácticas sobre sistemas de ficheros
Escuela Universitaria de Informática
E
ste documento define la primera fase de las prácticas de Sistemas
Operativos sobre implementación de módulos funcionales de un
sistema operativo.
Índice
El trabajo se llevará a cabo en dos etapas. En la primera se
realizará un diseño preliminar de los algoritmos y estructuras de
datos que se van a utilizar. Este diseño será sometido a
revisión por el profesor tutor de la práctica, que dará su
aprobación para la implementación definitiva, que será la
segunda etapa.
La implementación habrá de seguir el método y los
criterios de calidad expuestos en la práctica sobre programación
modular. Se diseñarán ficheros de interfaz y de
implementación, y se crearán los makefiles apropiados.
Una vez culminada la implementación, se elaborará un breve
informe/memoria del trabajo realizado, que será entregado al profesor
tutor para su evaluación.
En cualquier caso, es necesaria la lectura del capítulo 2
(Descripción general del sistema), donde se describe el sistema en
general, las características comunes, y algunos detalles relacionados
con el desarrollo en el laboratorio.
Esta es la documentación específica de cada práctica:
1.
Condiciones generales
1.1.
Tareas de los alumnos
La práctica asignada consiste en la implementación de un
componente funcional, según se describe en este documento. La
implementación se llevará a cabo en C o en C++, cumpliendo
estrictamente las interfaces propuestas.
1.2.
Guía de lectura
Este documento no ha de leerse por completo. Cada práctica necesita de
la documentación parcial de un número de capítulos. El
grado de profundidad con la que ha de verse un capítulo depende
también del tipo de práctica asignada. Así, el
desarrollador de la práctica de Sistema de Ficheros Simbólico ha
de leer el capítulo sobre el S.F. Básico, pero sólo para
conocer la interfaz de funcionamiento.
Simulación de disco capítulo 3(Simulación de un disco)
Caché de disco capítulos 4 (Sistema de Bloques Físicos) y 5
(Caché de disco)
Sistema de ficheros básico capítulos 4 (Sistema de Bloques Físicos) y 6
(Sistema de ficheros básico)
Sistema de ficheros simbólico capítulos 4 (Sistema de Bloques Físicos), 6
(Sistema de ficheros básico) y 7 (Sistema de
ficheros simbólico)
2.
Descripción general del sistema
Las cuatro prácticas que se proponen tienen por objeto la
construcción de componentes de un sistema de ficheros. El sistema se ha
estructurado en estas capas de software:
Sistema de Disco
Su misión consiste en proporcionar un archivo, manejado de manera análoga a como se hace con las unidades de disco, donde almacenar y posteriormente recuperar al información contenida en los archivos. La implementación de este sistema constituye la primera práctica.
Sistema de Bloques Físicos (SBF)
Su misión es principalmente ofrecer una interfaz simple de acceso al disco. Aparte, puede contener una caché de bloques para agilizar los accesos a los bloques físicos del sistema de disco. El desarrollo de la caché constituye la segunda práctica.
Sistemas de Ficheros
Este nivel es el más complejo, puesto que se pasa de trabajar con una secuencia de bloques a trabajar con ficheros. Debido a esta complejidad se ha descompuesto a su vez en dos niveles: Sistema de Ficheros Básico (SFB) y Sistema de Ficheros Simbólico (SFS), de alto nivel). En el SFB se resuelven los problemas de mantaner una organización simple de ficheros dentro del disco y gestionar el espacio libre. El SFE se encarga de trabajar con ficheros que tienen nombre simbólico y a los que se puede acceder byte por byte.
La tercera y cuarta prácticas consisten en la implementación de estos dos subsistemas.
La única práctica realmente de simulación es la primera; las tres restantes serían desarrollos de software similares a los correspondientes módulos de un sistema operativo real. Eso sí, serán módulos muy simples y con escasas prestaciones comparados con los sistemas reales.
2.1.
Las distintas organizaciones de los datos
El objeto de este apartado es describir cómo son "vistos", y por lo
tanto manejados, los datos en cada práctica (subsistema), sin olvidar
que nuestro sistema va a ser construido y ejecutado sobre un sistema UNIX de
desarrollo; por tanto, cualquier petición de acceso a los datos
perteneciente a los archivos del sistema diseñado finalmente
desembocará en la ejecución de una función del sistema UNIX.
2.1.1.
El Sistema de Disco
Como ya se ha mencionado, éste simula una unidad de disco
genérica mediante un fichero de nuestro sistema de desarrollo, por tanto
en la implementación de este módulo, se deberá hacer uso
de las primitivas de manejo de ficheros de bajo nivel de UNIX (open,
creat, read, write, lseek y close). A partir
de estas primitivas se desarrollarán unas funciones que accederán
a los datos del fichero UNIX de simulación como si éste fuese una
unidad de disco. Es decir, partiendo de una especificación en forma de
superficie, cilindro y sector (proveniente de los niveles superiores del
sistema a implementar y que conceptualmente está asociado a un sector
del disco simulado) las convertirán en una dirección de bajo
nivel para acceder a datos en un fichero UNIX en forma de desplazamiento del
origen del fichero y número de bytes para transferir (este campo
siempre tomará el mismo valor y será igual al tamaño de un
sector del disco simulado).
Si se utiliza una caché de bloques, las operaciones de lectura y escritura de bloques no siempre se corresponden con accesos al disco simulado.
La pregunta que surge a continuación es ¿Cómo se compatibilizan estas distintas visiones? y la respuesta es: de la misma manera que ocurre en los sistemas operativos reales y esto consiste en:
Al Sistema de Ficheros le llegan especificaciones simbólicas de usuario asociadas a:
Sistema de Ficheros que serán convertidas a especificaciones numéricas de sistemas de ficheros. En nuestro sistema cada sistema de ficheros se identificará por el nombre del fichero que simula a un disco y en el está almacenado, cuando se activa el sistema de ficheros (se avisa al sistema que va a ser accedido) entonces se abre dicho fichero y el descriptor de fichero devuelto pasará a ser el identificador numérico asociado al sistema de ficheros. Dicho descriptor de fichero será pasado como parámetro a las capas inferiores del sistema.
Ficheros en forma de cadena de caracteres que será convertida a identificador numérico asociado a la entrada de directorio que contiene la información de control del archivo.
Cadena de bytes en forma de posición de comienzo dentro del fichero y longitud, que será transformada a una especificación de bloque del sistema de ficheros básico y desplazamiento dentro del bloque, en primera instancia por parte del Sistema de Ficheros Simbólico o Básico, y posteriormente es transformado por parte del Sistema de Bloques Físicos a sector lógico y desplazamiento dentro del sector.
Al Sistema de Bloques Físicos le llegan especificaciones de
sector lógico, provenientes del Sistema de Ficheros Básico. En el
caso de que el sector especificado no se encuentre en memoria entonces
solicitará al Sistema de Disco una transferencia de dicho sector
lógico y en última instancia la especificación de sector
lógico se traduce a una del tipo (superficie, cilindro,
sector).
2.3.
Las interfaces de programación
Cada uno de los servicios de los cuatro niveles de este sistema de ficheros
tiene su propia interfaz. Las interfaces vienen impuestas; como
desarrolladores, ustedes se limitarán a implementar las funciones de
interfaz que se describen para la práctica que se les ha asignado.
La interfaces de los módulos se describen en capítulos subsiguientes: subsistema de disco (capítulo 3), caché de disco (capítulos ¡Error!No se encuentra la fuente de la referencia. y 5), sistema de ficheros básico (capítulo 6) y sistema de ficheros simbólico (capítulo ¡Error!No se encuentra la fuente de la referencia.).
En cualquier caso, todos los módulos tienen cosas en común, a saber: tipos de datos, tratamiento de errores y valores de retorno de las funciones. De ello se tratará en las siguiente líneas.
Los tipos son:
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
La mayoría de las funciones devuelven un int, que valdrá cero (0) si la función se ha cumplido correctamente. Si se produce cualquier anomalía o error, todas las funciones devuelven un -1.
Hay un mecanismo para obtener más información sobre el último error producido, que está inspirado directamente en las llamadas al sistema del UNIX. Consiste en utilizar una variable global llamada Error_SF cuyo valor se corresponde con un código de error.
Los códigos de error están preestablecidos y se muestran en el fichero /prac/include/errores.h, que habrán de incluir en sus fuentes en C.
Los códigos de error son valores numéricos, pero tienen su expresión simbólica, en la forma ERR_tipo. Así, por ejemplo, existe un símbolo de nombre ERR_RANGO que se usará para indicar que algún parámetro de la última función llamada estaba fuera de rango.
Si la variable Error_SF vale cero, no se ha producido ningún error.
La macro HAY_ERROR se puede usar como una expresión booleana que es cierta si se ha producido un error.
Hay algunas funciones que devuelven un valor útil, como las que abren ficheros, que devuelven un índice al fichero abierto. Si estas funciones fallan, también devuelven un -1.
Las funciones de informe quedan al libre albedrío de los implementadores, siempre que cumplan su misión, esta es, dar una panorámica en tiempo de ejecución del estado del servicio que están implementando.
Las funciones de informe tendrán un prototipo similar a éste:
void InformeXXX (void);
Estas funciones no devuelven ningún valor y, en principio, no tienen por
qué producir errores.
2.4.
Archivos en el laboratorio
Para realizar los trabajos prácticos se han depositado varios fuentes en
C con las interfaces (ficheros cabecera) más algunos ficheros de
implementación. También se irán introduciendo documentos
sobre la práctica, como por ejemplo este mismo texto.
Todos ello pende del directorio /prac, estructurado de esta manera:
/prac/include Ficheros cabeceras *.h /prac/src Fuentes de las implementaciones *.c /prac/lib Bibliotecas: implementaciones compiladas *.a /prac/doc Documentos relacionados
Para poder utilizar cómodamente las cabeceras y bibliotecas, habrán de modificar su fichero .profile, incluyendo una línea como la que sigue:
export CCOPTS=-Aa -D_HPUX_SOURCE -L/prac/lib -I/prac/include
Si, como es probable, ya tenían una línea similar en su fichero
.profile, sustitúyanla por la que se muestra aquí.
3.
Simulación de un disco
El objetivo de este trabajo práctico es la implementación de los
conceptos teóricos vistos en clase sobre estructura física,
organización de los datos y planificación de disco. En la
práctica a realizar se distinguirán dos niveles lógicos
bien definidos:
Nivel básico. Formado por todos aquellos módulos lógicos cuya misión es la de llevar a cabo las transferencias de datos solicitadas al más bajo nivel. Conceptualmente esta capa se correspondería con un manejador de disco.
Nivel de planificación. Cuya misión fundamental es la gestión de las peticiones de transferencia que llegan, por tanto en este nivel se encuentran implementadas las distintas políticas de planificación de disco. Otro aspecto importante es que en ella radica la interfaz con el sistema de memoria caché.
Bloque de parámetros físicos. Se trata de una tabla de tamaño fijo, en la que se distinguen los siguientes campos:
Número de cilindros.
Número de superficies.
Número de sectores por pista.
Tamaño en bytes del sector.
Tiempo de desplazamiento del brazo (seek).
Tiempo de Latencia.
Tiempo de transferencia.
Tiempo de recalibrado.
Área de sectores simulados. Se trata de una zona compuesta por registros de tamaño fijo, tanto el tamaño de los registros como el número de éstos se obtienen en base a la información contenida en el bloque anterior. Los registros de simulación de sectores de disco se disponen de la siguiente manera: en primer lugar los correspondientes a la primera superficie, luego a la segunda y así sucesivamente, para los asociados a una superficie dada, éstos se disponen por pistas, esto es: primero los asociados a la primera pista, a continuación los de la segunda y así sucesivamente hasta completar el número de pistas (cilindros) por superficie. Teniendo en cuenta esta organización, un aspecto que ha de resolver el diseñador o diseñadora es como pasar de una especificación del tipo (superficie, cilindro, sector) a número de registro de esta zona.
Inicializa_Disco que crea un fichero para simular una unidad de disco con unas características dadas.
Monta_Disco que prepara al sistema para poder acceder a una unidad de disco dada, ésta se ha de inicializar previamente.
Transfiere_Sector que realiza una trasferencia (lectura o escritura) de un sector lógico entre la caché y disco.
Posiciona_Cabeza que sitúa la cabeza de lectura y escritura de una unidad de disco sobre un cilindro dado.
Lee_Posicion que informa en qué cilindro se encuentra la cabeza
de lectura y escritura de una unidad dada.
Recalibra_Disco que efectúa una operación de recalibrado en una unidad de disco dada.
Este conjunto de funciones enunciado equivale a un manejador de disco elemental de un sistema operativo real. Seguidamente se pasa a describir con más detalle cada una de las rutinas anteriores.
errores.h códigos de error Tipos_DISCO.h Tipos de datos del sistema de disco Consta_DISCO.h Constantes del sistema de disco disco.h interfaz del sistema de disco
Todos estos ficheros se encuentran en el directorio /prac/include. Hay que resaltar que la interfaz de esta práctica ya está definida en disco.h y no se debería alterar.
Seguidamente pasamos a describir la interfaz del subsistema de disco.
#define "Consta_DISCO.h"
#define "Tipos_DISCO.h"
#define "Error_DISCO.h"
int Inicializa_Disco(char *nombre, bloq_param desc);
Inicializa_Disco inicializa un fichero, cuyo pathname se especifica mediante el parámetro nombre, para simular una unidad de disco cuyas características se especifican mediante el parámetro desc, si el fichero especificado no existe entonces lo creará y si ya existía, entonces lo reescribirá (perdiéndose por tanto la información contenida en él). Si la función se ejecuta con éxito, entonces devolverá un valor positivo igual al número de registros de simulación escritos en el fichero de simulación. En el caso de que se produzca un error durante su ejecución, entonces devolverá -1 y la variable global error_disco tomará un valor indicativo del motivo del error:
ERR_FORMAT Características inválidas de la nueva unidad.
ERR_ACCESO Error de lectura/escritura .
ERR_NOMBRE No se ha podido abrir/crear fichero.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Monta_Disco(char *nombre)
Monta_Disco lee el bloque de parámetros almacenado en el fichero (que simula a una unidad de disco) cuyo pathname se especifica mediante el parámetro nombre, una vez leído este bloque, ya la unidad virtual estará preparada para ser accedida por el sistema. Si la función se ejecuta con éxito retornará un valor no negativo que se corresponderá con un índice a la tabla de descriptores de unidades de disco del sistema, dicho índice deberá especificarse como identificador de unidad para las posteriores operaciones. En cambio, si se produce un error durante su ejecución entonces retornará -1 y la variable global error_disc tomará uno de los siguientes valores:
ERR_BLOQUE Error en la lectura del bloque de parámetros.
ERR_NOMBRE No se ha podido abrir/crear fichero.
ERR_MDISCO Alcanzado número máximo de unidades de disco.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Desmonta_Disco(unidad fd);
Desmonta_Disco desactiva una unidad de disco previamente activada mediante la función Monta_Disco, la unidad se especifica mediante el parámetro fd que es el descriptor de unidad. Si la función se ejecuta con éxito, entonces devuelve 0, en caso contrario, retornará -1 y la variable global error_disco tomará el valor ERR_DESCRI debido a que se ha pasado un descriptor de unidad de disco inválido.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Transferencia_Fisica(unidad fd, dir_fis direc, void *buff, int modo);
Tranf_Sector lleva a cabo la transferencia de datos entre la caché, a partir de la posición indicada por buff, y la unidad de disco especificada por fd, en el sector físico cuya dirección especifica direc. El tipo de transferencia se indicará mediante modo, pudiendo tomar este parámetro uno de los siguientes valores: LECTURA o ESCRITURA. Si la transferencia tiene éxito, entonces retornará 0, en caso contrario retornará -1 y la variable error_disco tomará alguno de los siguientes valores:
ERR_ACCESO Error de lectura/escritura .
ERR_DESCRI Descriptor de unidad de disco inválido.
ERR_DIRECC Dirección física de sector inválida.
ERR_MODO Modo de transferencia inválido.
Nota: Esta función simula los tiempos de latencia y de transferencia internamente mediante la llamadas a las funciones Tiempo_Late y Tiempo_Transf que proporciona la librería específica de esta práctica y que se les suministrará como material.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Posiciona_Cabeza(unidad fd, unsigned short int cil);
Posiciona_Cabeza sitúa la cabeza de lectura y escritura de la unidad de disco especificada por el descriptor fd en el cilindro especificado por cil. Si la función se ejecuta con éxito entonces retornará 0, en caso contrario retornará -1 y la variable global error_disco tomará alguno de los siguientes valores:
ERR_DESCRI Descriptor de unidad de disco inválido.
ERR_DIRECC Especificación de cilindro inválido.
Nota: Esta función simula el tiempo de posicionamiento del brazo internamente mediante la llamada a la función Tiempo_Seek y que proporciona la librería específica de esta práctica y que se les suministrará como material.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Lee_Posicion(unidad fd);
Lee_Posicion devuelve el número de cilindro en el que se encuentra la cabeza de lectura y escritura de la unidad asociada a fd. Si fd posee un valor incorrecto entonces devolverá -1 y la variable error_disco toma el valor ERR_DESCRI, en caso contrario, es decir si se ejecuta con éxito, entonces devolverá el cilindro en el que está ubicada la cabeza de lectura y escritura.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Recalibra_Disco(unidad fd);
Recalibra_Disco efectúa una operación de recalibrado en la unidad asociada a fd. Si fd posee un valor incorrecto entonces devolverá -1 y la variable error_disco toma el valor ERR_DESCRI, en caso contrario, es decir si se ejecuta con éxito, entonces devolverá el valor 0 y la cabeza estará en el cilindro 0.
Nota: Todo hardware de control de disco incorpora una facilidad de recalibrado de los elementos mecánicos de las unidades de disco, esta operación tiene por finalidad anular las inercias que se producen en dichos elementos. La función Recalibra_Disco simula el tiempo de recalibrado invocando a la función Tiempo_Recal que se proporciona en la librería específica de esta práctica.
#include "Tipos_DISCO.h"
int Informe_Disco (void);
Esta función imprimirá por la salida estándar un informe de estadísticas de uso y estado general del disco simulado.
No producirá errores en ningún caso.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Transfiere_Sector(unidad fd, sector_logico sect , void *buff, int modo);
Transfiere_Sector lleva a cabo la transferencia de un sector lógico, especificado por sect, entre la caché y la unidad de disco especificada por fd. El tipo de transferencia se especifica en modo pudiendo adoptar éste parámetro los valores LECTURA o ESCRITURA. Si la transferencia se ejecuta con éxito entonces esta función retornará 0, en caso contrario retornará -1 y la variable global error_disco tomará alguno de los siguientes valores:
ERR_DESCRI Descriptor de unidad de disco inválido.
ERR_DIRECC Dirección lógica de sector inválida.
ERR_ACCESO Error de lectura/escritura.
ERR_MODO Modo de transferencia inválido.
#include "Consta_DISCO.h"
#include "Tipos_DISCO.h"
int Dirección_Física(unidad fd, sector_logico sect, dir_fis *dir);
Dirección_Física obtiene la dirección del sector físico asociado al sector lógico, especificado por sect. El parámetro fd indica la unidad en donde se encuentra el sector. Si la función se ejecuta con éxito, entonces retornará 0 y el *dir contendrá la dirección física calculada, en el caso de que ocurra algún error, entonces retornará -1 y la variable global error_disco tomará alguno de los valores siguientes:
ERR_DESCRI Descriptor de unidad de disco inválido.
ERR_DIRECC Dirección inválida de sector lógico.
Dada su simplicidad, el SBF no constituye una práctica en sí mismo, aunque tres de los trabajos propuestos (Caché, S.F. Básico y S.F. Simbólico) han de utilizar sus servicios. Para ello se ha dispuesto un fuente en C con una implementación simple del SBF.
El disco virtual se define como un conjunto de bloques físicos contiguos de tamaño fijo, como muestra el diagrama. Los bloques físicos están numerados de cero en adelante.
4.1.
El superbloque
El primer bloque de un disco virtual se llama superbloque y contiene una
serie de campos que informan de las características del disco (longitud,
tamaño de sector, etc.)
El superbloque sólo se puede utilizar para leer o escribir esta información. No se pueden utilizar los campos del superbloque con fines distintos a los que fueron pensados.
Así pues, el primer bloque físico realmente utilizable es el número 1 (uno), ya que el bloque cero tiene una finalidad específica: es el superbloque.
Los campos del superbloque se muestran en este esquema, en el mismo orden en que se guardan en el disco.
char magico[8] Cadena de caracteres para verificar el formato WORD tam_bloque_fisico Tamaño en bytes de un bloque físico DWORD num_bloques_fisicos Cantidad total de bloques físicos WORD BF_bloque_datos Bloques físicos por cada bloque de datos WORD bytes_bloque_datos Tamaño efectivo en bytes de un bloque de datos DWORD num_BF_reservados Bloques físicos reservados por el SFS WORD tam_BFB Tamaño en bytes de un bloque de fichero básico WORD num_BFB Número de BFBs reservados WORD tam_DFE Tamaño en bytes de un DFE
Los distintos niveles del sistema de ficheros utilizarán y, en su caso, alterarán estos campos, según lo descrito en cada capítulo de este documento.
El único campo que es pertinente resaltar ahora es magico. Este campo es una cadena de ocho caracteres cuyo único propósito es dar un indicio de que el archivo UNIX puede ser un sistema de ficheros de esta práctica. Se supone que todos los discos formateados en las prácticas deberían tener la misma cadena en magico. El valor que se propone en los ficheros de interfaz es "SOPA123", que pueden cambiar para diferenciar sus sistemas de ficheros de los de otros compañeros, o entre distintas versiones de sus mismos programas.
Para manipular cómodamente el superbloque, se dispone de un tipo de datos, struct Superbloque, que se define en el fichero de interfaz <superblq.h>
Además, el Sistema de Bloques Físicos implementa sendas funciones
para leer y escribir el superbloque: LeeSuperbloque y
EscribeSuperbloque.
4.2.
Funciones del nivel físico
Todos los accesos al disco virtual en esta práctica se han de basar en
estas funciones, que aparecen en el fichero cabecera
<sbfisico.h>
/* Instalación y montaje de discos */
int InstalaDisco ( const char* nombre_fichero,
WORD tam_bloque, DWORD num_bloques );
int MontaDisco ( const char* nombre_fichero, int nbloques_cache );
int DesmontaDisco (void);
/* Acceso al superbloque */
int LeeSuperbloque ( struct Superbloque* bloque );
int EscribeSuperbloque ( const struct Superbloque* bloque );
/* Lectura y escritura de bloques físicos */
int LeeBloqueFisico ( DWORD nbloque, void* bufer );
int EscribeBloqueFisico ( DWORD nbloque, void* bufer );
int InstalaDisco ( const char* nombre_fichero,
WORD tam_bloque, DWORD num_bloques );
Esta función crea un fichero de nombre nombre_fichero, estructurado como Sistema de Bloques Físicos, conteniendo num_bloques bloques físicos, cada uno con un tamaño en bytes igual a tam_bloque.
La función se encarga de crear un superbloque con los valores adecuados en los campos magico, tam_bloque_fisico y num_bloques_fisicos.
La función devuelve un -1 en caso de error, depositando en la variable Error_SF el código de error, que puede ser:
ERR_DISCO Error al intentar crear el fichero UNIX ERR_RANGO Parámetros fuera de rango ERR_ESPACIO No había espacio suficiente en disco
int MontaDisco ( const char* nombre_fichero, int nbloques_cache );
Esta función monta el fichero nombre_fichero para su uso como Sistema de Bloques Físicos. nombre_fichero debió haber sido creado previamente con la función InstalaDisco. Sólo puede haber un disco montado.
El parámetro nbloques_cache indica cuántos bloques de desea que contenga la caché de bloques físicos. El sistema de caché intentará habilitar una caché de ese tamaño; si no es capaz se creará una caché con el máximo tamaño que sea posible.
La función devuelve un -1 en caso de error, depositando el código de error en Error_SF. Algunos errores posibles son:
ERR_NOEXISTE El fichero UNIX no existe ERR_DISCO Error con el fichero UNIX ERR_MONTADO Ya hay un SBF montado ERR_FORMATO El fichero no tiene formato de SBF
int DesmontaDisco (void);
Desmonta el disco actualmente en uso. Esta función devuelve un -1 en caso de error. El código de error que se guarda en Error_SF puede ser:
ERR_DISCO Error con el fichero UNIX ERR_INACTIVO No hay ningún SBF montado
int LeeSuperbloque ( struct Superbloque* bloque );
int EscribeSuperbloque ( const struct Superbloque* bloque );
Estas dos funciones se encargan, respectivamente, de leer y escribir el superbloque del SBF montado. Las transferencias se efectúan sobre la estructura apuntada por bloque.
Ambas funciones devuelven un -1 si se ha producido un error. Los valores posibles de Error_SF son:
ERR_INACTIVO No hay ningún SBF montado
int LeeBloqueFisico ( DWORD nbloque, void* bufer );
int EscribeBloqueFisico ( DWORD nbloque, void* bufer );
Estas dos funciones se encargan, respectivamente, de leer y de escribir bloques físicos pertenecientes al SBF montado actualmente. El número de bloque físico para transferir viene dado por nbloque. La transferencia se efectúa sobre la zona de memoria apuntada por bufer. El llamador es responsable de que bufer apunte a una zona con espacio suficiente para un bloque físico.
Ambas funciones devuelven un -1 si se ha producido un error. Los valores posibles de Error_SF son:
ERR_INACTIVO No hay ningún SBF montado ERR_RANGO Bloque físico fuera de rango
4.4.
Fuentes en C
Los archivos del Sistema de Bloques Físicos son los siguientes:
/prac/include/sbfisico.h Interfaz del SBF /prac/include/superblq.h Estructura del superbloque /prac/src/sbfisico.c Implementación del SBF
La implementación de muestra en sbfisico.c es el punto de partida
para la práctica del sistema de caché. Los desarrolladores de los
trabajos de Sistema de Ficheros Básico y Simbólico harían
bien en echar un vistazo al código de este fichero.
5.
Caché de disco
Un sistema de ficheros es una estructura de datos compleja instalada sobre un
soporte persistente, el disco, diferente de la memoria principal. Olvidando
esta diferencia crucial, las similitudes entre las estructuras de datos en
memoria y en disco son bastantes. Por ejemplo, el principio de localidad se
cumple también con los ficheros: es muy probable que los accesos futuros
a un fichero se hallen próximos a los hechos en el pasado reciente.
Por otro lado, en todo sistema de ficheros suele haber un conjunto de bloques de disco que contienen información descriptiva del sistema (en nuestro caso, puede ser el superbloque); o bien estructuras de gestión del sistema: mapas de bits, estructuras de directorio, bloques descriptores de fichero, etc. Si todos esos bloques se utilizan con mucha frecuencia, puede ser deseable mantenerlos en memoria de forma más o menos permanente.
La existencia del principio de localidad y de los bloques muy usuados invitan al desarrollo de un sistema de caché que almacene los bloques de disco que se consideren mejores candidatos a ser accedidos en un futuro inmediato.
Se propone así un trabajo práctico en el que se
diseñará e implementará una caché de bloques
físicos de disco, aplicando varias políticas de las estudiadas en
la asignatura.
5.1.
Interfaz con el disco manejado
La caché operará sobre discos virtuales, implementados sobre
ficheros UNIX. La organización del disco y su interfaz funcional
serán las definidas en el Sistema de Bloques Físicos (ver
página 16). Los desarrolladores dispondrán de varios fuentes en C
con la interfaz y la implementación de las funciones de manejo del
disco.
Los archivos que han de manejar son:
sbfisico.h Interfaz con el disco virtual sbfisico.c Implementación de las funciones del disco tipos.h Tipos de datos básicos errores.h Constantes de error superblq.h Estructura del superbloque
La función MontaDisco ha de reconocer el parámetro nbloques_cache, que indica cuántos bloques físicos se desean para la caché. El sistema que implementen tomará este parámetro como una simple indicación. En caso de que, por problemas de espacio, no se pueda habilitar una caché del tamaño solicitado, se dispondrá del máximo tamaño posible. La función MontaDisco nunca producirá un error por problemas de espacio de caché.
Por último, es planteable, aunque no obligatorio, modificar las funciones LeeSuperbloque y EscribeSuperbloque de manera que el superbloque del disco sólo se cargue una vez y se mantenga en memoria de forma permanente.
Según se ve, el trabajo consiste principalmente en alterar la implementación de los servicios del Sistema de Bloques Físicos, dejando la interfaz intacta. El punto de partida será una implementación primitiva, sin caché, que encontrarán en /prac/src/sbfisico.c
Aparte de lo anterior, se tendrá que crear una función para imprimir un informe sobre la caché. Su prototipo será
void InformeCache (void);
y devolverá principalmente información sobre estadísticas
de uso, como tasa de éxitos de la caché, número de accesos
totales, de lectura y de escritura, etc.
5.3.
Políticas de la caché
En el trabajo se han de probar tres políticas de sustitución.
Implementarán la LRU (menos recientemente usada), la LFU (menos
recientemente usada) y otra más de su elección.
Las tres políticas se implementarán en respectivas versiones del
programa, para poder probarlas por separado.
6.
Sistema de ficheros básico
En este trabajo se creará un sistema de ficheros muy primitivo a partir
de un disco simulado. Partirán de unas funciones elementales que
gestionan un disco virtual, definidas en el Sistema de Bloques Físicos.
A partir de ellas, confeccionarán un módulo funcional que permita
operar con ficheros muy simples, llamados ficheros básicos.
6.1.
El fichero básico
Un fichero básico es una secuencia de bloques de datos, los cuales
podrán leerse o escribirse con funciones que ustedes
desarrollarán. El fichero básico se identifica mediante un
número: carece de nombre simbólico. El número que
identifica un fichero básico se llamará índice a
fichero básico (IFB).
Un fichero básico estará compuesto de un bloque de fichero básico (BFB) y un conjunto de bloques de datos. El BFB contendrá la información necesaria para conocer qué bloques de datos pertenecen al fichero (los datos exactos dependerán del esquema de administración de espacio que ustedes escojan).
6.2.
Operaciones con ficheros básicos
En la práctica, implementarán una API para trabajar con estos
ficheros básicos. Las funciones que tendrán que construir
básicamente son para:
· crear y destruir ficheros
· añadir bloques a un fichero o truncarlo
· leer y escribir bloques de un fichero al azar
Observen que los ficheros básicos no tienen nombre en forma de cadena de caracteres, sino un simple identificador numérico. Observen también que los accesos al cotenido de un fichero básico son por bloques completos, nunca por bytes individuales. El desarrollo de servicios más elaborados y próximos a la interfaz de UNIX o MS-DOS se realiza en el nivel del Sistema de Ficheros Simbólico, que es otra práctica de la asignatura.
La API está definida y se expone en estas páginas. Ustedes
sólo tendrán que implementarla, eso sí, de forma que
cumpla con las especificaciones dadas.
6.3.
Organización física del disco
Desde la óptica del programador, el punto de partida del Sistema de
Ficheros Básico es un conjunto de servicios para gestionar un disco
virtual, estructurado como una secuencia de bloques físicos contiguos de
tamaño fijo. Estos servicios se definen e implementan en el nivel
llamado Sistema de Bloques Físicos (SBF). Tal nivel se describe
en este texto a partir de la página 16. Lean atentamente el apartado
para tener claras las funciones del SBF. También tienen disponibles una
implementación de muestra del SBF, en el archivo
/prac/src/sbfisico.c, que deberían estudiar y utilizar en sus
compilaciones.
6.4.
Estructura del sistema de ficheros básico
Aunque tendrán cierta libertad, el sistema de ficheros que
diseñen se ceñirá a la estructura que se muestra en este
diagrama.
Salvo el superbloque, las distintas áreas del disco tienen un tamaño variable, que se define cuando se formatea el disco. Una vez formateado, no se pueden alterar los tamaños de esas áreas.
La rutina de formateo se invoca de la siguiente forma:
FormateaSFB ( nombre_fichero, BF_reservados,
tamaño_DFE, tamaño_bloque_datos,
número_de_ficheros );
WORD BF_bloque_datos Bloques físicos por cada bloque de datos WORD bytes_bloque_datos Tamaño efectivo en bytes de un bloque de datos DWORD num_BF_reservados Bloques físicos reservados por el SFS WORD tam_BFB Tamaño en bytes de un bloque de fichero básico WORD num_BFB Número de BFBs reservados WORD tam_DFE Tamaño en bytes de un DFE
Según se avance en el documento, se irán manifestando los puntos donde se determinarán la razón de ser y los valores apropiados de cada uno de estos campos.
Habrá que insistir en que los restantes campos del superbloque no se deberían alterar directamente en este nivel.
El tamaño de esta área viene dado por el argumento BF_reservados de la rutina de formateo. El área de bloques reservados puede ser nula (tamaño cero).
El contenido del BFB dependerá de la implementación que ustedes escojan. Campos típicos del BFB pueden ser: tamaño del fichero (en número de bloques), apuntadores a los bloques de datos del fichero, un campo que indique si el BFB está libre u ocupado, etc. Aparte, hay que reservar espacio para el DFE (ver siguiente apartado).
Cada BFB ocupará un tamaño fijo, que será determinado por ustedes. Habrán de calcular cuántos BFB caben en un bloque físico, así como cuántos bloques físicos hacen falta para albergar todos los BFB requeridos.
El Sistema de Ficheros Básico no ofrece ninguna facilidad como nombres simbólicos de ficheros, permisos de uso, fechas de última modificación, etc. Previendo que otros usuarios quieran añadir campos a sus ficheros, se define un área por cada BFB llamada descriptor de fichero extendido (DFE).
El espacio reservado para el DFE en cada BFB es fijo y se declara en el momento del formateo del disco (argumento tamaño_DFE).
En esta práctica no se han de preocupar de qué contiene el DFE. Simplemente consideren que es un espacio adicional reservado en cada BFB.
El número de bloques reservados queda a criterio de los implementadores, o sea, ustedes. Su situación siempre ha de ser posterior a los BFB, aunque no necesariamente contigua a ellos.
Si emplean asignación de espacio enlazada, parte del bloque de datos se consume en los encadenamientos. Por ello hay un campo del superbloque que indica cuántos bytes de un bloque de datos son realmente utilizables: bytes_bloque_datos.
Los bloques de datos habrán de estar numerados de UNO en adelante.
Si usted va a realizar la práctica del Sistema de Ficheros Básico, deberá implementar las funciones aquí descritas.
Si usted va a realizar la práctica del Sistema de Ficheros Simbólico, ha de leer estas páginas como referencia de los servicios que se le proporcionan en el Sistema de Ficheros Básico.
#include <sfbasico.h>
A su vez, sfbasico.h tendrá que incluir lo siguiente:
#include <sbfisico.h>
#include <errores.h>
#include <superblq.h>
donde se declaran la interfaz con el Sistema de Bloques Físicos, los tipos básicos, las constantes de error y la estructura del superbloque.
Los ficheros cabeceras se encuentran instalados en el directorio /prac/include
#define CONTIGUO
Otras constantes se utilizan para que niveles superiores sepan cuáles son ciertos límites del Sistema de Ficheros Básico:
#define MAX_BLOQUES_CONTIGUOS 1
#define MAX_FICHEROS 20000
La primera define cuántos bloques contiguos se pueden solicitar; sólo tiene sentido en almacenamiento contiguo. La segunda define la cantidad máxima de ficheros que puede tener un SFB.
Los valores expuestos son sólo a título de ejemplo.
Este es el tipo de datos para trabajar con ficheros básicos:
typedef unsigned short IFB;
Los servicios de este módulo sólo trabajarán con un único disco montado.
int FormateaSFB ( const char* nombre_fichero,
WORD BF_reservados, WORD tam_DFE,
WORD tam_bloque_datos, WORD num_ficheros );
Da formato a un disco virtual, especificado por nombre_fichero. El disco virtual ha de estar ya instalado con la función de bajo nivel InstalaDisco().
Si se produce un error, esta función devuelve -1 y la variable global Error_SF tomará alguno de estos valores:
ERR_DISCO Error con el fichero UNIX ERR_PARAMETRO Parámetros no válidos ERR_FORMATO Error de formato del fichero
int MontaSFB ( const char* nombre_fichero, int nbloques_cache );
Esta función monta un sistema de ficheros ya formateado, que se encuentra en el fichero UNIX nombre_fichero. Para utilizar cualquier función que trabaje con ficheros básicos, hay que llamar previamente a esta función.
El parámetro nbloques_cache indica al SBF el número de bloques deseado para la caché. El SBF podría ignorar este parámetro.
No se puede mantener más de un sistema de ficheros activo. Si se llama a MontaSFB mientras se tiene un SFB activo, se considera un error.
Esta función devuelve -1 si hay un error. Algunos valores de la variable Error_SF pueden ser:
ERR_NOEXISTE Fichero inexistente ERR_FORMATO Error de formato del fichero ERR_MONTADO Ya hay un SFB montado
int DesmontaSFB (void);
Esta función se invoca para desmontar el SFB actualmente montado.
Esta función devuelve -1 si hay un error. Algunos valores de la variable Error_SF pueden ser:
ERR_DISCO Error con operaciones de bajo nivel sobre el fichero UNIX ERR_INACTIVO No hay un SFB montado
WORD TamBloqueDatos (void);
Esta función retorna el tamaño efectivo en bytes de un bloque de datos. Es una forma simple de acceder a la configuración del superbloque, y es útil para que las rutinas del Sistema de Ficheros Simbólico puedan trocear sus transferencias.
La función devuelve un (unsigned)-1 si no hay un SFB montado, con lo que Error_SF pasaría a valer ERR_INACTIVO.
IFB CreaFB ( WORD num_bloques );
La función CreaFB ubica un bloque de fichero básico y le asigna num_bloques bloques de datos. El contenido de los bloques de datos es indefinido.
Esta función devuelve un entero que corresponde con el fichero básico seleccionado. Este valor puede usarse en las operaciones de lectura, escritura o manipulación de DFE. En caso de que no pueda completar la operación, devuelve un -1. Si no se consiguen los num_bloques, no se crea el fichero.
Algunos valores de la variable Error_SF pueden ser:
ERR_ESPACIO No hay BFB libres o bloques de datos suficientes ERR_INACTIVO No hay un SFB montado
int DestruyeFB ( IFB fichero );
La función DestruyeFB desasigna todos los bloques de datos pertenecientes al fichero número fichero y libera el BFB correspondiente.
Esta función devuelve -1 si se produce un error. Algunos valores de la variable Error_SF pueden ser:
ERR_RANGO IFB fuera de rango ERR_OCUPADO El BFB ya estaba disponible ERR_INACTIVO No hay un SFB montado
WORD LongitudFB ( IFB fichero );
Devuelve el número de bloques de datos asignados al fichero.
Si hay alguna anomalía, se devuelve un ((unsigned)-1). Algunos valores de la variable Error_SF pueden ser:
ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
int ExpandeFB ( IFB fichero, WORD num_bloques );
Añade num_bloques al fichero. Los bloques se añaden siempre por el final (apéndice). El contenido de los nuevos bloques es indefinido. El contenido de los bloques de datos y asignados no ha de variar.
Si no hay espacio para satisfacer la petición, no se asigna ningún bloque y la función devuelve un -1.
ExpandeFB devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_ESPACIO Espacio insuficiente: no se asignan los bloques pedidos ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
int TruncaFB ( IFB fichero, WORD num_bloques );
La función TruncaFB ajusta el tamaño de fichero a exactamente num_bloques. num_bloques ha de ser menor o igual al tamaño actual del fichero, y puede valer cero.
Los bloques de datos desasignados deberían quedar disponibles para otros ficheros.
La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_PARAMETRO num_bloques es mayor que el tamaño del fichero ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
int LeeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer );
Esta función lee el bloque de datos num_bloque perteneciente a fichero y lo deposita en la zona de memoria apuntada por bufer.
El llamador de esta función es responsable de que bufer apunte a un lugar correcto y que tenga espacio suficiente para albergar un bloque de datos, que viene determinado por el campo bytes_bloque_datos del superbloque del S.F.B. actualmente montado.
La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_NOEXISTE num_bloque está fuera del rango del fichero ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
int EscribeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer );
La función EscribeBloqueFB copia la zona de memoria apuntada por bufer en el bloque de datos num_bloque perteneciente a fichero. Se copian exactamente los bytes que ocupa un bloque de datos del S.F.B. actualmente montado, según el campo bytes_bloque_datos del superbloque.
El llamador de esta función es responsable de que bufer apunte a un lugar correcto en la memoria.
La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_NOEXISTE num_bloque está fuera del rango del fichero ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
int LeeDFE ( IFB fichero, void* estructura );
Esta función copia el DFE de fichero en la zona de memoria apuntada por estructura. El llamador de la función es el responsable de que estructura apunte a una dirección de memoria correcta, capaz de albergar un DFE.
El tamaño del DFE ha de estar almacenado en el campo tam_DFE del superbloque del S.F. actualmente montado.
La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
int EscribeDFE ( IFB fichero, void* estructura );
Esta función copia la zona de memoria apuntada por estructura en el DFE de fichero. Se copian exactamente los bytes que ocupa un DFE del S.F.B. actualmente montado, según el campo tam_DFE del superbloque. El llamador de la función es el responsable de que estructura apunte a una dirección de memoria correcta.
La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_RANGO IFB fuera de rango ERR_LIBRE IFB no ocupado ERR_INACTIVO No hay un SFB montado
Sería muy recomendable que las funciones que gestionan bloques de datos: CreaFB, DestruyeFB, ExpandeFB, TruncaFB; llamaran a estas dos funciones.
DWORD ReservaBloqueDatos (void);
Esta función reserva un bloque de datos libre.
Devuelve un número que identifica el bloque de datos asignado.
Si se da algún error o no hay más espacio disponible, se devuelve (unsigned)-1. Algunos valores de la variable Error_SF pueden ser:
ERR_ESPACIO No hay más bloques de datos libres ERR_INACTIVO No hay un SFB montado
int LiberaBloqueDatos ( DWORD bloque );
Esta función devuelve al espacio libre el bloque de datos indicado por bloque.
Devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:
ERR_RANGO Bloque de datos fuera de rango ERR_LIBRE Bloque de datos ya disponible ERR_INACTIVO No hay un SFB montado
DWORD ReservaSegmentoDatos ( DWORD num_bloques );
Reserva num_bloques bloques de datos contiguos. Devuelve el número de bloque del primer bloque de datos del conjunto.
int LiberaSegmentoDatos ( DWORD bloque_inicial, DWORD num_bloques );
Devuelve al espacio libre un conjunto de bloques de datos, un total de num_bloques a partir de bloque_inicial.
Las condiciones de error serían similares a las dos anteriores funciones.
Esta función imprimirá por la salida estándar un informe con las estadísticas de uso y el estado general del sistema de ficheros actualmente montado, si lo hay.
Esta función no retorna nada y no genera ningún error.
6.6.
Ejemplos de uso
En este apartado se muestran algunos ejemplos simples del uso de los servicios
del Sistema de Ficheros Básico.
Los ejemplos son fragmentos de código para insertar en un programa. Por mayor legibilidad, los ejemplos están escritos en C++.
#include <sfbasico.h>
// ...
// Se instala un disco de 12000x512 = 6Mbytes
InstalaDisco ( "disco.dat", 512, 12000 );
// Estos dos valores provendrían de un nivel superior
int reservados = 24; // bloques físicos reservados
int dfe = 64; // espacio reservado para el DFE
// Se da formato al disco con
// tamaño de bloque = 2 bloques físicos(1Kbyte)
// y un máximo de 500 ficheros
FormateaSFB ( "disco.dat", reservados, dfe, 2, 500 );
#include <sfbasico.h>
// ...
MontaSFB ("disco.dat");
// Crea un fichero tonto de 10 bloques
int fich = CreaFB (10);
// añade un bloque más
ExpandeFB(fich,1);
// Desmonta el disco
DesmontaSFB();
#include <sfbasico.h>
// ...
struct Superbloque SB; // Guardará los parámetros del disco
MontaSFB ("disco.dat");
LeeSuperbloque (&SB);
// Reserva espacio para escribir en un bloque
char* bufer = new char[SB->bytes_bloque_datos];
// Otra forma de hacerlo
char* bufer = new char[TamBloqueDatos()];
// Escribe algo en el primer bloque del fichero 33
strcat(bufer,"Hola, mundo\n");
EscribeBloqueFB (33,0,bufer);
// lee el tercer bloque del fichero 98
LeeBloqueFB (98,2,bufer);
for (int i=0; i<SB->bytes_bloque_datos; i++)
{
printf ( "bufer[%d] = %d\n", i, bufer[i] );
}
// Desmonta el disco, que ya no hace falta
DesmontaSFB();
7.
Sistema de ficheros simbólico
Esta práctica consiste en el desarrollo de una parte de un sistema de
ficheros, concretamente se refiere a lo que hemos denominado Sistema de
Ficheros Simbólico, en el que se soportan las siguientes funciones:
Crea_Sistema Crea un sistema de ficheros Activa_Sistema Prepara al sistema de ficheros para ser accedido. Crea_Fichero Crea un archivo en un sistema de ficheros. Abre_Fichero Abre un archivo. Lee_Fichero Lee una ristra de bytes de un archivo. Escribe_Fichero Escribe una ristra de bytes en un archivo. Posiciona_Puntero Actualiza el puntero de acceso de un archivo. Cierra_Fichero Cierra un archivo. Borra_Fichero Borra un archivo
El conjunto de funciones anterior configuran la interfaz con el usuario, por tanto, en su desarrollo deberán estar contempladas como mínimo, se verá a medida que se avance en la exposición de la práctica que éstas además requerirán el apoyo de otras funciones que, o bien, se han de implementar en este nivel, o bien, forman parte del Sistema de Ficheros Básico, que proporciona servicios de gestión de bloques y ficheros sencillos (ver la siguiente práctica). Desde el punto de vista del usuario, el sistema de ficheros que se ha de diseñar posee las siguientes características:
1. Los archivos se identifican por una cadena de 12 caracteres.
2. Los archivos se organizan linealmente.
3. De cara al usuario, un archivo es visto como una cadena de bytes.
El módulo del sistema de ficheros que abarca esta práctica, Sistema Simbólico, será el responsable de soportar la visión del usuario anteriormente descrita y transformarla de manera que:
1. Los archivos se identifican por un identificador numérico del sistema, dícho identificador está asociado a una estructura de control que posee los atributos de los archivos, por tanto, en este nivel se llevarán a cabo la transformación de identificador simbólico en forma de cadena de cacarecteres (de usuario) a indentificador númerico del sistema.
2. Los archivos se constituyen como secuencia de bloques de datos de tamaño fijo.
Este nivel utilizará las siguientes estructuras de datos: bloque de control de fichero en disco, que llamaremos Bcfd, bloque de control de fichero en memoria, Bcfm, bloque de control de fichero de usuario, Bcfu, bloque de sistema de ficheros activo, Bsfa. Para cada una de las estructuras anteriores, se tendrá asociada una tabla, así se tendrán: con la estructura Bcfm se tendrá la Tabla de ficheros activos del sistema, Tfas, con la estructura Bcfu se tendrá la Tabla de ficheros de usuario, Tfu, y por último, con la estructura Bsfa se tendrá la Tabla de sistemas de ficheros activos, Tsfa. Seguidamente se pasa a describir la información contenida en cada una de las estructuras de control mencionadas.
- Nombre del archivo: 12 caracteres pertenecientes a [a ... z, A ... Z].
- Fecha de creación/modificación: 3 caracteres asociados a año (a partir de 1900, mes y día.
- Hora de creación/modificación: 3 caracteres asociados a hora, minuto, segundo.
- Tamaño: 4 bytes sin signo.
- Atributo de acceso: 1 byte que especifica si es de sólo lectura o sólo escritura.
- Dirección de bloques de datos: 40 bytes que localizan los bloques de datos del archivo.
Asociada a la estructura Bcfd se tendrá el directorio del sistema (que será el único existente pues los archivos se organizan en un sólo nivel) formado por un número dado de elementos del tipo Bcfd, el número específico de entradas de este tipo que se tienen para un sistema de ficheros se establece en el momento de la creación de éste (función Crea_Sistema).
- Índice al directorio del sistema donde se encuentra su Bcfd: 2 bytes sin signo.
- Imagen actualizada del Bcfd: tamaño en bytes de la estructura Bcfd.
- Número de referencias: 1 byte sin signo.
- Flag de bloqueo: 1 byte
Asociada a la estructura Bcfm se tiene la tabla de ficheros activos del sistema, Tfas, que consiste en un conjunto de entradas del tipo Bcfm, cuyo número, MAX_TFAS, es una constante del sistema definida en el fichero Consta_SF.h que se proporcionará como apoyo para el desarrollo de la práctica. Cuando se abre por primera vez un fichero, entonces se asigna una entrada de la Tfas, cuando se cierra un fichero y además el contador de referencias del fichero cerrado es 1, entonces es cuando se libera la entrada asociada al fichero cerrado.
- Índice a la entrada en la Tfas que el fichero tiene asociada: 2 bytes sin signo.
- Copia de los atributos de acceso: 1 byte.
- Puntero de acceso al fichero: 4 bytes sin signo.
Asociada a esta estructura se tiene la Tabla de ficheros de usuario, Tfu, que está constituida por un conjunto de entradas del tipo Bcfu, el número de estas entradas, MAX_TFUS, es una constante del sistema definida en Consta_S.h . Por cada operación de apertura realizada sobre un fichero se tendrá una entrada del tipo Bcfu en la tabla que el usuario que realizó la operación posee. Cuando se ejecuta un cierre de fichero por parte de un usuario (Cierra_Fichero) se libera la entrada asociada al fichero cerrado.
· Funciones de interfaz con el usuario.
· Funciones de uso interno.
· Funciones de interfaz con el Sistema de Ficheros Básico.
Las primeras son las que vamos a describir en este apartado y son aquellas que pueden ser invocadas directamente por el usuario desde la línea de órdenes de la aplicación a desarrollar. Las segundas son invocadas por la funciones anteriores y sirven para efectuar ciertas tareas elementales del SFS, la definición de tales tareas y la forma de implementarlas es un aspecto importante a resolver por los diseñadores y diseñadoras de esta práctica.
Por último, las funciones de interfaz con el nivel inferior más cercano al SFS, el Sistema de Ficheros Básico (SFB en adelante) pertenecen a este último y se han de conocer a nivel funcional (como cajas negras) por los diseñadores y diseñadoras de esta práctica, puesto que son los puntos de entrada al resto del sistema de ficheros. Habrán de consultar la definición de estas funciones, que se halla a partir de la página 26.
También podrán necesitar las funciones provistas en el Sistema de Bloques Físicos. Por otra parte, existen tres ficheros cabeceras que definen la interfaz con el sistema de ficheros simbólico. Con ello, los ficheros de implementación de esta práctica habrán de incluir los siguientes ficheros cabeceras:
errores.h códigos de error sfbasico.h interfaz del sistema de ficheros básico sbfisico.h interfaz del sistema de bloques físico Tipos_SF.h Tipos de datos del S.F. simbólico Consta_SF.h Constantes del S.F. simbólico sfsimb.h interfaz del sistema de ficheros simbólico
Todos estos ficheros se encuentran en el directorio /prac/include. Hay que resaltar que la interfaz de esta práctica ya está definida en sfsimb.h y no se debería alterar.
Seguidamente pasamos a describir la interfaz del sistema de ficheros simbólico.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Crea_Fichero (Nombre *nomb, Atributo atrib);
Crea_Fich crea un fichero, cuyo nombre está contenido en una cadena de caracteres apuntada por nomb, y con atributos de acceso especificados por el parámetro atrib, dichos atributos se encuentran definidos en el archivo cabecera Consta_SF.h y son: SOLO_LECTURA y SOLO_ESCRITURA. Si la función tiene éxito, entonces retornará 0, en caso contrario, retornará - 1 y la variable global Error_SF tomará alguno de los valores siguientes:
ERRFS_EXISTE Fichero ya existente.
ERR_MAXFICH Todas las entradas de directorio ya están asignadas.
ERRFS_EATRIB Especificación de atributo inválida.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Borra_Fichero(Nombre *nomb);
Borra_Fichero borre un fichero, cuyo nombre está contenido en una cadena de caracteres apuntada por nomb. Si la función tiene éxito, entonces retornará 0, en caso contrario, retornará - 1 y la variable global Error_SF tomará alguno de los valores siguientes:
ERR_NOEXISTE Fichero ya existente.
ERR_MAXFICH Todas las entradas de directorio ya están asignadas.
ERRFS_EATRIBU Especificación de atributo inválida.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Abre_Fich(Nombre *nomb, Acceso acces);
Abre_Fichero realiza una apertura de un fichero, cuyo nombre se especifica mediante nomb, para ser accedido según el modo especificado por el parámetro acces, los modos de acceso básicos permitidos son:
LECTURA Sólo lectura.
ESCRITURA Sólo escritura.
BLOQUEO Apertura con bloqueo.
También se permiten combinaciones de los modos anteriores mediante el uso del operador | (OR), por ejemplo: LECTURA | BLOQUEO (apertura para accesos sólo lectura con bloqueo) o LECTURA | ESCRITURA (apertura para accesos de lectura y escritura). Si la función se ejecuta con éxito, entonces retornará un valor entero no negativo que se corresponde con el índice a una entrada en la Tfu que se ha asociado al archivo abierto como consecuencia de la ejecución sin error. Si se produce un error durante la ejecución de esta función, entonces retornará - 1 y la variable global Error_SF tomará alguno de los siguientes valores:
ERR_NOEXISTEE Fichero no existente.
ERR_RANGO No hay entradas disponibles en la tabla Tu.
ERR_RANGO No hay entradas disponibles en la tabla Tfas.
ERRFS_EACC Tipo de acceso inválido o no permitido.
ERR_ACCESO Fichero bloqueado.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Posiciona_Puntero(int fd, Posfich punt);
Posiciona_Puntero actualiza el valor de puntero de acceso del archivo referenciado por fd a la posición indicada por el parámetro punt. Si la función se ejecuta sin error, entonces devolverá un valor no negativo igual a punt, en cambio si un error ocurre, entonces devolverá - 1 y la variable global del sistema Error_SF tomará alguno de los valores siguientes:
ERR_EFD Descriptor de fichero inválido.
ERR_PUNTERO Nuevo valor del puntero de acceso inválido.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Escribe_Fichero(int fd, void *buff, int nbytes);
Escribe_Fichero transfiere nbytes desde la posición apuntada por buff al fichero asociado al descriptor de fichero fd, los bytes escritos se ubicarán a partir de la posición indicada por el valor actual del puntero del fichero y el puntero a fichero se incrementará en un valor de nbytes. Si la función se ejecuta con éxito, entonces retornará un valor entero positivo igual a nbytes, en caso contrario retornará -1 y la variable global Error_SF tomará alguno de los siguientes valores:
ERR_EFD Descriptor de fichero inválido.
ERRFS_EACC Acceso no permitido.
ERR_ESCRIBE Error en la escritura del fichero.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Lee_Fichero(int fd, void *buff, int nbytes);
Lee_Fichero transfiere nbytes desde el fichero asociado al descriptor de fichero fd a la posición de memoria apuntada por buff, los bytes se leerán a partir de la posición indicada por el valor actual del puntero del fichero. Si la función se ejecuta con éxito, entonces retornará un valor entero positivo igual a nbytes y el puntero del fichero se incrementará en nbytes, en caso contrario retornará -1 y la variable global Error_SF tomará alguno de los siguientes valores:
ERR_EFD Descriptor de fichero inválido.
ERRFS_EACC Acceso no permitido.
ERR_LEE Error en la lectura del fichero.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Cierra_Fichero( int fd );
Cierra_Fichero cierra el fichero referenciado por fd, procediéndose a actualizar tanto los datos como la información de control de memoria a disco. Si la función se ejecuta con éxito, entonces retornará 0, en cambio si un error se produce devolverá -1 y la variable global Error_SF tomará el valor ERR_EFD.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Crea_Sistema(char *nomb, u_short nbloq, u_short tbloq, u_short nfich);
Crea_Sistema construye un sistema de ficheros en el archivo cuyo nombre se especifica mediante nomb. El parámetro nbloq indica el número de bloques de sistema de ficheros, tbloq especifica la longitud del cada bloque y nfich especifica el número de entradas de ficheros del directorio del sistema a crear. Si la función se ejecuta con éxito, entonces devolverá 0, en caso contrario, retornará -1 y la variable global Error_SF tomará alguno de los valores siguientes:
ERR_FICHERO Error fichero inválido.
ERR_PARAMETRO Número de bloques inválido.
ERR_PARAMETRO Longitud de bloque inválida.
ERR_PARAMETRO Número de entradas de directorio inválido.
#include "Consta_SF.h"
#include "Tipos_SF.h"
int Activa_Sistema( char *nomb, int nbloques_cache );
Activa_Sistema activa un sistema de ficheros contenido en el archivo cuyo nombre se especifica mediante nomb y que fue creado previamente por Crea_Sistema. El parámetro nbloques_cache indica cuántos bloques se desean para la caché.
Si la función se ejecuta con éxito, entonces devolverá el índice a la tabla de sistemas de ficheros activos asignado, en caso contrario, retornará -1 y la variable global Error_SF tomará alguno de los valores siguientes:
ERR_FICHERO Error fichero inválido.
#include "Consta_SF.h"
#include "Tipos_SF.h"
void InformeSFS (void);
InformeSFS imprime en la salida estándar un informe con las estadísticas y estado general del Sistema de Ficheros Simbólico.
Esta función no devuelve nada y no produce ningún error.