fcntl en Python: locks y control de archivos

Publicado el: 25/08/2026
Tempo de leitura: 5 minutos
Multiple padlocks securing a green chain link fence, symbolizing safety and protection.

El módulo fcntl expone las llamadas Unix fcntl() e ioctl(), además de helpers para locking de archivos. Trabaja con file descriptors y permite acceder a flags, locks, configuración de pipes, control de terminales y operaciones específicas de dispositivos.

Ese poder exige cuidado. Los comandos, constantes y layouts de estructuras C cambian por sistema operativo y arquitectura. Un buffer con tamaño o tipo incorrecto puede corromper memoria o terminar el proceso. Prefiere APIs de mayor nivel y aísla las llamadas nativas en funciones pequeñas y probadas.

Disponibilidad

fcntl está disponible en Unix y no funciona en WASI. Windows usa APIs distintas. Software portable debe desactivar la característica o proporcionar una implementación específica verificada.

try:
    import fcntl
except ImportError:
    fcntl = None

No presentes un fallback incompleto como equivalente. Un lock con semántica incorrecta puede permitir escrituras simultáneas sobre el mismo estado.

File descriptors

Las funciones aceptan un entero o un objeto de I/O cuyo fileno() devuelve un descriptor real.

with open("datos.txt", "a+", encoding="utf-8") as archivo:
    fd = archivo.fileno()
    print(fd)

El sistema puede reutilizar el número después del cierre. No guardes un fd y continúes usándolo tras cerrar su objeto.

Lock exclusivo con flock()

flock() es la interfaz más clara para bloquear un archivo completo.

import fcntl

with open("estado.lock", "a+") as archivo:
    fcntl.flock(archivo, fcntl.LOCK_EX)
    try:
        actualizar_estado()
    finally:
        fcntl.flock(archivo, fcntl.LOCK_UN)

LOCK_EX solicita exclusividad y LOCK_SH permite lectores cooperantes. Normalmente son locks advisory: todos los procesos deben usar la misma convención.

Adquisición no bloqueante

Combina LOCK_NB para fallar inmediatamente.

import errno
import fcntl

try:
    fcntl.flock(archivo, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as exc:
    if exc.errno in {errno.EACCES, errno.EAGAIN}:
        print("Otra instancia está activa")
    else:
        raise

Comprueba ambos códigos por portabilidad. errno en Python explica estos errores.

Un archivo con PID no es un lock

Guardar el PID ayuda al diagnóstico, pero no crea exclusión. Los PIDs se reutilizan y el archivo puede quedar después de un crash.

import fcntl
import os

lock = open("app.lock", "a+")
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
lock.seek(0)
lock.truncate()
lock.write(str(os.getpid()))
lock.flush()

Mantén el descriptor abierto durante todo el período protegido. Cerrarlo normalmente libera el lock.

Regiones con lockf()

lockf() envuelve record locking y puede proteger una región de bytes.

import fcntl
import os

with open("base.dat", "r+b") as archivo:
    fcntl.lockf(archivo, fcntl.LOCK_EX, 128, 0, os.SEEK_SET)
    try:
        archivo.seek(0)
        actualizar_registro(archivo)
    finally:
        fcntl.lockf(archivo, fcntl.LOCK_UN, 128, 0, os.SEEK_SET)

La semántica cambia por sistema y filesystem. Prueba NFS, volúmenes de containers, almacenamiento compartido y filesystems especiales en el entorno real.

flock() o lockf()

Usa flock() para exclusión del archivo completo. Usa lockf() cuando necesitas rangos y todos los participantes siguen el mismo modelo. No asumas interoperabilidad.

Leer flags de estado

F_GETFL devuelve las flags actuales.

import fcntl
import os

flags = fcntl.fcntl(fd, fcntl.F_GETFL)
print(bool(flags & os.O_NONBLOCK))

Para cambiar un bit, conserva los demás.

fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

Reemplazar toda la máscara puede eliminar comportamiento importante.

Close-on-exec

FD_CLOEXEC evita que un descriptor se filtre a un programa iniciado con exec.

flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)

Python moderno crea muchos descriptors como no heredables, pero software que recibe descriptors externos todavía debe definir ownership.

Duplicación

F_DUPFD y variantes con CLOEXEC crean otra referencia a la misma open file description. Las copias pueden compartir offset y flags.

Cerrar un descriptor no libera el recurso mientras existan duplicados abiertos.

ioctl()

ioctl() envía requests específicos a dispositivos y terminales.

import array
import fcntl
import termios

buffer = array.array("h", [0])
fcntl.ioctl(0, termios.TIOCGPGRP, buffer, True)
print(buffer[0])

El número de request y el layout deben venir de la documentación C de la plataforma exacta. Copiar una estructura de otra arquitectura puede provocar crash.

Buffers mutables

Con bytearray, array.array u otro buffer escribible y mutate_flag=True, la syscall puede modificar el objeto. Python puede usar un área interna de 1024 bytes, pero el tamaño lógico debe ser correcto.

