Entrada/Salida de Archivos y Bibliotecas en Sistemas Linux

Este documento aborda dos áreas fundamentales en el desarrollo de sistemas bajo Linux: el manejo directo de archivos mediante llamadas al sistema y la creación de bibliotecas de código reutilizable.

Parte I: Operaciones de E/S de Archivos a Bajo Nivel

Este apartado se enfoca en funciones que interactúan directamente con el kernel, sin buffering intermedio.

1. Manipulación de Descriptores de Archivo

El núcleo de la E/S de archivos es el descriptor (fd), un identificador entero no negativo.

La función para abrir un archivo y su contraparte para cerrarlo son:

int descriptor = open(const char *ruta, int banderas, ...);
int estado_cierre = close(int descriptor);

Las banderas (banderas) principales son O_RDONLY (lectura), O_WRONLY (escritura) y O_RDWR (lectura/escritura). Se pueden combinar con O_CREAT (crear si no existe), O_APPEND (añadir al final) y O_TRUNC (truncar a longitud cero). Al crear, se especifica el modo de permisos, el cual se ve afectado por la umask del proceso (p.ej., 0666 & ~umask).

2. Lectura y Escritura de Datos

Para transferir datos entre el descriptor y un búfer en memoria:

ssize_t bytes_leidos = read(int descriptor, void *búfer, size_t cantidad);
ssize_t bytes_escritos = write(int descriptor, const void *búfer, size_t cantidad);

Un bucle de lectura/escritura es esencial para manejar casos donde no se transfieren todos los bytes de una sola vez (común en dispositivos y sockets).

3. Control de la Posición en el Archivo

La posición (cursor) dentro de un archivo se mueve con lseek:

off_t nueva_pos = lseek(int descriptor, off_t desplazamiento, int punto_referencia);

Los puntos de referencia (punto_referencia) son SEEK_SET (inicio absoluto), SEEK_CUR (relativo a la posición actual) y SEEK_END (relativo al final). Esta función es clave para determinar el tamaño de un archivo (moviéndose al final y obteniendo la posición) o para leer en posiciones arbitrraias.

4. Control Avanzado y Bloqueo de Archivos

La función fcntl proporciona control avanzado sobre descriptores:

int resultado = fcntl(int descriptor, int comando, ... /* arg */ );

Permite, por ejemplo, duplicar descriptores (F_DUPFD) o gestionar bloqueos de archivo. Para bloqueos, se usa la estructura struct flock:

struct flock candado;
candado.l_type = F_WRLCK; // Tipo: F_RDLCK, F_WRLCK, F_UNLCK
candado.l_whence = SEEK_SET;
candado.l_start = 0;
candado.l_len = 100; // Longitud del segmento a bloquear
fcntl(descriptor, F_SETLK, &candado);

Parte II: Metadatos, Directorios y Mapeo de Usuarios

Esta sección cubre la inspección de atributos de archivos y la navegación por el sistema de archivos.

1. Consulta de Atributos de Archivo

Para obtener información detallada de un archivo:

struct stat info_archivo;
int exito = stat(const char *ruta, &info_archivo); // Sigue enlaces simbólicos
int exito = lstat(const char *ruta, &info_archivo); // No sigue enlaces simbólicos
int exito = fstat(int descriptor, &info_archivo);   // Usando descriptor

La estructura stat contiene campos clave como st_mode (tipo y permisos), st_size (tamaño), st_uid/st_gid (propietario/grupo) y st_mtime (última modificación). Para interpretar st_mode, se usan macros como S_ISREG(modo) (archivo regular) o S_ISDIR(modo) (directorio).

2. Iteración sobre Contenidos de Directorios

Para listar los elementos dentro de un directorio:

DIR *flujo_dir = opendir(const char *ruta_directorio);
struct dirent *entrada;
while ((entrada = readdir(flujo_dir)) != NULL) {
    // entrada->d_name contiene el nombre del archivo/subdirectorio
    // entrada->d_type indica el tipo (DT_REG, DT_DIR, etc.)
}
closedir(flujo_dir);

El flujo (flujo_dir) debe cerrarse después de su uso. Las entradas incluyen los directorios especiales . (actual) y .. (padre).

3. Resolución de Identificadores de Usuario y Grupo

Para traducir IDs numéricos a nombres legibles:

struct passwd *datos_usuario = getpwuid(uid_t uid); // Busca por UID
struct group *datos_grupo = getgrgid(gid_t gid);   // Busca por GID

Estas estructuras proporcionan campos como pw_name (nombre de usuario) y gr_name (nombre del grupo).

Aspectos Críticos a Considerar

  • Enlaces Simbólicos: Usar stat en un enlace devuelve información del archivo objetivo. Para información del propio enlace, se debe usar lstat.
  • Gestión de Descriptores: Verificar que open no retorne -1. Siempre cerrar descriptores con close para evitar fugas de recursos.
  • Umask: Los permisos al crear archivos se enmascaran (&) con umask, generalmente 0002.

Parte III: Bibliotecas Estáticas y Dinámicas en Linux

Las bibliotecas permiten encapsular y reutilizar código compilado.

1. Concepto y Nomenclatura

Una biblioteca es un archivo que contiene código objeto precompilado. En Linux:

  • Biblioteca Estática: Archivo con extensión .a (p.ej., libmatematicas.a).
  • Biblioteca Compartida (Dinámica): Archivo con extensión .so (p.ej., libssl.so.1.1). Puede incluir información de versión.

2. Bibliotecas Estáticas

Su código se copia enlazado al ejecutable final durante la compilación.

Creación:

  1. Compilar el código fuente a código objeto: gcc -c modulo.c -o modulo.o
  2. Empaquetar los objetos en una biblioteca: ar -crv libnombre.a modulo.o modulo2.o

Uso: Al compilar el programa principal, se enlaza con -lnombre -L/ruta/a/biblioteca.

3. Bibliotecas Compartidas (Dinámicas)

Su código se carga en memoria en tiempo de ejecución y puede ser compartido por múltiples procesos.

Creación:

  1. Generar código independiente de posición (PIC): gcc -c -fPIC modulo.c -o modulo.o
  2. Enlazar los objetos en una biblioteca compartida: gcc -shared modulo.o -o libnombre.so

Uso: Se enlaza durante la compilación con -lnombre -L/ruta, pero el sistema debe encontrar el archivo .so en tiempo de ejecución.

Configuración de Rutas en Tiempo de Ejecución:

  • Variable de Entorno: export LD_LIBRARY_PATH=/ruta/a/libreria:$LD_LIBRARY_PATH (efímera).
  • Configuración del Sistema: Añadir la ruta a /etc/ld.so.conf y ejecutar ldconfig (permanente).
  • Rutas por Defecto: Copiar el archivo .so a /usr/lib o /lib.

4. Comparativa Clave

  • Enlace: La estática ocurre en compilación; la dinámica, en ejecución.
  • Tamaño del Ejecutable: Con estática, el ejecutable es más grande; con dinámica, es más pequeño.
  • Consumo de Memoria: La estática duplica código en memoria por proceso; la dinámica lo comparte.
  • Actualización: Cambiar una biblioteca estática requiere recompilar; una dinámica, solo reemplazar el archivo .so (si la API es compatible).

Etiquetas: linux system_calls file_IO POSIX stat

Publicado el 6-10 16:37