fcntl no Python: locks e controle de arquivos

Publicado em: 25/08/2026
Tempo de leitura: 6 minutos
Detailed close-up of a combination lock with numbers in focus, highlighting security and privacy.

O módulo fcntl oferece acesso direto às chamadas Unix fcntl() e ioctl(), além de helpers para locking de arquivos. Ele trabalha com file descriptors e expõe recursos que não existem na API de arquivos de alto nível, como alterar flags, consultar propriedades do terminal, controlar pipes e coordenar processos.

Esse poder exige cuidado. Comandos, estruturas e constantes variam por sistema operacional. Um buffer com tipo ou tamanho incorreto pode causar corrupção de dados ou até segfault. Por isso, use as funções de mais alto nível sempre que possível e isole chamadas nativas em código pequeno e testado.

Disponibilidade

fcntl está disponível em Unix e não funciona em WASI. Windows possui APIs diferentes. Código multiplataforma deve oferecer alternativa ou desativar o recurso.

try:
    import fcntl
except ImportError:
    fcntl = None

Não esconda a diferença com um fallback incompleto. Um lock de arquivo com semântica errada pode permitir duas instâncias modificando o mesmo estado.

File descriptors

As funções aceitam um inteiro retornado por fileno() ou um objeto de I/O com esse método.

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

O número pode ser reutilizado depois do fechamento. Nunca guarde um fd e continue usando após fechar o objeto correspondente.

Lock exclusivo com flock()

flock() é a interface mais simples para bloquear um arquivo inteiro.

import fcntl

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

LOCK_EX solicita exclusividade. LOCK_SH permite múltiplos leitores. O lock normalmente é advisory: outros processos precisam cooperar usando o mesmo mecanismo.

Lock não bloqueante

Combine LOCK_NB para falhar imediatamente.

import errno
import fcntl

