Logo Studenta

Silberschatz 10a castellano cap13

¡Este material tiene más páginas!

Vista previa del material en texto

Sexta parte
Sistema de archivos
Un archivo es una colección de información relacionada definida por su creador. Los archivos son mapeados por el sistema operativo en dispositivos físicos de almacenamiento masivo. El sistema de archivos describe cómo se asignan los archivos a los dispositivos físicos, así como la forma en que los usuarios y los programas acceden a ellos y los manipulan.
El acceso al almacenamiento físico a menudo puede ser lento, por lo que los sistemas de archivos deben estar diseñados para un acceso eficiente. Otros requisitos pueden ser importantes también, incluida la prestación de soporte para compartir archivos y acceso remoto a archivos.
CAPITULO 13
Interfaz del Sistema de Archivos
Para la mayoría de los usuarios, el sistema de archivos es el aspecto más visible de un sistema operativo. Proporciona el mecanismo para el almacenamiento y el acceso en línea tanto a los datos como a todos los programas de usuarios y del sistema operativo del sistema de computación. El sistema de archivos consta de dos partes distintas: una colección de archivos, cada uno almacenando datos relacionados, y una estructura de directorio, que organiza y proporciona información sobre todos los archivos del sistema. La mayoría de los sistemas de archivos “viven” en dispositivos de almacenamiento, que describimos en el Capítulo 11 y continuaremos discutiendo en el próximo capítulo. En este capítulo, consideramos los diversos aspectos de los archivos y las principales estructuras de directorios. También discutimos la semántica de compartir archivos entre múltiples procesos, usuarios y sistemas. Finalmente, discutimos formas de manejar la protección de archivos, necesaria cuando tenemos varios usuarios y queremos controlar quién puede acceder a los archivos y cómo se puede acceder a ellos.
OBJETIVOS DEL CAPÍTULO
• Explicar la función de los sistemas de archivos.
• Describir las interfaces a los sistemas de archivos.
• Discutir cuestiones de diseño del sistema de archivos, incluidos los métodos de acceso, el compartimiento de archivos, bloqueo de estructuras de directorios y archivos.
• Explorar la protección del sistema de archivos.
13.1 Concepto de archivo
Las computadoras pueden almacenar información en varios medios de almacenamiento, como dispositivos NVM, discos duros, cintas magnéticas y discos ópticos. Para que el sistema de computación sea conveniente de usar, el sistema operativo proporciona una visión lógica uniforme de la información almacenada. El sistema operativo abstrae las propiedades físicas de sus dispositivos de almacenamiento para definir una unidad lógica de almacenamiento, el archivo. Los archivos son mapeados por el sistema operativo en dispositivos físicos. Estos dispositivos de almacenamiento normalmente son no volátiles, por lo que el contenido es persistente entre reinicios del sistema.
Un archivo es una colección de información relacionada con nombre que es registrada en un almacenamiento. Desde la perspectiva del usuario, un archivo es la asignación lógica más pequeña de almacenamiento secundario; es decir, los datos no se pueden escribir en el almacenamiento secundario a menos que estén dentro de un archivo. Comúnmente, los archivos representan programas (tanto fuentes como objetos) y datos. Los archivos de datos pueden ser numéricos, alfabéticos, alfanuméricos, o binarios. Los archivos pueden ser de formato libre, como archivos de texto, o pueden tener rígidamente un formato. En general, un archivo es una secuencia de bits, bytes, líneas o registros, el significado de los cuales está definido por el creador y el usuario del archivo. El concepto de archivo es, por lo tanto, extremadamente general.
Los archivos tienen que ver con los métodos que utilizan los usuarios y las aplicaciones para almacenar y recuperar datos, y debido a que son de propósito general, su uso se ha extendido más allá de sus confines originales. Por ejemplo, UNIX, Linux y algunos otros sistemas proporcionan un sistema de archivos proc que utiliza interfaces de sistema de archivos para proporcionar acceso a la información del sistema (como los detalles del proceso).
La información de un archivo la define su creador. Muchos tipos diferentes de información puede almacenarse en un archivo: programas fuente o ejecutables, datos numéricos o de texto, fotos, música, videos, etc. Un archivo tiene una cierta estructura definida, que depende de su tipo. Un archivo es una secuencia de caracteres organizados en líneas (y posiblemente páginas). Un archivo fuente es una secuencia de funciones, cada una de las cuales que se organiza además como declaraciones seguidas de declaraciones ejecutables. Un archivo ejecutable es una serie de secciones de código que el cargador puede incorporar en memoria y ejecutar.
13.1.1 Atributos de archivo
Un archivo se nombra, para la conveniencia de sus usuarios humanos, y es mencionado por su nombre. Un nombre suele ser una cadena de caracteres, como example.c. Algunos sistemas diferencian entre caracteres en mayúsculas y minúsculas en los nombres, mientras que otros sistemas no lo hacen. Cuando se le pone nombre a un archivo, se vuelve independiente del proceso, del usuario e incluso del sistema que lo creó. Por ejemplo, un usuario puede crear el archivo example.c, y otro usuario puede editar ese archivo especificando su nombre. El propietario del archivo podría escribir el archivo en una unidad USB, enviarlo como un archivo adjunto de correo electrónico o copiarlo a través de una red, y aún podría llamarse example.c en el sistema de destino. A menos que haya compartimiento y un método de sincronización, esa segunda copia ahora es independiente de la primera y se puede cambiar por separado.
Los atributos de un archivo varían de un sistema operativo a otro, pero normalmente constan de estos:
• Nombre. El nombre simbólico del archivo es la única información que se mantiene en formato legible por humanos.
• Identificador. Esta etiqueta única, generalmente un número, identifica el archivo dentro del
sistema de archivos; ese identificador del archivo no es legible por humanos.
• Tipo. Esta información es necesaria para sistemas que admiten diferentes tipos de archivos.
• Ubicación. Esta información es un puntero a un dispositivo y a la ubicación del archivo en ese dispositivo.
• Tamaño. El tamaño actual del archivo (en bytes, palabras o bloques) y posiblemente el tamaño máximo permitido se incluye en este atributo.
• Protección. La información de control de acceso determina quién puede leer, escribir y ejecutar
• Tiempos importantes e identificación de usuario. Esta información se puede conservar para la creación, última modificación y último uso. Estos datos pueden ser útiles para la protección, seguridad y monitoreo de uso.
Algunos sistemas de archivos más nuevos también admiten atributos de archivo extendidos, incluido los códigos de permisos del archivo y características de seguridad como una suma de comprobación de archivo. Figura 13.1 ilustra una ventana de información de archivo en macOS que muestra los atributos de un archivo.
La información sobre todos los archivos se mantiene en la estructura de directorios, que reside en el mismo dispositivo que los propios archivos. Normalmente, una entrada de directorio consta del nombre del archivo y su identificador único. El identificador a su vez localiza los otros atributos del archivo. Puede llevar más de un kilobyte registrar esta información para cada archivo. En un sistema con muchos archivos, el tamaño del directorio en sí puede ser megabytes o gigabytes. Los directorios también son archivos, y deben almacenarse en el dispositivo y generalmente se los trae a la memoria por partes, según sea necesario.
Figura 13.1 Una ventana de información de archivo en macOS
13.1.2 Operaciones con archivos
Un archivo es un tipo de datos abstracto. Para definir un archivo correctamente, debemos considerar las operaciones que se pueden realizar en archivos. El sistema operativo puede proporcionar llamadas al sistema para crear, escribir, leer, reposicionar,eliminar y truncar archivos. Vamos a examinar lo que debe hacer el sistema operativo para realizar cada una de estas siete operaciones básicas de archivos. Entonces debería ser fácil ver cómo otras operaciones similares, como cambiar el nombre de un archivo, se puede implementar.
• Creación de un archivo. Se necesitan dos pasos para crear un archivo. Primero, se debe encontrar espacio en el sistema de archivos para el archivo. Analizamos cómo asignar espacio para el archivo en el Capítulo 14. En segundo lugar, se debe hacer una entrada para el nuevo archivo en un directorio.
• Abrir un archivo. En lugar de que todas las operaciones de archivo especifiquen un nombre de archivo, haciendo que el sistema operativo evalúe el nombre, verifique los permisos de acceso, y así sucesivamente, todas las operaciones excepto crear y eliminar requieren primero abrir() un archivo. Si tiene éxito, la llamada de apertura devuelve un identificador de archivo que se utiliza como un argumento en las otras llamadas.
• Escritura de un archivo. Para escribir un archivo, hacemos una llamada al sistema especificando tanto el identificador de archivo abierto y la información que se escribirá en el archivo. El sistema debe mantener un puntero de escritura a la ubicación en el archivo donde la próxima escritura tiene lugar si es secuencial. El puntero de escritura debe actualizarse siempre que ocurra una escritura.
• Leer un archivo. Para leer de un archivo, usamos una llamada al sistema que especifica el identificador de archivo y dónde (en la memoria) el siguiente bloque del archivo debe se debe poner. Nuevamente, el sistema necesita mantener un puntero de lectura a la ubicación en el archivo donde tendrá lugar la siguiente lectura, si es secuencial. Una vez que se ha leído, el puntero de lectura se actualiza. Porque un proceso suele leer o escribir en un archivo, la ubicación de la operación actual puede mantenerse como un puntero de posición de archivo actual por proceso. Tanto la lectura como las operaciones de escritura utilizan este mismo puntero, lo que ahorra espacio y reduce la complejidad del sistema.
• Reposicionamiento (seek) dentro de un archivo. El puntero de posición del archivo abierto se reposiciona a un valor dado. No es necesario reposicionar dentro de un archivo a cualquier E/S actual. Esta operación de archivo también se conoce como búsqueda de archivo.
• Eliminar un archivo. Para eliminar un archivo, buscamos en el directorio el archivo nombrado. Habiendo encontrado la entrada de directorio asociada, liberamos todo el espacio del archivo, para que sea reutilizado por otros archivos y borrar o marcar como libre la entrada de directorio. Tenga en cuenta que algunos sistemas permiten enlaces físicos: varios nombres (entradas de directorio) para el mismo archivo. En este caso, el contenido real del archivo no es eliminado hasta que se elimine el último enlace.
• Vaciar un archivo. El usuario puede querer borrar el contenido de un archivo, pero mantener sus atributos. En lugar de obligar al usuario a eliminar el archivo y luego recrearlo, esta función permite que todos los atributos permanezcan sin cambios, excepto el tamaño del archivo. El archivo se puede restablecer a la longitud cero y su espacio de archivo puede ser liberado.
Estas siete operaciones básicas comprenden el conjunto mínimo de operaciones requeridas sobre los archivos. Otras operaciones comunes incluyen agregar nueva información al final (append) de un archivo existente o cambiar el nombre de un archivo existente. Estas operaciones primitivas se pueden combinar para realizar otras operaciones de archivo. Por ejemplo, podemos crear una copia de un archivo creando un nuevo archivo y luego leyendo desde el viejo y escribiendo en el nuevo. También queremos tener operaciones que permitan a un usuario obtener y establecer los distintos atributos de un archivo. Por ejemplo, es posible que deseemos tener operaciones que permiten a un usuario determinar el estado de un archivo, como la longitud y para establecer atributos de archivo, como el propietario del archivo.
Como se mencionó, la mayoría de las operaciones de archivo mencionadas implican buscar en el directorio la entrada asociada con el archivo nombrado. Para evitar esta constante búsqueda, muchos sistemas requieren que se realice una llamada al sistema open () antes de que el archivo se utiliza por primera vez. El sistema operativo mantiene una tabla, llamada tabla de archivos abiertos, que contiene información sobre todos los archivos abiertos. Cuando se solicita una operación de archivo, el archivo se especifica mediante un índice en esta tabla, por lo que no es necesario realizar ninguna búsqueda. Cuando el archivo ya no se utiliza activamente, el proceso lo cierra y el sistema operativo elimina su entrada de la tabla de archivos abiertos, potencialmente liberando cerraduras (lock). Las llamadas al sistema create () y delete () funcionan sobre archivos cerrados (en lugar de abiertos).
Algunos sistemas abren implícitamente un archivo cuando se hace la primera referencia a él. El archivo se cierra automáticamente cuando el trabajo o programa que abrió el archivo termina. La mayoría de los sistemas, sin embargo, requieren que el programador abra un archivo explícitamente con la llamada al sistema open () antes de que ese archivo pueda ser utilizado. La operación open () toma un nombre de archivo y busca en el directorio, copiando la entrada de directorio en la tabla de archivos abiertos. La llamada open () también puede aceptar que se le especifique el modo de acceso a la información: creación, solo lectura, lectura y escritura, solo adición, etc. Este modo se compara con los permisos del archivo. Si el modo de solicitud es permitido, el archivo se abre para el proceso. La llamada al sistema open () normalmente devuelve un puntero a la entrada en la tabla de archivos abiertos. Este puntero, no el nombre real del archivo, se utiliza en todas las operaciones de E/S, evitando cualquier búsqueda y simplificando la interfaz de llamada al sistema.
La implementación de las operaciones open () y close () es más complicada en un entorno donde varios procesos pueden abrir el archivo simultáneamente. Esto puede ocurrir en un sistema donde varias aplicaciones diferentes abren el mismo archivo al mismo tiempo. Normalmente, el sistema operativo utiliza dos niveles de tablas internas: una tabla por proceso y una tabla para todo el sistema. La tabla por proceso rastrea todos los archivos que un proceso tiene abiertos. En esta tabla se almacena información con respecto al uso del archivo por parte del proceso. Por ejemplo, el puntero actual para cada archivo se encuentra aquí. Los derechos de acceso al archivo y la información contable pueden también se incluirá.
Cada entrada en la tabla por proceso, a su vez, apunta a un archivo abierto en la tabla de archivos abiertos en todo el sistema. La tabla de todo el sistema contiene información independiente del proceso, como la ubicación del archivo en el disco, las fechas de acceso y el tamaño del archivo. Una vez que un archivo ha sido abierto por un proceso, la tabla de todo el sistema incluye una entrada para el archivo. Cuando otro proceso ejecuta una llamada open (), simplemente se agrega una nueva entrada a la tabla de archivos abiertos del proceso que apunta a la entrada adecuada en la tabla de todo el sistema. Normalmente, la tabla de archivos abiertos también tiene un contador de aperturas asociado con cada archivo para indicar cuántos procesos tienen el archivo abierto. Cada close() disminuye este contador de archivos abiertos, y cuando el contador llega a cero, el archivo ya no está en uso, y la entrada del archivo se elimina de la tabla de archivos abiertos.BLOQUEO DE ARCHIVOS EN JAVA
En la API de Java, adquirir un bloqueo requiere primero obtener el FileChannel para que el archivo se bloquee. El método lock () del FileChannel se utiliza para adquirir la cerradura. La API del método lock () es 
FileLock lock (long begin, long end, boolean shared)donde begin y end son las posiciones inicial y final de la región que está siendo bloqueada. La configuración de shared (compartida) pone en verdadero los bloqueos compartidos; la configuración compartida a falso adquiere la cerradura en exclusiva. El bloqueo se libera invocando el release () del FileLock devuelto por la operación lock ().
El programa de la Figura 13.2 ilustra el bloqueo de archivos en Java. Este programa adquiere dos bloqueos en el archivo file.txt. El candado de la primera mitad del archivo es un candado exclusivo; la cerradura de la segunda mitad es una cerradura compartida.
En resumen, varios datos están asociados con un archivo abierto.
• Puntero de archivo. En sistemas que no incluyen un desplazamiento de archivo como parte de las llamadas al sistema read () y write (), el sistema debe rastrear la última ubicación de lectura/escritura como un puntero de posición de archivo actual. Este puntero es exclusivo de cada proceso que opera en el archivo y, por lo tanto, debe mantenerse separado de los atributos del archivo en disco.
• Contador de aperturas del archivo. A medida que se cierran los archivos, el sistema operativo debe reutilizar sus entradas de la tabla de archivos abiertos, o podría quedarse sin espacio en la tabla. Múltiples procesos pueden haber abierto un archivo, y el sistema debe esperar el último cierre antes de eliminar la entrada de la tabla de archivos abiertos. El contador de archivos abiertos rastrea el número de aperturas y cierres y llega a cero en el último cierre. A continuación, el sistema puede eliminar la entrada.
• Ubicación del archivo. La mayoría de las operaciones de archivos requieren que el sistema lea o escriba datos dentro del archivo. La información necesaria para ubicar el archivo (donde sea que está ubicado, ya sea en almacenamiento masivo, en un servidor de archivos a través de la red o en una unidad RAM) se mantiene en la memoria para que el sistema no tenga que leerlo de la estructura del directorio para cada operación.
• Derechos de acceso. Cada proceso abre un archivo en un modo de acceso. Esta información se almacena en la tabla por proceso para que el sistema operativo pueda permitir o denegar solicitudes de E/S posteriores.
Algunos sistemas operativos proporcionan funciones para bloquear un archivo abierto (o secciones de un archivo). Los bloqueos de archivos permiten que un proceso bloquee un archivo y evita que otros procesos obtengan acceso a él. Los bloqueos de archivos son útiles para archivos que se comparten mediante varios procesos, por ejemplo, un archivo de registro (log) del sistema al que se puede acceder y modificar por una serie de procesos en el sistema.
Los bloqueos de archivos proporcionan una funcionalidad similar a los bloqueos de lector-escritor, que se tratan en Sección 7.1.2. Un bloqueo compartido es similar a un bloqueo de lector en el que varios procesos pueden adquirir el bloqueo al mismo tiempo. Un bloqueo exclusivo se comporta como un bloqueo de escritura; solamente un proceso a la vez puede adquirir tal bloqueo. Es importante señalar que no Todos los sistemas operativos proporcionan ambos tipos de bloqueos: algunos sistemas sólo proporcionan bloqueo exclusivo de archivos.
import java.io.*;
import java.nio.channels.*;
public class LockingExample {
public static final boolean EXCLUSIVE = false;
public static final boolean SHARED = true;
public static void main(String args[]) throws IOException {
FileLock sharedLock = null;
FileLock exclusiveLock = null;
try {
RandomAccessFile raf = new RandomAccessFile("file.txt","rw");
// get the channel for the file
FileChannel ch = raf.getChannel();
// this locks the first half of the file - exclusive
exclusiveLock = ch.lock(0, raf.length()/2, EXCLUSIVE);
/** Now modify the data . . . */
// release the lock
exclusiveLock.release();
// this locks the second half of the file - shared
sharedLock = ch.lock(raf.length()/2+1,raf.length(),SHARED);
/** Now read the data . . . */
// release the lock
sharedLock.release();
} catch (java.io.IOException ioe) {
System.err.println(ioe);
}
finally {
if (exclusiveLock != null)
exclusiveLock.release();
if (sharedLock != null)
sharedLock.release();
} } }
Figura 13.2 Ejemplo de bloqueo de archivos en Java.
Además, los sistemas operativos pueden proporcionar información obligatoria o consultiva de los mecanismos de bloqueo de archivos. Con un bloqueo obligatorio, una vez que un proceso adquiere un bloqueo exclusivo, el sistema operativo evitará que cualquier otro proceso acceda al archivo bloqueado. Por ejemplo, suponga que un proceso adquiere un bloqueo exclusivo en el archivo system.log. Si intentamos abrir system.log desde otro proceso, por ejemplo, un editor de texto, el sistema operativo evitará el acceso hasta que se libere el bloqueo exclusivo. Alternativamente, si el bloqueo es de aviso, entonces el sistema operativo no evitará que el editor de texto adquiera acceso al archivo. Más bien, el editor de texto debe ser escrito de modo que adquiera manualmente el bloqueo antes de acceder al archivo. En otras palabras, si el esquema de bloqueo es obligatorio, el sistema operativo asegura la integridad del bloqueo. Para el bloqueo de aviso, depende de los desarrolladores de software asegurarse de que las cerraduras se adquieran adecuadamente y sean liberadas. Como regla general, los sistemas operativos Windows adoptan bloqueo obligatorio, y los sistemas UNIX emplean bloqueos de aviso.
El uso de bloqueos de archivos requiere las mismas precauciones que la sincronización ordinaria de procesos. Por ejemplo, los programadores que desarrollan en sistemas con bloqueo deben tener cuidado de mantener bloqueos de archivos exclusivos sólo mientras están accediendo al archivo. De lo contrario, evitarán innecesariamente que otros procesos accedan el archivo también. Además, se deben tomar algunas medidas para garantizar que dos o más procesos no se involucren en un abrazo mortal mientras intentan adquirir bloqueos de archivos.
13.1.3 Tipos de archivos
Cuando diseñamos un sistema de archivos, de hecho, un sistema operativo completo, consideramos siempre si el sistema operativo debe reconocer y admitir tipos de archivo. Si un sistema operativo reconoce un tipo de archivo, entonces puede operar en el archivo de manera razonable. Por ejemplo, un error común ocurre cuando un usuario intenta mostrar el contenido de un objeto binario de un programa. Este intento normalmente produce basura; sin embargo, el intento puede tener éxito si al sistema operativo se le ha dicho que el archivo es un programa de objeto binario.
Una técnica común para implementar tipos de archivos es incluir el tipo como parte del nombre del archivo. El nombre se divide en dos partes: un nombre y una extensión, generalmente separada por un punto (Figura 13.3). De esta forma, el usuario y el sistema operativo pueden llamar solo por el nombre y se sabe cuál es el tipo de archivo. La mayoría de los sistemas operativos permiten a los usuarios especificar un nombre de archivo como una secuencia de caracteres seguidos de un punto y terminados por una extensión hecha de caracteres adicionales. Los ejemplos incluyen resume.docx, server.c y ReaderThread.cpp.
El sistema usa la extensión para indicar el tipo de archivo y el tipo de operaciones que se pueden realizar en ese archivo. Sólo un archivo con la extensión: .com, .exe o .sh se puede ejecutar, por ejemplo. Los archivos .com y .exe son dos formas de archivos ejecutables binarios, mientras que el archivo .sh es un script de shell que contiene, en Formato ASCII, comandos para el sistema operativo. Los programas de aplicación también utilizan extensiones para indicar los tipos de archivos que les interesan. Por ejemplo, Los compiladores de Java esperan que los archivos fuente tengan una extensión .java, y el procesador de textos de Word de Microsoft espera que sus archivos terminen con una extensión .doc o .docx. Estas extensiones no siempre son necesarias, por lo que un usuario puede especificar un archivo sin la extensión (para guardarla escritura), y la aplicación buscará un archivo con el nombre de pila y la extensión que espera. Porque estas extensiones no son compatibles con el sistema operativo, se pueden considerar "sugerencias" para las aplicaciones que operan en ellos.
Figura 13.3 Tipos de archivos comunes.
Considere también el sistema operativo macOS. En este sistema, cada archivo tiene un tipo, como .app (para la aplicación). Cada archivo también tiene un atributo del creador que contiene el nombre del programa que lo creó. Este atributo lo establece el sistema operativo durante la llamada create (), por lo que su uso es obligatorio y soportado por el sistema. Por ejemplo, un archivo producido por un procesador de textos tiene el nombre del procesador de textos como su creador. Cuando el usuario abre ese archivo, haciendo doble clic con el ratón en el icono que representa el archivo, el procesador de texto se invoca automáticamente y el archivo se carga, listo para ser editado.
El sistema UNIX utiliza un número mágico almacenado en la cabecera de algunos archivos binarios para indicar el tipo de datos en el archivo (por ejemplo, el formato de un archivo de imagen). Asimismo, utiliza un número mágico de texto al comienzo de archivos de texto para indicar el tipo de archivo (en qué lenguaje de shell está escrito un script) y así. (Para obtener más detalles sobre números mágicos y otra jerga informática, consulte http://www.catb.org/esr/jargon/.) No todos los archivos tienen números mágicos, así que Las funciones del sistema no pueden basarse únicamente en esta información. UNIX no registra el nombre del programa que lo está creando. UNIX permite sugerencias en la extensión del nombre de archivo, pero estas extensiones no son impuestas ni dependen del sistema operativo; están destinados principalmente a ayudar a los usuarios a determinar qué tipo de contenido contiene el archivo. Las extensiones pueden ser utilizadas o ignoradas por una determinada aplicación, pero eso depende del programador de la aplicación.
13.1.4 Estructura de archivo
Los tipos de archivo también se pueden utilizar para indicar la estructura interna del archivo. Los archivos Fuente y objeto tienen estructuras que coinciden con las expectativas de los programas que los lee. Además, ciertos archivos deben ajustarse a una estructura requerida que es entendido por el sistema operativo. Por ejemplo, el sistema operativo requiere que un archivo ejecutable tenga una estructura específica para que pueda determinar dónde cargar en memoria el archivo y cuál es la ubicación de la primera instrucción. Algunos sistemas operativos extienden esta idea a un conjunto de estructuras de archivos de sistemas, con conjuntos de operaciones especiales para manipular archivos con esas estructuras.
Este punto nos lleva a una de las desventajas de tener que el sistema operativo soporte múltiples estructuras de archivos: hace que el sistema operativo sea grande y engorroso. Si el sistema operativo define cinco estructuras de archivos diferentes, debe contener el código para admitir estas estructuras de archivos. Además, puede ser necesario definir cada archivo como uno de los tipos de archivo admitidos por el sistema. Cuando las nuevas aplicaciones requieran información de estructuras no compatibles con el sistema operativo, pueden producirse problemas graves.
Por ejemplo, suponga que un sistema admita dos tipos de archivos: archivos de texto (compuesto por caracteres ASCII separados por un retorno de carro y un salto de línea) y archivos binarios ejecutables. Ahora, si nosotros (como usuarios) queremos definir un archivo cifrado para proteger el contenido de que sea leído por personas no autorizadas, vemos que no encuentra ningún tipo de archivo apropiado. El archivo cifrado no es un tipo de líneas de texto ASCII sino que son (aparentemente) bits aleatorios. Aunque pueda parecer un binario archivo, no es ejecutable. Como resultado, es posible que tengamos que eludir o hacer un mal uso del mecanismo de tipo de archivo del sistema operativo o abandonar nuestro esquema de cifrado.
Algunos sistemas operativos imponen (y admiten) una cantidad mínima de estructuras de archivos. Este enfoque se ha adoptado en UNIX, Windows y otros. UNIX considera que cada archivo es una secuencia de bytes de 8 bits; sin ninguna interpretación de estos bits por parte del sistema operativo. Este esquema proporciona el máximo de flexibilidad y poco apoyo. Cada programa de aplicación debe incluir su propio código para interpretar un archivo de entrada según la estructura adecuada. Sin embargo, todos los sistemas operativos deben admitir al menos una estructura: la de un archivo ejecutable — para que el sistema pueda cargar y ejecutar programas.
13.1.5 Estructura de archivos internos
Internamente, ubicar un desplazamiento dentro de un archivo puede resultar complicado para el sistema. Los sistemas de disco suelen tener un tamaño de bloque bien definido determinado por el tamaño de un sector. Toda la E/S de disco se realiza en unidades de un bloque (registro físico), y todos los bloques tienen el mismo tamaño. Es poco probable que el tamaño del registro físico coincida exactamente con la longitud del registro lógico deseado. Registros lógicos incluso puede variar en longitud. Empaquetar varios registros lógicos en blocks es una solución común a este problema.
Por ejemplo, el sistema operativo UNIX define todos los archivos como simples flujos de bytes. Cada byte es direccionable individualmente por su desplazamiento desde el principio (o final) del archivo. En este caso, el tamaño del registro lógico es de 1 byte. El sistema de archivos empaqueta y descomprime automáticamente los bytes en los bloques físico de disco— digamos, 512 bytes por bloque, según sea necesario.
El tamaño de registro lógico, el tamaño del bloque físico y la técnica de empaquetado determinan cuántos registros lógicos hay en cada bloque físico. El embalaje puede ser realizado por el programa de aplicación del usuario o por el sistema operativo. En cualquier caso, el archivo puede considerarse una secuencia de bloques. Todas las funciones básicas de E/S operan en términos de bloques. La conversión de registros lógicos a bloques físicos son un problema de software relativamente simple. 
Debido a que el espacio en disco siempre se asigna en bloques, una parte del último bloque de cada archivo generalmente se desperdicia. Si cada bloque tuviera 512 bytes, por ejemplo, luego, a un archivo de 1.949 bytes se le asignarían cuatro bloques (2.048 bytes); en el último se desperdiciarían 99 bytes. El desperdicio incurrido para mantener todo en unidades de bloques (en lugar de bytes) es una fragmentación interna. Todos los sistemas de archivos sufren de la fragmentación interna; Cuanto mayor sea el tamaño del bloque, mayor será la fragmentación interna.
13.2 Métodos de acceso
Los archivos almacenan información. Cuando se la necesita, se debe acceder a esta información y traerla a la memoria de la computadora. Se puede acceder a la información del archivo en varias formas. Algunos sistemas proporcionan sólo un método de acceso para los archivos. Otros (como los sistemas operativos de mainframe) admiten muchos métodos de acceso, y elegir el adecuado para una aplicación en particular es un gran problema de diseño.
13.2.1 Acceso secuencial
El método de acceso más simple es el acceso secuencial. La información del archivo es procesada ​​en orden, un registro tras otro. Este modo de acceso es por lejos el más común; por ejemplo, los editores y compiladores suelen acceder a archivos en este modo.
Figura 13.4 Archivo de acceso secuencial.
Las lecturas y escrituras constituyen la mayor parte de las operaciones en un archivo. Una operación de lectura— leer siguiente () - lee la siguiente porción del archivo y automáticamente avanza el puntero de archivo, que sigue la ubicación de E/S. Del mismo modo, la escritura operación - escribir siguiente () - se agrega al final del archivo y avanza al final de los bytes recién escritos (el nuevo final del archivo).Dicho archivo se puede restablecer al principio, y en algunos sistemas, un programa puede saltar n registros hacia adelante o hacia atrás para algún número entero n, quizás solo para n = 1. El acceso Secuencial, que se muestra en la Figura 13.4, se basa en un modelo de cinta de un archivo y funciona tan bien en dispositivos de acceso secuencial como en los de acceso aleatorio.
13.2.2 Acceso directo
Otro método es el acceso directo (o acceso relativo). Aquí, un archivo se compone de registros lógicos de longitud fija que permiten a los programas leer y escribir registros rápidamente sin ningún orden en particular. El método de acceso directo se basa en un modelo de archivo en disco, ya que los discos permiten el acceso aleatorio a cualquier bloque de archivos. Para acceso directo, el archivo se ve como una secuencia numerada de bloques o registros. Así, podemos leer el bloque 14, luego leer el bloque 53 y luego escribir el bloque 7. No hay restricciones en el orden de lectura o escritura de un archivo de acceso directo.
Los archivos de acceso directo son de gran utilidad para el acceso inmediato a grandes cantidades de información. Las bases de datos suelen ser de este tipo. Cuando llega una consulta sobre un registro particular, calculamos qué bloque contiene la respuesta y luego leemos ese bloque directamente para proporcionar la información deseada.
Como ejemplo simple, en un sistema de reserva de aerolíneas, podríamos almacenar toda la información sobre un vuelo en particular (por ejemplo, vuelo 713) en el bloque identificado por el número de vuelo. Así, el número de asientos disponibles para el vuelo 713 se almacena en el bloque 713 del archivo de reserva. Para almacenar información sobre un conjunto más grande, como personas, podríamos calcular una función hash en las personas nombres o que busque un pequeño índice en la memoria para determinar un bloque para leer y buscar.
Para el método de acceso directo, las operaciones de archivo deben modificarse para incluir el número de bloque como parámetro. Por tanto,si tenemos read(n), donde n es el número de bloque, en lugar de read next (), y write(n) en lugar de que write next (). Un enfoque alternativo es retener read next () y escribir next () y agregar una operación position_file(n) donde n es el Número de bloque. Entonces, para efectuar un read(n), posicionaríamos position_file(n) y luego read_next ().
El número de bloque proporcionado por el usuario al sistema operativo es normalmente un número de bloque relativo. Un número de bloque relativo es un índice relativo al comienzo del archivo. Así, el primer bloque relativo del archivo es 0, el siguiente es 1, y así sucesivamente, aunque la dirección absoluta del disco puede ser 14703 para el primer bloque y 3192 para el segundo. El uso de números de bloque relativos permite al sistema operativo decidir dónde se debe colocar el archivo (llamado el problema de asignación, lo discutimos en el Capítulo 14) y ayuda a evitar que el usuario de acceda a partes del sistema de archivos que no son parte de su archivo. Algunos los sistemas comienzan sus números de bloque relativos en 0; otros comienzan en 1.
Entonces, ¿cómo satisface el sistema una solicitud de registro N en un archivo? Asumiendo que tenemos una longitud de registro lógico L, la solicitud de registro N se convierte en una solicitud de E/S para L bytes comenzando en la ubicación L ∗ (N) dentro del archivo (asumiendo el primer registro es N = 0). Dado que los registros lógicos tienen un tamaño fijo, también es fácil para leer, escribir o eliminar un registro.
No todos los sistemas operativos admiten el acceso directo y secuencial para archivos. Algunos sistemas solo permiten el acceso secuencial a archivos; otros permiten solo directo acceso. Algunos sistemas requieren que un archivo se defina como secuencial o directo cuando se crea. Solo se puede acceder a dicho archivo de una manera compatible con su declaración. Podemos simular fácilmente el acceso secuencial en un archivo de acceso directo mediante simplemente manteniendo una variable cp que define nuestra posición actual, como se muestra en Figura 13.5. Simular un archivo de acceso directo en un archivo de acceso secuencial, sin embargo, es extremadamente ineficiente y torpe.
Figura 13.5 Simulación de acceso secuencial en un archivo de acceso directo.
13.2.3 Otros métodos de acceso
Se pueden construir otros métodos de acceso sobre un método de acceso directo. Estos métodos generalmente implican la construcción de un índice para el archivo. El índice, como un índice en la parte posterior de un libro, contiene punteros a los distintos bloques. Encontrar un registro en el archivo, primero buscamos en el índice y luego usamos el puntero para acceder al archivo directamente y encontrar el registro deseado. 
Por ejemplo, un archivo de precio minorista podría enumerar los códigos de producto universales (UPC) para artículos, con los precios asociados. Cada registro consta de un UPC de 10 dígitos y un precio de 6 dígitos, para un registro de 16 bytes. Si nuestro disco tiene 1.024 bytes por bloque, Puede almacenar 64 registros por bloque. Un archivo de 120.000 registros ocuparía aproximadamente 2.000 bloques (2 millones de bytes). Manteniendo el archivo ordenado por UPC, podemos definir un índice que consta del primer UPC de cada bloque. Este índice tendría 2000 entradas de 10 dígitos cada una, o 20.000 bytes, y por lo tanto podrían guardarse en la memoria. Para encontrar el precio de un artículo en particular, podemos hacer una búsqueda binaria del índice. De esta búsqueda, vemos exactamente qué bloque contiene el registro deseado y accedemos a ese bloque. Esta estructura nos permite buscar en un archivo grande con poca E/S.
En archivos grandes, el archivo de índice en sí puede volverse demasiado grande para guardarlo en memoria. Una solución es crear un índice para el archivo de índice. El índice primario del archivo contiene punteros a archivos de índice secundarios, que apuntan a los datos reales de artículos.
Por ejemplo, el método de acceso secuencial indexado (ISAM) de IBM utiliza un pequeño índice maestro que apunta a bloques de disco de un índice secundario. Los bloques de índice secundario apuntan a los bloques de archivos reales. El archivo se mantiene ordenado por clave. Para encontrar un elemento en particular, primero hacemos una búsqueda binaria del índice maestro, que proporciona el número de bloque del índice secundario. Se lee este bloque, y de nuevo se utiliza una búsqueda binaria para encontrar el bloque que contiene el registro deseado. Finalmente, este bloque se busca secuencialmente. De esta forma, cualquier registro puede ser ubicado con su clave como máximo en dos lecturas de acceso directo. La figura 13.6 muestra una situación similar a la implementada por el índice OpenVMS y los archivos relativos.
Figura 13.6 Ejemplo de archivos de índice y relativos.
13.3 Estructura de directorio
El directorio se puede ver como una tabla de símbolos que traduce los nombres de los archivos a sus bloques de control de archivos. Si tomamos tal punto de vista, vemos que el directorio en sí se puede organizar de muchas formas. La organización debe permitirnos insertar entradas, para eliminar entradas, para buscar una entrada con nombre y para enumerar todas las entradas en el directorio. En esta sección, examinamos varios esquemas para definir la estructura lógica del sistema de directorios.
Al considerar una estructura de directorio en particular, debemos tener en cuenta las operaciones que se van a realizar en un directorio:
• Buscar un archivo. Necesitamos poder buscar en una estructura de directorio para encontrar la entrada de un archivo en particular. Dado que los archivos tienen nombres simbólicos y nombres similares pueden indicar una relación entre archivos, es posible que deseemos poder buscar todos los archivos cuyos nombres coincidan con un patrón particular.
• Crear un archivo. Es necesario crear nuevos archivos y agregarlos al directorio.
• Eliminar un archivo. Cuando yano se necesita un archivo, podemos eliminarlo desde el directorio. Tenga en cuenta que una eliminación deja un agujero en la estructura del directorio y el sistema de archivos puede tener un método para defragmentar la estructura de directorio.
• Listar un directorio. Necesitamos poder listar los archivos de un directorio y el contenido de la entrada del directorio para cada archivo de la lista.
• Cambiar el nombre de un archivo. Dado que el nombre de un archivo representa su contenido para sus usuarios, debemos poder cambiar el nombre cuando el contenido o el uso del archivo ha cambiado. Cambiar el nombre de un archivo también puede permitir cambiar su posición dentro de la estructura de directorio.
• Recorrer el sistema de archivos. Es posible que deseemos acceder a todos los directorios y archivos dentro de una estructura de directorio. Por confiabilidad, es una buena idea guardar el contenido y estructura de todo el sistema de archivos a intervalos regulares. A menudo, hacemos esto copiando todos los archivos en cinta magnética, en otro almacenamiento secundario o a través de una red a otro sistema o la nube. Esta técnica proporciona una copia de seguridad en caso de falla del sistema. Además, si un archivo ya no se utiliza, el archivo se puede copiar en la copia de seguridad y el espacio en disco de ese archivo ser liberado para su reutilización por otro archivo.
En las siguientes secciones, describimos los esquemas más comunes para definir la estructura lógica de un directorio.
13.3.1 Directorio de un solo nivel
La estructura de directorio más simple es el directorio de un solo nivel. Todos los archivos están contenidos en el mismo directorio, que es fácil de soportar y comprender (Figura 13,7).
Figura 13.7 Directorio de un solo nivel
Sin embargo, un directorio de un solo nivel tiene limitaciones importantes cuando aumenta el número de archivos o cuando el sistema tiene más de un usuario. Puesto que todos los archivos están en el mismo directorio, deben tener nombres únicos. Si dos usuarios llaman su archivo de datos test.txt, entonces se viola la regla del nombre único. Por ejemplo, en una clase de programación, 23 estudiantes llamaron al programa para su segundo asignación prog2.c; otros 11 lo llamaron assign2.c. Afortunadamente, la mayoría de los sistemas de archivos admiten nombres de archivo de hasta 255 caracteres, por lo que es relativamente fácil seleccionar nombres de archivo únicos.
Incluso un solo usuario en un directorio de un solo nivel puede tener dificultades para recordar los nombres de todos los archivos a medida que aumenta el número de archivos. No es poco común para que un usuario tenga cientos de archivos en un sistema informático y un número de archivos adicionales en otro sistema. Hacer un seguimiento de tantos archivos es una tarea abrumadora.
13.3.2 Directorio de dos niveles
Como hemos visto, un directorio de un solo nivel a menudo genera confusión en los nombres de los archivos entre diferentes usuarios. La solución estándar es crear un directorio separado para cada usuario.
En la estructura de directorios de dos niveles, cada usuario tiene su propio directorio de archivos de usuario (UFD). Los UFD tienen estructuras similares, pero cada uno enumera solo los archivos de un solo usuario. Cuando se inicia un trabajo de usuario o un usuario inicia sesión, el directorio maestro de archivos busca en el directorio (MFD). El MFD está indexado por nombre de usuario o número de cuenta, y cada entrada apunta al UFD de ese usuario (Figura 13.8).
Figura 13.8 Estructura de directorios de dos niveles
Cuando un usuario hace referencia a un archivo en particular, solo se busca en su propia UFD. Así, diferentes usuarios pueden tener archivos con el mismo nombre, siempre que todos los nombres de archivo dentro de cada UFD sean únicos. Para crear un archivo para un usuario, el sistema operativo busca solo en el UFD de ese usuario para determinar si otro archivo con ese nombre existe. Para eliminar un archivo, el sistema operativo limita su búsqueda a la UFD local; por lo tanto, no puede eliminar accidentalmente el archivo de otro usuario que tenga el mismo nombre.
Los directorios de usuarios en sí deben crearse y eliminarse según sea necesario. Se ejecuta un programa especial de sistema con el nombre de usuario y la información de cuenta adecuados. El programa crea una nueva UFD y agrega una entrada para ella a la MFD. La ejecución de este programa puede estar restringida a los administradores del sistema. La asignación de espacio en disco para directorios de usuarios se puede manejar con las técnicas discutidas en el Capítulo 14 para los archivos mismos.
Aunque la estructura de directorios de dos niveles resuelve el problema de la colisión de nombres, todavía tiene desventajas. Esta estructura aísla efectivamente a un usuario de otro. El aislamiento es una ventaja cuando los usuarios son completamente independientes pero es una desventaja cuando los usuarios quieren cooperar en alguna tarea y acceder a los archivos de los demás. Algunos sistemas simplemente no permiten que los archivos de usuarios locales sean accedidos por otros usuarios.
Si se va a permitir el acceso, un usuario debe tener la capacidad de nombrar un archivo en el directorio de otro usuario. Para nombrar un archivo en particular de forma única en dos niveles de directorios, debemos dar tanto el nombre de usuario como el nombre del archivo. Un directorio de dos niveles se puede considerar como un árbol, o un árbol invertido, de altura 2. La raíz del árbol es el MFD. Sus descendientes directos son los UFD. Los descendientes de los UFD son los propios archivos. Los archivos son las hojas del árbol. Especificando un nombre de usuario y un nombre de archivo define una ruta en el árbol desde la raíz (el MFD) a una hoja (el archivo especificado). Por lo tanto, un nombre de usuario y un nombre de archivo definen un nombre de ruta. Cada archivo del sistema tiene un nombre de ruta. Para nombrar un archivo de forma única, un usuario debe conocer el nombre de la ruta del archivo deseado.
Por ejemplo, si el usuario desea acceder a su propio archivo de prueba llamado test.txt, simplemente puede hacer referencia a test.txt. Para acceder al archivo llamado test.txt del usuario B (con el nombre de entrada de directorio userb), sin embargo, es posible que deba consultar /userb/test.txt. Cada sistema tiene su propia sintaxis para nombrar archivos en directorios que no sean del usuario.
Se necesita sintaxis adicional para especificar el volumen de un archivo. Por ejemplo, en Windows, un volumen se especifica con una letra seguida de dos puntos. Así, la especificación del archivo puede ser C: ∖ userb ∖ test. Algunos sistemas van más allá y separan las partes de volumen, nombre de directorio y nombre de archivo de la especificación. En OpenVMS, por ejemplo, el archivo login.com podría especificarse como: u: [sst.crissmeyer] login.com; 1, donde u es el nombre del volumen, sst es el nombre del directorio, crissmeyer es el nombre del subdirectorio y 1 es el número de versión. Otros sistemas, como UNIX y Linux, simplemente tratan el nombre del volumen como parte del nombre del directorio. El primer nombre dado es el del volumen, y el resto es el directorio y el archivo. Por ejemplo, / u / pgalvin / test puede especificar el volumen u, el directorio pgalvin y el archivo test.
Una instancia especial de esta situación ocurre con los archivos del sistema. Programas proporcionados como parte del sistema: cargadores, ensambladores, compiladores, rutinas de utilidad, bibliotecas, etc., generalmente se definen como archivos. Cuando se dan los comandos apropiados al sistema operativo, estos archivos son leídos por el cargador y ejecutados. Muchos intérpretes de comandos simplemente tratan un comando como el nombre de un archivo para cargar y ejecutar. En el sistema de directorio como lo definimos arriba, este nombre de archivo se buscaría en la UFD actual. Una solución sería copiar los archivos del sistema en cada UFD. Sin embargo, copiando todoslos archivos del sistema desperdiciarían una enorme cantidad de espacio. (Si los archivos del sistema requieren 5 MB, luego, admitir 12 usuarios requeriría 5 × 12 = 60 MB solo para copias de los archivos del sistema.)
La solución estándar es complicar un poco el procedimiento de búsqueda. Un directorio de usuario especial se define para contener los archivos del sistema (por ejemplo, usuario 0). Siempre que se proporciona un nombre de archivo para cargar, el sistema operativo primero busca la UFD local. Si se encuentra el archivo, se utiliza. Si no se encuentra, el sistema busca automáticamente el directorio de usuario especial que contiene los archivos del sistema. La secuencia de directorios buscados cuando se nombra un archivo se llama camino de búsqueda. La ruta de búsqueda se puede ampliar para contener una lista ilimitada de directorios. para buscar cuando se da un nombre de comando. Este método es el más utilizado en UNIX y Windows. Los sistemas también se pueden diseñar para que cada usuario tenga su propia ruta de búsqueda.
13.3.3 Directorios estructurados en árbol
Una vez que hemos visto un directorio de dos niveles que es como un árbol de dos niveles, la generalización natural es extender la estructura del directorio a un árbol de altura arbitraria (Figura 13.9). Esta generalización permite a los usuarios crear sus propios subdirectorios y organizar sus archivos. Un árbol es la extensión de una estructura de directorio. El árbol tiene un directorio raíz y todos los archivos del sistema tiene un nombre de ruta exclusivo.
Figura 13.9 Estructura de directorios estructurada en árbol
Un directorio (o subdirectorio) contiene un conjunto de archivos o subdirectorios. En muchas implementaciones, un directorio es simplemente otro archivo, pero se lo trata de una manera especial. Todos los directorios tienen el mismo formato interno. La entrada en un directorio se define como un archivo (0) o como un subdirectorio (1). Las llamadas al sistema especiales se utilizan para crear y eliminar directorios. En este caso el sistema operativo (o el código del sistema de archivos) implementa otro formato de archivo, el de un directorio.
En uso normal, cada proceso tiene un directorio actual. El directorio actual debe contener la mayoría de los archivos que son de interés actual para el proceso. Cuando se hace referencia a un archivo, se busca en el directorio actual. Si se necesita un archivo que no esté en el directorio actual, el usuario normalmente debe especificar un nombre de ruta o cambiar el directorio actual para que sea el directorio que contenga ese archivo. Para cambiar de directorio, se puede proporcionar una llamada al sistema que tome un nombre de directorio como parámetro y lo use para redefinir el directorio actual. Así, el usuario puede cambiar su directorio actual cuando quiera. Otros sistemas le dejan a la aplicación (digamos, una shell) para rastrear y operar en un directorio actual, así que cada proceso podría tener diferentes directorios actuales.
El directorio actual inicial del shell de inicio de sesión de un usuario se designa cuando el trabajo del usuario se inicia o el usuario inicia sesión. El sistema operativo busca el archivo de contabilidad (o alguna otra ubicación predefinida) para encontrar una entrada para este usuario (para fines contables). En el archivo de contabilidad hay un puntero a (o el nombre de) el directorio inicial del usuario. Este puntero se copia a una variable local para este usuario que especifica el directorio actual inicial del usuario. Desde esa shell, otros procesos se pueden generar. El directorio actual de cualquier subproceso suele ser el actual directorio del padre cuando se generó.
Los nombres de ruta (path name) pueden ser de dos tipos: absolutos y relativos. En UNIX y Linux, un nombre de ruta absoluto comienza en la raíz (que se designa con una inicial "/") Y sigue una ruta hasta el archivo especificado, dando los nombres del directorio en el camino. Un nombre de ruta relativo define una ruta desde el directorio actual. Por ejemplo, en el sistema de archivos estructurado en árbol de la Figura 13.9, si el directorio actual es / spell / mail, entonces el nombre de la ruta relativa prt / first se refiere al mismo archivo al igual que el nombre de la ruta absoluta / spell / mail / prt / first.
Permitir que un usuario defina sus propios subdirectorios le permite imponer una estructura en sus archivos. Esta estructura puede resultar en directorios separados para archivos asociados con diferentes temas (por ejemplo, se creó un subdirectorio para contener el texto de este libro) o diferentes formas de información. Por ejemplo, los programas de directorio pueden contener programas fuente; el directorio bin puede almacenar todos los binarios. (Como nota al margen, los archivos ejecutables eran conocidos en muchos sistemas como "binarios", lo que llevó a que se almacenaran en el directorio bin).
Una decisión política interesante en un directorio estructurado en árbol se refiere a cómo para manejar la eliminación de un directorio. Si un directorio está vacío, su entrada en el directorio que lo contiene simplemente se puede eliminar. Sin embargo, suponga que el directorio que se eliminará no está vacío, sino que contiene varios archivos o subdirectorios. Unos pueden adoptarse dos enfoques. Algunos sistemas no eliminarán un directorio a menos que esté vacío. Por lo tanto, para eliminar un directorio, el usuario primero debe eliminar todos los archivos en ese directorio. Si existen subdirectorios, se debe aplicar este procedimiento de forma recursiva a ellos, para que también se puedan borrar. Este enfoque puede resultar en una cantidad sustancial de trabajo. Un enfoque alternativo, como el adoptado por el comando rm de UNIX, es proporcionar una opción: cuando se realiza una solicitud para eliminar un directorio, todos los archivos y subdirectorios de ese directorio también deben eliminado. Cualquiera de los dos enfoques es bastante fácil de implementar; la elección es de política. Esta última política es más conveniente, pero también más peligrosa, porque toda la estructura del directorio se puede eliminar con un comando. Si ese comando se emite por error, será necesario restaurar una gran cantidad de archivos y directorios (asumiendo que existe una copia de seguridad).
Con un sistema de directorio estructurado en árbol, los usuarios pueden tener acceso, además de sus archivos, los archivos de otros usuarios. Por ejemplo, el usuario B puede acceder a un archivo del usuario A, especificando su nombre de ruta. El usuario B puede especificar un nombre de ruta absoluto o uno relativo. Alternativamente, el usuario B puede cambiar su directorio actual para ser el directorio del usuario A y acceder al archivo por su nombre de archivo.
13.3.4 Directorios de grafos acíclicos
Considere dos programadores que están trabajando en un proyecto conjunto. Los archivos asociados con ese proyecto se pueden almacenar en un subdirectorio, separándolos de otros proyectos y archivos de los dos programadores. Pero dado que ambos programadores son igualmente responsables del proyecto, ambos quieren que el subdirectorio esté en sus propios directorios. En esta situación, se debe compartir el subdirectorio común. Existe un directorio o archivo compartido en el sistema de archivos en dos (o más) lugares en una vez.
Una estructura de árbol prohíbe compartir archivos o directorios. Un grafo acíclico, es decir, un grafo sin ciclos, permite que los directorios compartan subdirectorios y archivos (Figura 13.10). 
Figura 13.10 Estructura de directorios de grafos acíclicos.
El mismo archivo o subdirectorio puede estar en dos diferentes directorios. El gráfico acíclico es una generalización natural del árbol estructurado en esquema de directorio.
Es importante tener en cuenta que un archivo (o directorio) compartido no es lo mismo que dos copias del archivo. Con dos copias, cada programador puede ver la copia en lugar de que el original, pero si un programador cambia el archivo, los cambiosno aparecen en la copia del otro. Con un archivo compartido, sólo existe un archivo real, por lo que cualquier cambio realizado por una persona es inmediatamente visible para la otra. Compartir es particularmente importante para los subdirectorios. Un nuevo archivo creado por una persona aparece automáticamente en todos los subdirectorios compartidos.
Cuando las personas trabajan en equipo, todos los archivos que desean compartir se pueden poner en un directorio. El directorio de inicio de cada miembro del equipo podría contener este directorio de archivos compartidos como subdirectorio. Incluso en el caso de un solo usuario, la organización del archivo del usuario puede requerir que algún archivo se coloque en diferentes subdirectorios. Por ejemplo, un programa escrito para un proyecto en particular debe estar en el directorio de todos los programas y en el directorio de ese proyecto. 
Los archivos y subdirectorios compartidos se pueden implementar de varias formas. La forma común, ejemplificada por los sistemas UNIX, es crear una nueva entrada de directorio llamado enlace. Un enlace es efectivamente un puntero a otro archivo o subdirectorio. Por ejemplo, un enlace puede implementarse como un nombre de ruta absoluto o relativo. Cuando se hace una referencia a un archivo, buscamos en el directorio. Si en el directorio, la entrada está marcada como un enlace, luego el nombre del archivo real se incluye en la información de enlace. Resolvemos el enlace usando ese nombre de ruta para ubicar el archivo real. Los enlaces se identifican fácilmente por su formato en la entrada del directorio (o por tener un tipo especial en sistemas que admiten tipos) y son efectivamente punteros indirectos. El sistema operativo ignora estos enlaces al recorrer árboles de directorios para preservar la estructura acíclica del sistema.
Otro enfoque común para implementar archivos compartidos es simplemente duplicar toda la información sobre ellos en ambos directorios compartidos. Por tanto, tanto las entradas son idénticas e iguales. Considere la diferencia entre este enfoque y la creación de un enlace. El enlace es claramente diferente de la entrada de directorio original; por tanto, los dos no son iguales. Sin embargo, las entradas de directorio duplicadas, el original y la copia son indistinguibles. Un problema importante con el duplicado es que las entradas del directorio mantienen la coherencia cuando se modifica un archivo.
Una estructura de directorio de grafo acíclico es más flexible que una simple estructura de árbol, pero también es más compleja. Se deben considerar varios problemas cuidadosamente. Un archivo ahora puede tener varios nombres de ruta absolutos. Por consiguiente, los nombres de archivo distintos pueden hacer referencia al mismo archivo. Esta situación es similar a la Problema de aliasing para lenguajes de programación. Si estamos tratando de recorrer el sistema de archivos completo: para encontrar un archivo, para acumular estadísticas sobre todos los archivos o para copiar todos los archivos al almacenamiento de copia de seguridad; este problema se vuelve importante, ya que no se quieren atravesar estructuras compartidas más de una vez.
Otro problema involucra la eliminación. ¿Cuándo el espacio asignado a un archivo compartido se desasignará y reutilizará? Una posibilidad es eliminar el archivo cada vez que alguien lo borra, pero esta acción puede dejar punteros colgando al archivo, ya inexistente. Peor aún, si los punteros de archivo restantes contienen direcciones de disco real, y el espacio se reutiliza posteriormente para otros archivos, estos punteros colgantes pueden apuntar a otros archivos.
En un sistema donde la compartición se implementa mediante enlaces simbólicos, esta situación es algo más fácil de manejar. La eliminación de un enlace no tiene por qué afectar al archivo original; solo se elimina el enlace. Si se elimina la entrada del archivo, el espacio para el archivo se desasigna, dejando los enlaces colgando. Podemos buscar estos enlaces y eliminarlos también, pero a menos que se mantenga una lista de los enlaces asociados con cada archivo, esta búsqueda puede resultar costosa. Alternativamente, podemos dejar los enlaces hasta que se intente utilizarlos. En ese momento, podemos determinar que el archivo del nombre dado por el enlace no existe y puede fallar al resolver el nombre del enlace; el acceso se trata como cualquier otro nombre de archivo ilegal. (En esto caso, el diseñador del sistema debe considerar cuidadosamente qué hacer cuando un archivo se ha eliminado y se crea otro archivo con el mismo nombre, antes de que un enlace simbólico utilice el archivo original.) En el caso de UNIX, los enlaces simbólicos se dejan cuando un archivo se elimina, y depende del usuario darse cuenta de que el archivo original se ha ido o ha sido reemplazado. Microsoft Windows utiliza el mismo enfoque. Otro enfoque para la eliminación es conservar el archivo hasta que todas las referencias se eliminen. Para implementar este enfoque, debemos tener algún mecanismo para determinar que se ha eliminado la última referencia al archivo. Podríamos mantener una lista de todas las referencias a un archivo (entradas de directorio o enlaces simbólicos). Cuando se establece un enlace o una copia de la entrada del directorio, se agrega una nueva entrada a la lista de referencias al archivo. Cuando se elimina un enlace o una entrada de directorio, eliminamos su entrada en la lista. El archivo se elimina cuando su lista de referencias de archivo está vacía. El problema con este enfoque es el tamaño variable y potencialmente grande de la lista de referencia de archivo. Sin embargo, realmente no necesitamos mantener la lista completa —Necesitamos llevar solo un recuento del número de referencias. Añadiendo uno nuevo a la entrada de enlace o directorio se incrementa el recuento de referencias. Eliminar un enlace o una entrada disminuye el recuento. Cuando el recuento es 0, el archivo se puede eliminar; no existen referencias restantes al mismo. El sistema operativo UNIX utiliza este enfoque para enlaces no simbólicos (o enlaces duros), manteniendo un recuento de referencias en el bloque de información del archivo (o inodo; consulte la Sección C.7.2). Al prohibir efectivamente múltiples referencias a directorios, mantenemos una estructura de gráfico acíclico. Para evitar problemas como los que acabamos de comentar, algunos sistemas simplemente no permiten enlaces compartidos a directorios.
13.3.5 Directorios de grafos general
Un problema serio con el uso de una estructura de grafo acíclico es asegurarse de que no hay ciclos. Si comenzamos con un directorio de dos niveles y permitimos a los usuarios crear subdirectorios, resulta un directorio estructurado en árbol. Debería ser bastante fácil de ver que simplemente agregando nuevos archivos y subdirectorios a una estructura de directorio de árbol existente conserva la naturaleza estructurada en árbol. Sin embargo, cuando agregamos enlaces, la estructura del árbol se destruye, lo que resulta en una estructura grafo simple (Figura 13.11).
Figura 13.11 Directorio de grafo general.
La principal ventaja de un grafo acíclico es la relativa simplicidad de los algoritmos para recorrer el grafo y determinar cuándo no hay más referencias a un archivo. Queremos evitar atravesar secciones compartidas de un grafo acíclico dos veces, principalmente por motivos de rendimiento. Si acabamos de buscar en un subdirectorio compartido un archivo en particular sin encontrarlo, queremos evitar seguir buscando en ese subdirectorio de nuevo; la segunda búsqueda sería una pérdida de tiempo. Si se permite que existan ciclos en el directorio, también queremos evitar buscar cualquier componente dos veces, por razones de corrección y rendimiento. Un algoritmo mal diseñado puede resultar en un bucle infinito continuamente buscando a través del ciclo y nunca terminar. Una solución es limitar arbitrariamente el número de directorios a los que se accederá durante una búsqueda.
Existe un problemasimilar cuando intentamos determinar cuándo un archivo se puede eliminar. Con estructuras de directorios de grafos acíclicos, un valor de 0 en el recuento de referencias significa que no hay más referencias al archivo o directorio, y el archivo se puede eliminar. Sin embargo, cuando existen ciclos, el recuento de referencia puede que no sea 0 incluso cuando ya no sea posible hacer referencia a un directorio o archivo. Esta anomalía resulta de la posibilidad de autorreferencia (o un ciclo) en la estructura de directorios. En este caso, generalmente necesitamos utilizar un esquema de recolección de basura para determinar cuándo se ha eliminado la última referencia y el espacio de disco se puede reasignar. La recolección de basura implica recorrer todo el sistema de archivos, marcando todo lo que sea accedido. Luego, un segundo paso recoge todo lo que no está marcado en una lista de espacio libre. (Un procedimiento de marca similar se puede utilizar para garantizar que un recorrido o una búsqueda cubra todo en el sistema de archivos una vez y solo una vez.). En cambio, la Recolección de basura para un sistema de archivos basado en disco requiere mucho tiempo y, por lo tanto, rara vez se intenta.
La recolección de basura es necesaria solo debido a los posibles ciclos en el grafo. Por tanto, es mucho más fácil trabajar con una estructura de grafo acíclico. La dificultad es Evitar los ciclos a medida que se agregan nuevos enlaces a la estructura. ¿Cómo sabemos cuándo un nuevo enlace completará un ciclo? Existen algoritmos para detectar ciclos en grafos; sin embargo, son computacionalmente costosos, especialmente cuando el grafo está en almacenamiento de disco. Un algoritmo más simple en el caso especial de directorios y enlaces, es evitar los enlaces durante el recorrido del directorio. Los ciclos se evitan y no se incurre en gastos generales extra.
13.4 Protección
Cuando la información se almacena en un sistema informático, queremos mantenerla segura de daño físico (el problema de la confiabilidad) y acceso inadecuado (el problema de protección).
Generalmente, la fiabilidad se obtiene mediante copias duplicadas de archivos. Muchas computadoras tienen programas de sistemas que automáticamente (o a través de la intervención de un operador de computadora) copiar archivos de disco a cinta a intervalos regulares (una vez al día o a la semana o mes) para mantener una copia en caso de que un sistema de archivos se destruya accidentalmente. Los sistemas de archivos pueden dañarse por problemas de hardware (como errores en la lectura o escritura), subidas de tensión o fallas, golpes de cabeza, suciedad, temperaturas extremas y vandalismo. Los archivos pueden eliminarse accidentalmente. Errores en el software del sistema de archivos también puede provocar la pérdida del contenido del archivo. La confiabilidad se cubrió en más detalles en el Capítulo 11. 
La protección se puede proporcionar de muchas formas. Para un sistema operativo moderno de un sistema portátil en funcionamiento, podríamos brindar protección requiriendo a un usuario autenticación de nombre y contraseña para acceder a él, cifrando el almacenamiento secundario por lo que incluso si alguien abre la computadora portátil y extrae la unidad, que le sea difícil acceder a los datos y colocar cortafuegos de acceso a la red para que cuando está en uso, sea difícil acceder a través de su conexión de red. En sistemas multiusuario, incluso el acceso válido al sistema necesita mecanismos más avanzados para permitir un acceso válido a los datos.
13.4.1 Tipos de acceso
La necesidad de proteger archivos es un resultado directo de la capacidad de acceder a los archivos. Los Sistemas que no permiten el acceso a los archivos de otros usuarios no necesitan protección. Así, podríamos proporcionar una protección completa prohibiendo el acceso. Alternativamente, nosotros podríamos proporcionar acceso libre sin protección. Ambos enfoques son demasiado extremos para uso general. Lo que se necesita es un acceso controlado.
Los mecanismos de protección proporcionan acceso controlado al limitar los tipos de acceso a archivos que se puede realizar. El acceso está permitido o denegado según varios factores, uno de los cuales es el tipo de acceso solicitado. Varios tipos de operaciones pueden ser controladas:
• Read. Leer del archivo.
• Write. Escribe o reescribe el archivo.
• Execute. Carga el archivo en la memoria y lo ejecuta.
• Append. Escribe nueva información al final del archivo.
• Delete. Elimina el archivo y libera su espacio para una posible reutilización.
• List. Lista los nombres y los atributos del archivo.
• Cambio de atributos. Cambia los atributos del archivo.
Otras operaciones, como cambiar el nombre, copiar y editar el archivo, también pueden ser controladas. Para muchos sistemas, sin embargo, estas funciones de nivel superior pueden ser implementadas por un programa del sistema que realiza llamadas al sistema de nivel inferior. La protección se proporciona solo en el nivel inferior. Por ejemplo, copiar un archivo puede implementarse simplemente mediante una secuencia de solicitudes de lectura. En este caso, un usuario con acceso de lectura también puede hacer que el archivo se copie, imprima, etc.
Se han propuesto muchos mecanismos de protección. Cada uno tiene ventajas y desventajas y debe ser apropiado para su aplicación prevista. Un pequeño sistema informático que sólo lo utilizan unos pocos miembros de un grupo de investigación, por ejemplo, puede que no necesite los mismos tipos de protección que una gran empresa computacional que se utiliza para investigación, finanzas y operaciones de personal. Nosotros discutimos algunos enfoques de protección en las siguientes secciones y presentar un tratamiento más completo en el Capítulo 17.
13.4.2 Control de acceso
El enfoque más común para el problema de protección es hacer que el acceso dependa sobre la identidad del usuario. Los diferentes usuarios pueden necesitar diferentes tipos de acceso a un archivo o directorio. El esquema más general para implementar identidad dependiente el acceso es asociar con cada archivo y directorio un control de acceso lista (ACL) que especifica los nombres de usuario y los tipos de acceso permitidos para cada usuario. Cuando un usuario solicita acceso a un archivo en particular, el sistema operativo verifica la lista de acceso asociada con ese archivo. Si ese usuario aparece en la lista para el acceso solicitado, el acceso está permitido. De lo contrario, se produce una violación de la protección y al trabajo del usuario se le niega el acceso al archivo.
Este enfoque tiene la ventaja de permitir metodologías de acceso complejas. El principal problema de las listas de acceso es su longitud. Si queremos permitir que todos lean un archivo, debemos enumerar todos los usuarios con acceso de lectura. Esta técnica tiene dos consecuencias indeseables:
• La construcción de una lista de este tipo puede ser una tarea tediosa y poco gratificante, especialmente si no conocemos de antemano la lista de usuarios en el sistema.
• La entrada del directorio, antes de tamaño fijo, ahora debe ser de tamaño variable, resultando en una gestión del espacio más complicada.
Estos problemas se pueden resolver mediante el uso de una versión condensada de la lista de acceso.
Para condensar la longitud de la lista de control de acceso, muchos sistemas reconocen tres clasificaciones de usuarios en relación con cada archivo:
• Propietario. El usuario que creó el archivo es el propietario.
• Grupo. Un conjunto de usuarios que comparten el archivo y necesitan un acceso similar es un grupo, o grupo de trabajo.
• Otros (universo). Todos los demás usuarios del sistema.
El enfoque reciente más común es combinar listas de control de acceso con el control de acceso de propietario, grupo y universo más general (y más fácil de implementar) que el esquema recién descrito. Por ejemplo, Solaris utiliza las tres categorías de acceso de forma predeterminada, pero permite agregar listas de control de acceso a archivos específicosy directorios cuando se desea un control de acceso más detallado.
Para ilustrarlo, considere a una persona, Sara, que está escribiendo un libro nuevo. Ella contrató a tres estudiantes graduados (Jim, Dawn y Jill) para ayudar con el proyecto. El texto del libro se guarda en un archivo llamado book.tex. La protección asociada con este archivo es el siguiente:
• Sara debería poder invocar todas las operaciones en el archivo.
• Jim, Dawn y Jill solo deberían poder leer y escribir el archivo; a ellos no se les debe permitir eliminar el archivo.
• Todos los demás usuarios deben poder leer, pero no escribir el archivo. (Sara está interesada en dejar que la mayor cantidad de personas posible lean el texto para que ella puede obtener comentarios.)
Para lograr tal protección, debemos crear un nuevo grupo, digamos, texto con los miembros Jim, Dawn y Jill. El nombre del grupo, texto, debe luego estar asociado con el archivo book.tex, y los derechos de acceso deben establecerse de acuerdo con la política que hemos descrito.
Ahora considere un visitante al que Sara le gustaría otorgar acceso temporal al Capítulo 1. El visitante no se puede agregar al grupo de texto porque eso sería darle acceso a todos los capítulos. Como un archivo solo puede estar en un grupo, Sara no puede agregar otro grupo al Capítulo 1. Con el agregado de la funcionalidad de una lista de control de acceso, sin embargo, el visitante se puede agregar a la lista de control de acceso de Capítulo 1.PERMISOS EN UN SISTEMA UNIX
En el sistema UNIX, se manejan la protección de directorios y la protección de archivos en forma similar. Asociados con cada archivo y directorio hay tres campos: propietario, grupo y universo, cada uno formado por los tres bits rwx, donde r controla acceso de lectura, w controla el acceso de escritura y x controla la ejecución. Por tanto, un usuario puede listar el contenido de un subdirectorio solo si el bit r está establecido en el campo apropiado. Del mismo modo, un usuario puede cambiar su directorio actual a otro directorio actual (digamos, foo) solo si el bit x asociado con el subdirectorio foo se establece en el campo apropiado. A continuación, se muestra una lista de directorios de muestra de un entorno UNIX:
El primer campo describe la protección del archivo o directorio. Un d como el primero carácter indica un subdirectorio. También se muestra el número de enlaces al archivo, el nombre del propietario, el nombre del grupo, el tamaño del archivo en bytes, la fecha de la última modificación, y finalmente el nombre del archivo (con extensión opcional).
Para que este esquema funcione correctamente, se deben controlar los permisos y las listas de acceso. Este control se puede lograr de varias formas. Por ejemplo, en el sistema UNIX, los grupos solo pueden ser creados y modificados por el administrador del sistema (o por cualquier superusuario). Por lo tanto, el control se logra a través de Interacción. Las listas de acceso se analizan con más detalle en la Sección 17.6.2.
Con la clasificación de protección más limitada, solo se necesitan tres campos para definir la protección. A menudo, cada campo es una colección de bits y cada bit permite o impide el acceso asociado a él. Por ejemplo, el sistema UNIX define tres campos de tres bits cada uno: rwx, donde r controla el acceso de lectura, w controla el acceso de escritura y x controla la ejecución. Se mantiene un campo separado para propietario del archivo, para el grupo del archivo y para todos los demás usuarios. En este esquema, nueve bits por archivo son necesarios para registrar la información de protección. Así, para nuestro ejemplo, los campos de protección para el archivo book.tex son los siguientes: para la propietaria Sara, todos los bits se establecen; para el grupo texto, se establecen los bits r, w; y para el universo, solo se establece el bit r.
Una dificultad en la combinación de enfoques se encuentra en la interfaz de usuario. Los Usuarios deben poder saber cuándo se establecen los permisos opcionales de ACL en un archivo. En el Ejemplo de Solaris, se agrega un "+" a los permisos normales, como en:
19 -rw-r - r - + 1 jim staff 130 25 de mayo 22:13 file1
Se utiliza un conjunto separado de comandos, setfacl y getfacl, para administrar ACL.
Los usuarios de Windows generalmente administran listas de control de acceso a través de la GUI. La Figura 13.12 muestra una ventana de permisos de archivos en el sistema de archivos NTFS de Windows 7. Por ejemplo, al usuario "invitado" se le niega específicamente el acceso al archivo ListPanel.java.
Otra dificultad es asignar precedencia cuando los permisos y las ACL en conflicto. Por ejemplo, si Walter está en el grupo de un archivo, que tiene permiso de lectura, pero el archivo tiene una ACL que otorga a Walter, permiso de lectura y escritura, en caso de que Walter quiera escribir? se le concede o se le niega? Solaris y otros sistemas operativos dar prioridad a las ACL (ya que son más detalladas y no son asignadas por defecto). Esto sigue la regla general de que la especificidad debe tener prioridad.
13.4.3 Otros enfoques de protección
Otro enfoque al problema de protección es asociar una contraseña con cada archivo. Así como el acceso al sistema informático suele estar controlado por una contraseña, el acceso a cada archivo se puede controlar de la misma manera. Si las contraseñas se eligen al azar y se cambian con frecuencia, este esquema puede ser eficaz para limitar acceso a un archivo. Sin embargo, el uso de contraseñas tiene algunas desventajas. Primero, la cantidad de contraseñas que un usuario necesita recordar puede convertirse grande, lo que hace que el esquema no sea práctico. En segundo lugar, si solo se utiliza una contraseña para todos los archivos, una vez que se descubre, todos los archivos son accesibles; la protección está encendida una base de todo o nada. Algunos sistemas permiten que un usuario asocie una contraseña con un subdirectorio, en lugar de un archivo individual, para abordar este problema. Más comúnmente, el cifrado de una partición o archivos individuales proporciona protección, pero la gestión de contraseñas es clave.
En una estructura de directorios de varios niveles, debemos proteger no solo a los archivos, pero también colecciones de archivos en subdirectorios; es decir, tenemos que proporcionar un mecanismo de protección de directorios. Las operaciones de directorio que deben ser protegidas son algo diferentes de las operaciones de archivo. Queremos controlar la creación y eliminación de archivos en un directorio. Además, probablemente queramos controlar si un usuario puede determinar la existencia de un archivo en un directorio. A veces, el conocimiento de la existencia y el nombre de un archivo es importante en sí mismo. Por lo tanto, listar el contenido de un directorio debe ser una operación protegida. De manera similar, si un nombre de ruta se refiere a un archivo en un directorio, el usuario debe tener acceso tanto al directorio como al archivo. En sistemas donde los archivos pueden tener numerosos nombres de ruta (como grafos acíclicos y generales), un usuario dado puede tener diferentes derechos de acceso a un archivo en particular, dependiendo del nombre de la ruta usado.
Figura 13.12 Gestión de listas de control de acceso de Windows 10
13.5 Archivos asignados en memoria
Existe otro método para acceder a los archivos y se utiliza con mucha frecuencia. Considere una lectura secuencial de un archivo en disco usando las llamadas estándar del sistema open(), read() y write(). Cada acceso a archivos requiere una llamada al sistema y un acceso a disco. Alternativamente, podemos usar las técnicas de memoria virtual discutidas en Capítulo 10 para tratar las E/S de archivos como accesos rutinarios a la memoria. Este enfoque, conocido como mapeo de un archivo en memoria, permite que una parte del espacio de direcciones virtuales sea asociado lógicamente con el archivo. Como veremos, esto puede conducir a importantes aumentos del rendimiento.
13.5.1 Mecanismo básico
El mapeo

Continuar navegando