Arquitectura y Gestión de Archivos en Sistemas UNIX

En el diseño de sistemas tipo UNIX, el principio fudnamental es tratar todos los elementos como archivos. Para comprender cómo se almacenan estos datos, es necesario revisar la jerarquía física del hardware de almacenamiento. Un disco duro está compuesto por múltiples platos, y cada plato posee pistas concéntricas que se dividen en sectores. El conjunto de pistas alineadas verticalmente en diferentes platos forma lo que se conoce como un cilindro. La unidad mínima de direccionamiento físico es el sector. Aunque históricamente los sectores manejaban 512 bytes, las tecnologías modernas utilizan sectores físicos de 4096 bytes (4K), aunque el sistema operativo puede abstraerlos como sectores lógicos de 512 bytes para mantener compatibilidad. Dado que gestionar operaciones de E/S a nivel de sector individual es ineficiente debido a la sobrecarga (overhead) del sistema, el sistema de arhcivos agrupa varios sectores en una unidad lógica denominada bloque. El tamaño estándar de un bloque suele ser de 4096 bytes. Podemos consultar el tamaño del bloque lógico programáticamente en C utilizando la interfaz del sistema: ```

#include <stdio.h> #include <sys/statvfs.h>

int main() { struct statvfs fs_info; // Obtener información del sistema de archivos raíz if (statvfs("/", &fs_info) == 0) { unsigned long block_size = fs_info.f_frsize; printf("Tamaño de bloque del archivo: %lu bytes\n", block_size); } return 0; }


### 2. Estructura del Sistema de Archivos e Inodos

A nivel lógico, una partición de disco contiene un sistema de archivos. Este se estructura generalmente en un bloque de arranque (boot block), un superbloque (superblock) que describe el estado del sistema de archivos, y grupos de cilindros que contienen i-nodos (inodos) y bloques de datos. El **inodo** es una estructura de datos fundamental que almacena metadatos del archivo, como permisos, tamaño, propietario y punteros a los bloques de datos. Es crucial notar que el nombre del archivo no se almacena en el inodo, sino en la entrada de directorio que apunta a ese inodo. Cada inodo posee un contador de enlaces (link count). Este contador refleja cuántas entradas de directorio apuntan a él. Cuando este contador llega a cero, el sistema libera los bloques de datos asociados. Esto permite que un mismo archivo físico sea accesible desde diferentes rutas (enlaces duros). Si renombramos un archivo dentro del mismo sistema de archivos, el inodo no cambia; simplemente se actualiza la entrada del directorio. Por ejemplo, al crear un directorio nuevo, automáticamente se crean dos entradas: `.` (apunta a sí mismo) y `..` (apunta al directorio padre), estableciendo la jerarquía del sistema de archivos mediante punteros a inodos. ### 3. Mecanismos de E/S de Archivos

La interacción con el kernel para operaciones de archivos ocurre a través de dos capas principales: - **E/S sin búfer (System Calls):** Operaciones directas al kernel. El flujo es: Datos → Buffer del Kernel → Disco. Funciones típicas: `read`, `write`, `open`.
- **E/S estándar (Standard I/O Library):** Proporciona un búfer adicional en espacio de usuario para optimizar rendimiento. El flujo es: Datos → Búfer de Stream → Buffer del Kernel → Disco. Funciones típicas: `fread`, `fwrite`.

El kernel identifica los archivos abiertos mediante **descriptores de archivo**, que son enteros no negativos. Por convención, todo proceso inicia con tres descriptores abiertos. Podemos verificar sus valores numéricos de forma programática: ```

#include <stdio.h>
#include <unistd.h>

int main(void) {
    // STDIN_FILENO, STDOUT_FILENO y STDERR_FILENO son macros definidas en unistd.h
    printf("Descriptor de entrada estándar: %d\n", STDIN_FILENO);
    printf("Descriptor de salida estándar: %d\n", STDOUT_FILENO);
    printf("Descriptor de error estándar: %d\n", STDERR_FILENO);

    // Distinción de tipos: STDIN_FILENO es 'int', stdin es 'FILE*'
    printf("Tipo de stdin (puntero): %p\n", (void*)stdin);
    
    return 0;
}

Compartición de Archivos entre Procesos

El kernel mantiene tres estructuras de datos para la gestión de archivos abiertos: 1. Tabla de descriptores de archivo: Específica de cada proceso, asocia un descriptor con un puntero a una entrada en la tabla de archivos. 2. Tabla de archivos: Contiene el estado del archivo (modos de lectura/escritura) y el offset actual (posición de lectura/escritura). 3. Tabla de V-nodos: Contiene información del inodo y punteros a las funciones de operación del sistema de archivos.

Si dos procesos independientes abren el mismo archivo, comparten la entrada en la tabla de V-nodos (apuntando al mismo inodo), pero poseen entradas separadas en la tabla de archivos, manteniendo offsets independientes. ### 4. Permisos Especiales y Máscaras

Más allá de los permisos básicos de lectura, escritura y ejecución (r, w, x), existen bits de permiso especiales: - Set-User-ID (SUID): Cuando se establece en un ejecutable, el proceso resultante se ejecuta con los permisos del propietario del archivo, no del usuario que lo ejecuta. Es vital para herramientas como passwd que necesitan escribir en archivos protegidos del sistema.

  • Set-Group-ID (SGID): Para directorios, asegura que los archivos creados dentro hereden el grupo propietario del directorio.
  • Sticky Bit: Utilizado en directorios compartidos como /tmp. Impide que usuarios borren archivos de otros usuarios, incluso si tienen permisos de escritura en el directorio.

La creación de nuevos archivos se ve afectada por la máscara de permisos (umask). Podemos manipular esta máscara mediante código para establecer permisos predeterminados seguros: ```

#include <stdio.h> #include <sys/stat.h> #include <fcntl.h>

int main(void) { // Establecer umask a 022 (quitar permisos de escritura para grupo y otros) mode_t old_mask = umask(022);

// Crear un archivo con permisos 0666 (rw-rw-rw-), resultado real: 0644 (rw-r--r--)
int fd = open("nuevo_archivo.txt", O_CREAT | O_WRONLY, 0666);
if (fd != -1) {
    close(fd);
    printf("Archivo creado con umask aplicada.\n");
}

// Restaurar máscara original
umask(old_mask);
return 0;

}


</div>

Etiquetas: unix filesystem inode system-calls linux-kernel

Publicado el 8-22 18:45