try:
    fcntl.flock(arquivo, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as exc:
    if exc.errno in {errno.EACCES, errno.EAGAIN}:
        print("Outra instância está executando")
    else:
        raise

Confira EACCES e EAGAIN por portabilidade. O artigo de errno no Python detalha códigos de sistema.

Arquivo de lock e PID

Um arquivo contendo PID pode ajudar no diagnóstico, mas não substitui o lock. PIDs são reutilizados e o arquivo pode permanecer após 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()

Mantenha o arquivo aberto durante todo o período protegido. Fechar o descriptor normalmente libera o lock.

lockf() e regiões

lockf() envolve locks de registro do fcntl() e pode proteger uma região.

import fcntl
import os

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

A semântica varia entre sistemas e filesystems. Locks em NFS, containers, volumes compartilhados e filesystems especiais precisam de testes reais.

flock() ou lockf()?

flock() costuma ser melhor para exclusão de arquivo inteiro. lockf() é útil para regiões, mas depende mais das regras de record locking. Não misture mecanismos supondo que eles interoperam.

Documente qual mecanismo todos os processos devem usar.

Ler flags do descriptor

fcntl.fcntl() com F_GETFL consulta flags de status.

import fcntl
import os

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

Para alterar, recupere o valor, ajuste bits e use F_SETFL.

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

Preserve bits desconhecidos. Não substitua todas as flags por uma constante isolada.

FD_CLOEXEC

O flag close-on-exec impede que um descriptor vaze para um novo programa após exec.

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

Python moderno cria muitos descriptors como não herdáveis por padrão, mas código que recebe descriptors externos deve verificar ownership e herança.

Duplicar descriptors

Constantes como F_DUPFD e variantes com CLOEXEC criam outra referência ao mesmo open file description.

O novo descriptor compartilha offset e algumas flags. Fechar um não fecha o outro, mas o recurso só é liberado após todas as referências serem fechadas.

ioctl()

ioctl() envia operações específicas a dispositivos, terminais e drivers.

import array
import fcntl
import termios

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

O request e o layout do buffer vêm da documentação C da plataforma. Copiar um formato de outra arquitetura pode causar erro grave.

Buffers mutáveis

Com bytearray, array.array ou outro buffer gravável e mutate_flag=True, o kernel pode alterar o objeto no lugar. Se o buffer for curto, o módulo usa internamente uma área de até 1024 bytes e copia de volta.

O tamanho ainda precisa ser correto. A documentação alerta que incompatibilidade pode causar segfault ou corrupção sutil.

struct.pack() com cautela

Alguns comandos esperam estruturas C.

import struct

estrutura = struct.pack("hhllhh", tipo, whence, inicio, tamanho, pid, 0)

O layout depende de alinhamento, tamanho de tipos, arquitetura e sistema. Não trate uma format string encontrada na internet como portável. Prefira wrappers de alto nível ou compile uma extensão que use os headers corretos.

Limite de 1024 bytes

Quando fcntl() recebe um argumento bytes-like, o retorno tem o mesmo tamanho e é limitado a 1024 bytes. Uma operação que devolve estrutura maior não pode ser usada com segurança dessa forma.

Não tente contornar passando tamanho incorreto.

Chamadas interrompidas

No Python 3.14, ioctl() libera o GIL durante a syscall e repete automaticamente falhas por EINTR. Mesmo assim, erros de dispositivo e argumentos continuam gerando OSError.

Pipes

Em Linux, F_GETPIPE_SZ e F_SETPIPE_SZ consultam e alteram capacidade de pipe quando suportados.

if hasattr(fcntl, "F_GETPIPE_SZ"):
    tamanho = fcntl.fcntl(fd_pipe, fcntl.F_GETPIPE_SZ)
    print(tamanho)

Aumentar o tamanho pode exigir privilégios e consumir memória do kernel. Não use como substituto de backpressure. O guia de select no Python explica I/O não bloqueante.

Seals em memfd

Linux pode expor F_ADD_SEALS, F_GET_SEALS e constantes F_SEAL_* para impedir alterações em descriptors criados com os.memfd_create().

Seals podem restringir escrita, crescimento, redução e futuros mapeamentos graváveis. Eles não são portáveis e algumas operações são irreversíveis.

Em Linux recente, FICLONE e FICLONERANGE podem criar cópias copy-on-write em filesystems compatíveis. A operação pode falhar em ext4 antigo, filesystem remoto ou entre dispositivos.

Tenha fallback para cópia normal e verifique o resultado.

Open file description locks

Constantes F_OFD_SETLK e relacionadas permitem locks associados à open file description em Linux. Eles têm semântica diferente dos locks tradicionais relacionados ao processo.

Use somente quando a aplicação compreende duplicação de fd, fork e threads. Para exclusão simples, flock() continua mais claro.

Auditoria

As operações geram eventos de auditoria como fcntl.fcntl, fcntl.ioctl, fcntl.flock e fcntl.lockf. Ambientes restritos podem registrar ou impedir chamadas.

Segurança

Não aceite diretamente de usuário o número do request, cmd, fd ou bytes de estrutura. Isso pode transformar a aplicação em uma interface para controlar dispositivos ou memória.

Use allowlist de operações, valide o tipo do recurso e execute com o menor privilégio possível.

Testes

Teste lock concorrente em processos separados, liberação após exceção, crash, filesystem local e remoto, descriptors fechados, herança em subprocessos, non-blocking, arquitetura 32/64 bits, constants ausentes e permissões.

Testes de locking precisam de processos, não apenas threads, para reproduzir a semântica real.

Erros comuns

Os erros mais frequentes são esquecer que locks são advisory, fechar o arquivo cedo, misturar flock e lockf, presumir NFS igual a disco local, substituir flags existentes, reutilizar fd fechado, usar estrutura C de outra plataforma e passar buffers com tamanho errado.

Conclusão

fcntl oferece controle Unix de baixo nível sobre arquivos, pipes, locks e dispositivos. Para locking simples, prefira flock() com contextos claros. Para fcntl() e ioctl(), siga exatamente os headers e manuais da plataforma.

Valide constantes, preserve flags, limite privilégios e teste no sistema de produção. Consulte a documentação oficial de fcntl e os manuais fcntl(2) e ioctl(2).

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código Python assíncrono em notebook para inspect.markcoroutinefunction
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: identifique wrappers async

    Aprenda inspect.markcoroutinefunction no Python para identificar wrappers assíncronos, integrar frameworks e evitar detecção incorreta de corrotinas.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Código Python para percorrer pastas e arquivos com Path.walk
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: percorra diretórios com segurança

    Aprenda Path.walk no Python para percorrer diretórios, filtrar arquivos, tratar erros e controlar a travessia com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Depuração de processo Python em terminal com código
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depure processos Python em execução

    Aprenda a anexar o pdb a um processo Python em execução, inspecionar pilhas e diagnosticar travamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python e representação de frações numéricas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: converta números em frações

    Aprenda fractions.from_number no Python para converter números em frações exatas, controlar precisão e evitar arredondamentos inesperados.

    Ler mais

    Tempo de leitura: 5 minutos
    09/10/2026
    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026