Skip to content

fcntl Module

The fcntl module wraps three Unix system calls, fcntl(), ioctl() and flock(), plus lockf(), which is fcntl()'s record locking behind a simpler signature. Each is one call into the kernel, and the bounds below count that call as constant: what the kernel does with a lock table or a driver's ioctl handler is not priced. The only work Python adds is copying a bytes-like argument through a fixed 1024-byte buffer: in before the call, and out afterwards into a new bytes object of the same length or, for a buffer ioctl() mutates, back into that buffer. The one exception is a writable buffer longer than that, which a mutating ioctl() hands to the kernel as it is.

Complexity Reference

Operation Time Space Notes
fcntl.fcntl(fd, cmd, arg=0) with an integer arg O(1) O(1) One system call; returns its integer result
fcntl.fcntl(fd, cmd, arg) with a bytes or str arg O(n) O(n) n = length of arg in bytes, UTF-8 encoded for a str; at most 1024 or ValueError. Copied in, and the result is a new bytes of the same length. Other bytes-like objects such as bytearray are accepted from Python 3.14
fcntl.ioctl(fd, request, arg=0) with an integer arg O(1) O(1) One system call; returns its integer result
fcntl.ioctl(fd, request, arg) with a read-only bytes-like or str arg, or mutate_flag=False O(n) O(n) Same copy and 1024-byte cap as fcntl()
fcntl.ioctl(fd, request, buffer, mutate_flag=True) with a writable buffer O(n) O(1) n = buffer length. Up to 1024 bytes it is copied through the fixed buffer and back; a longer one is handed to the kernel uncopied, so the cap does not apply. The caller's buffer is changed in place and the integer result returned
fcntl.flock(fd, operation) O(1) O(1) Locks the open file description, so a second open() of the file contends even in the same process; a system without flock(2) gets lockf()'s process-owned lock instead. Waits for a conflicting lock unless LOCK_NB is set, which raises BlockingIOError instead; the waiting is not work
fcntl.lockf(fd, cmd, len=0, start=0, whence=0) O(1) O(1) A byte-range lock through fcntl(), owned by the process: another descriptor in the same process never contends, and closing any descriptor of the file releases it. An exclusive lock needs the file open for writing
fcntl.LOCK_SH, fcntl.LOCK_EX, fcntl.LOCK_NB, fcntl.LOCK_UN O(1) O(1) Operation flags for flock() and lockf()
fcntl.LOCK_MAND, fcntl.LOCK_READ, fcntl.LOCK_WRITE, fcntl.LOCK_RW O(1) O(1) Linux mandatory-lock flags for flock(); platform-dependent
fcntl.F_DUPFD, fcntl.F_DUPFD_CLOEXEC, fcntl.F_GETFD, fcntl.F_SETFD, fcntl.F_GETFL, fcntl.F_SETFL, fcntl.FD_CLOEXEC, fcntl.FASYNC O(1) O(1) Descriptor commands and flag bits for fcntl()
fcntl.F_GETLK, fcntl.F_SETLK, fcntl.F_SETLKW, fcntl.F_GETLK64, fcntl.F_SETLK64, fcntl.F_SETLKW64, fcntl.F_OFD_GETLK, fcntl.F_OFD_SETLK, fcntl.F_OFD_SETLKW, fcntl.F_RDLCK, fcntl.F_WRLCK, fcntl.F_UNLCK, fcntl.F_SHLCK, fcntl.F_EXLCK O(1) O(1) Record-lock commands and lock types for fcntl(); which exist is platform-dependent
fcntl.F_GETOWN, fcntl.F_SETOWN, fcntl.F_GETSIG, fcntl.F_SETSIG, fcntl.F_NOTIFY, fcntl.DN_ACCESS, fcntl.DN_MODIFY, fcntl.DN_CREATE, fcntl.DN_DELETE, fcntl.DN_RENAME, fcntl.DN_ATTRIB, fcntl.DN_MULTISHOT, fcntl.F_GETLEASE, fcntl.F_SETLEASE, fcntl.F_GETPIPE_SZ, fcntl.F_SETPIPE_SZ O(1) O(1) Signal-driven I/O, directory notification, lease and pipe-size commands for fcntl(); platform-dependent
fcntl.F_ADD_SEALS, fcntl.F_GET_SEALS, fcntl.F_SEAL_SEAL, fcntl.F_SEAL_SHRINK, fcntl.F_SEAL_GROW, fcntl.F_SEAL_WRITE O(1) O(1) File-sealing commands and seal bits for fcntl(); platform-dependent
fcntl.F_GETPATH, fcntl.F_NOCACHE, fcntl.F_FULLFSYNC O(1) O(1) macOS commands for fcntl(); platform-dependent
fcntl.I_ATMARK, fcntl.I_CANPUT, fcntl.I_CKBAND, fcntl.I_FDINSERT, fcntl.I_FIND, fcntl.I_FLUSH, fcntl.I_FLUSHBAND, fcntl.I_GETBAND, fcntl.I_GETCLTIME, fcntl.I_GETSIG, fcntl.I_GRDOPT, fcntl.I_GWROPT, fcntl.I_LINK, fcntl.I_LIST, fcntl.I_LOOK, fcntl.I_NREAD, fcntl.I_PEEK, fcntl.I_PLINK, fcntl.I_POP, fcntl.I_PUNLINK, fcntl.I_PUSH, fcntl.I_RECVFD, fcntl.I_SENDFD, fcntl.I_SETCLTIME, fcntl.I_SETSIG, fcntl.I_SRDOPT, fcntl.I_STR, fcntl.I_SWROPT, fcntl.I_UNLINK O(1) O(1) STREAMS requests for ioctl(); platform-dependent
fcntl.F_DUP2FD, fcntl.F_DUP2FD_CLOEXEC O(1) O(1) Python 3.11+; FreeBSD commands for fcntl(), platform-dependent
fcntl.FICLONE, fcntl.FICLONERANGE O(1) O(1) Python 3.12+; Linux copy-on-write clone requests for ioctl(), platform-dependent
fcntl.F_GETOWN_EX, fcntl.F_SETOWN_EX, fcntl.F_OWNER_PID, fcntl.F_OWNER_PGRP, fcntl.F_OWNER_TID, fcntl.F_GET_RW_HINT, fcntl.F_SET_RW_HINT, fcntl.F_GET_FILE_RW_HINT, fcntl.F_SET_FILE_RW_HINT, fcntl.RWH_WRITE_LIFE_NOT_SET, fcntl.RWH_WRITE_LIFE_NONE, fcntl.RWH_WRITE_LIFE_SHORT, fcntl.RWH_WRITE_LIFE_MEDIUM, fcntl.RWH_WRITE_LIFE_LONG, fcntl.RWH_WRITE_LIFE_EXTREME, fcntl.F_SEAL_FUTURE_WRITE, fcntl.F_READAHEAD, fcntl.F_ISUNIONSTACK, fcntl.F_KINFO, fcntl.F_RDAHEAD, fcntl.F_CLOSEM, fcntl.F_MAXFD, fcntl.F_GETNOSIGPIPE, fcntl.F_SETNOSIGPIPE O(1) O(1) Python 3.13+; commands and values for fcntl(), each on the platforms that define it
fcntl.F_DUPFD_QUERY O(1) O(1) Python 3.14+; Linux command for fcntl(), platform-dependent

File Locking and Control

File Locking

import fcntl

# Open without truncating: the file must not change before the lock is held
with open('data.txt', 'a+') as f:
    # Acquire lock - O(1)
    fcntl.flock(f.fileno(), fcntl.LOCK_EX)

    try:
        # Replace the contents while locked; flush so the data lands before the lock goes
        f.seek(0)
        f.truncate()
        f.write('Protected data')
        f.flush()
    finally:
        # Release lock - O(1)
        fcntl.flock(f.fileno(), fcntl.LOCK_UN)

Non-blocking Lock

import fcntl

# A flock() lock belongs to the open file description, so a second
# open() of the same file contends with the first even in one process
with open('file.txt', 'w') as holder, open('file.txt', 'w') as f:
    fcntl.flock(holder.fileno(), fcntl.LOCK_EX)
    try:
        # Non-blocking exclusive lock - O(1)
        fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
        print("Lock acquired")
    except BlockingIOError:
        print("File is locked elsewhere")