La documentación advierte que una incompatibilidad puede causar segmentation fault o corrupción sutil.

struct.pack() con cautela

Algunas operaciones esperan una estructura C.

import struct

payload = struct.pack("hhllhh", tipo, whence, inicio, longitud, pid, 0)

Alineación, tamaños, endianess y campos cambian. Una format string encontrada en internet no es portable. Prefiere flock(), un wrapper mantenido o una extensión compilada con los headers correctos.

Límite de 1024 bytes

Cuando fcntl() recibe bytes-like, el resultado tiene el mismo tamaño y un máximo de 1024 bytes. No hagas segura una operación grande adivinando otro tamaño.

Llamadas interrumpidas

En Python 3.14, ioctl() libera el GIL durante la syscall y reintenta automáticamente EINTR. Otros errores siguen generando OSError.

Capacidad de pipes

Linux puede exponer F_GETPIPE_SZ y F_SETPIPE_SZ.

if hasattr(fcntl, "F_GETPIPE_SZ"):
    capacidad = fcntl.fcntl(pipe_fd, fcntl.F_GETPIPE_SZ)
    print(capacidad)

Aumentarla puede requerir privilegios y consume memoria del kernel. No sustituye backpressure. Consulta select en Python.

Seals en memfd

Linux puede ofrecer F_ADD_SEALS, F_GET_SEALS y F_SEAL_* para descriptors de os.memfd_create(). Los seals pueden impedir escritura, reducción, crecimiento o mapeos futuros.

No son portables y algunas decisiones son irreversibles.

FICLONE y FICLONERANGE pueden crear clones copy-on-write en filesystems compatibles. Pueden fallar entre dispositivos, en red o en formatos no soportados.

Implementa fallback de copia normal y valida el destino.

Open file description locks

Linux ofrece F_OFD_SETLK y constantes relacionadas. Estos locks pertenecen a la open file description y se comportan distinto con duplicados, fork y threads.

Úsalos solo cuando necesitas esa semántica. Para exclusión simple, flock() es más claro.

Auditoría

Las llamadas generan eventos como fcntl.fcntl, fcntl.ioctl, fcntl.flock y fcntl.lockf. Entornos restringidos pueden registrarlos o rechazarlos.

Seguridad

No aceptes directamente de un usuario el request, cmd, fd o estructura empaquetada. Eso puede exponer dispositivos y recursos del proceso.

Usa allowlist, verifica el tipo de descriptor y ejecuta con mínimos privilegios.

Pruebas

Prueba procesos competidores, limpieza tras excepción, crash, filesystem local y remoto, descriptors cerrados, herencia en subprocesses, non-blocking, builds 32/64 bits, constantes ausentes y permisos.

Los locks deben probarse con procesos separados, no solo threads.

Errores comunes

Los fallos frecuentes son olvidar que los locks son advisory, cerrar el archivo demasiado pronto, mezclar flock y lockf, asumir que NFS es igual al disco local, sobrescribir flags, reutilizar fd cerrado, copiar estructuras de otra plataforma y pasar buffers con tamaño incorrecto.

Conclusión

fcntl proporciona control Unix de bajo nivel sobre archivos, pipes, locks y dispositivos. Prefiere flock() para exclusión simple. Para fcntl() e ioctl(), sigue exactamente los headers y manuales de la plataforma.

Valida constantes, conserva flags, minimiza privilegios y prueba en producción. Consulta la documentación oficial de fcntl, fcntl(2) y ioctl(2).

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    readline en Python: historial y autocomplete

    Aprende readline en Python para historial, autocompletado, edición de línea, GNU Readline, libedit y prompts seguros en terminal.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Vibrant green tree python elegantly coiled on branch, showcasing its natural beauty.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    io en Python: domina streams y buffers

    Aprende io en Python para streams de texto y bytes, buffering, encoding, StringIO, BytesIO, I/O bruto e interfaces file-like.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Vibrant green tree python elegantly coiled on branch, showcasing its natural beauty.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    select en Python: monitorea varios I/O

    Aprende select en Python para monitorear sockets y pipes, tratar I/O parcial, backpressure, poll, epoll, señales y diferencias de plataforma.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    signal en Python: cierre correcto

    Aprende signal en Python para manejar SIGTERM y SIGINT, detener servicios, usar timers, wakeup FD y evitar deadlocks en handlers.

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026
    Creative concept showing the word 'error' with cut out letters on a table with scissors and paper.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    errno en Python: errores del sistema

    Aprende errno en Python para interpretar códigos del sistema, tratar OSError, archivos, red, retries y llamadas nativas de forma portable.

    Ler mais

    Tempo de leitura: 4 minutos
    24/08/2026
    African American man using a laptop in a well-organized library setting with colorful bookshelves.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ctypes en Python: usa bibliotecas C

    Aprende ctypes en Python para cargar bibliotecas C, definir tipos y punteros, gestionar memoria, callbacks, ABI y errores nativos.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026