Skip to content

Archive

Linux

205 articles
Software Engineering 17 Sep 2026 4 min read

Linux timerfd Read Reports Accumulated Expirations

A periodic Linux timerfd can expire several times before an event loop reads it. The next successful read() does not return one record per wakeup. It returns one host-order uint64_t containing the number of expirations accumulated since the timer was last armed or since the preceding successful read. That count makes timerfd readiness a notification that timer state is consumable, not a one-to-one mapping between scheduler wakeups and timer periods.

Software Engineering 17 Sep 2026 6 min read

Linux timerfd Makes Missed Periods Observable as Expiration Counts

A periodic Linux timerfd can expire several times before an event loop runs again. The next successful read() does not merely report that the timer fired; it returns an unsigned 64-bit count of expirations accumulated since the previous successful read or since the timer was last configured. Delayed dispatch therefore becomes observable as a count rather than a sequence of queued timer records. That contract separates timer schedule from consumer execution. A process may be descheduled, an event loop may spend time on other descriptors, or several periods may pass before the timerfd is consumed. The kernel tracks expirations, while application code decides what multiple expirations mean for the work associated with them.

Software Engineering 17 Sep 2026 7 min read

Linux splice Makes the Pipe a Kernel Data-Transfer Boundary

splice() can transfer bytes between two file descriptors without first copying the payload into a userspace buffer, but the interface requires at least one endpoint to be a pipe. That requirement makes the pipe more than an incidental transport. It is the kernel-visible buffer boundary around which the operation’s offset, blocking, capacity, and partial-progress semantics are defined. This differs from a conventional read() followed by write(). In that sequence, userspace owns an intermediate byte array and can inspect or modify it. With splice(), the payload can remain in kernel-managed storage while the process coordinates movement between endpoints.

Software Engineering 17 Sep 2026 5 min read

Linux signalfd Turns Pending Signals Into Readable State

A Linux process can block selected signals and receive them by reading a file descriptor instead of running an asynchronous handler. signalfd() makes those pending signals visible through the same readiness interfaces used for sockets, pipes, and other descriptors, including poll() and epoll. That conversion is not a replacement for signal masking. The descriptor has its own signal set, while each thread retains a signal mask that controls ordinary delivery. A robust design depends on both states remaining aligned.

Software Engineering 17 Sep 2026 6 min read

Linux signalfd Moves Signal Delivery Into File-Descriptor Readiness

Linux signalfd Moves Signal Delivery Into File-Descriptor Readiness signalfd() changes the consumption interface for selected Linux signals. Instead of arranging for an asynchronous signal handler to run when one of those signals is delivered, a process can block the signals and receive their information by reading a file descriptor. That descriptor can participate in poll(), epoll, and related readiness mechanisms, so signal handling can share the same dispatch boundary as sockets, pipes, timers, and other pollable objects.

Software Engineering 17 Sep 2026 6 min read

Linux seccomp Notification Splits Syscall Entry From Supervisor Response

A seccomp filter can stop a task at syscall entry and turn that event into a message for another process. With SECCOMP_RET_USER_NOTIF, the kernel does not immediately execute the selected syscall. It creates a notification for a listener, blocks the calling task, and waits for a response that can supply a return value, inject a file descriptor, or permit the syscall to continue. That boundary is narrower than general syscall emulation. The notification carries syscall metadata and register argument values, while memory referenced by pointer arguments remains in the target process. The target can also disappear or have its notification invalidated while a supervisor is making a decision. Those properties make identity, memory ownership, and response timing part of the interface contract.

Software Engineering 17 Sep 2026 6 min read

Linux pidfds Turn Process Identity Into a Pollable Handle

A Linux process ID is a number from a reusable namespace. A pidfd is different: it is a file descriptor that refers to a particular process. That distinction changes process management from repeated lookup by numeric name into operations against a kernel-held handle whose identity does not silently retarget when a PID is recycled. The difference is most visible in supervisors, launchers, sandboxes, and service managers that retain process references across asynchronous work. A numeric PID can remain syntactically valid after the original process exits, yet later identify another process. A pidfd keeps the reference tied to the original process object and can also participate in descriptor-oriented event loops.

Software Engineering 17 Sep 2026 5 min read

Linux pidfd Binds Process Identity to a File Descriptor

A numeric PID is a name from a reusable kernel namespace. Once a process exits and its PID becomes available for reuse, a later process can receive the same number. Linux pidfds add a different form of reference: a file descriptor tied to a specific task rather than a number that must be resolved again at each operation. That distinction changes the boundary between process discovery and later process control. A program can resolve a PID once with pidfd_open(), retain the resulting descriptor, and use pidfd-aware interfaces without treating the numeric PID as permanent identity.

Software Engineering 17 Sep 2026 6 min read

Linux openat2 Makes Path Resolution Policy Part of the Open Operation

A pathname is not an object reference. It is an instruction for traversing a mutable namespace, and another task can alter directory entries, symbolic links, or mounts while that traversal is relevant to an application. Linux openat2() addresses this boundary by placing path-resolution constraints in the same kernel operation that returns the file descriptor. That placement matters when a program accepts a pathname but intends to confine resolution to a directory tree. A user-space sequence that inspects components and later calls open() separates validation from use. openat2() can instead make selected traversal rules part of the lookup itself.

Software Engineering 17 Sep 2026 9 min read

Linux openat2 Makes Path Resolution Constraints Atomic

A pathname can name a different object by the time a second lookup checks it. On Linux, openat2() addresses that boundary by attaching resolution constraints to the same kernel operation that walks the pathname and opens the resulting object. The policy is evaluated during lookup rather than inferred from a pathname inspected before or after the open. This distinction matters whenever a process accepts path components from a less-trusted source while intending to keep resolution inside a directory, reject symbolic links, avoid mount crossings, or require a cache-only lookup. The relevant object is not the input string alone. It is the result of resolving that string against a live namespace whose directory entries, links, and mounts can change concurrently.

Software Engineering 17 Sep 2026 6 min read

Linux O_TMPFILE Keeps Staging Files Out of the Namespace

Linux O_TMPFILE creates a regular file without first placing a name for that file in a directory. The caller receives a file descriptor and can write data, set metadata, or abandon the object while no pathname exposes the partially prepared file. If publication is required, a later link operation can attach a directory entry to the same inode. This separates object construction from namespace publication. It does not make every surrounding filesystem operation transactional, and it does not provide replacement semantics for an existing destination. Its useful boundary is narrower: intermediate file state can remain reachable only through open references until the process explicitly creates a name.

Software Engineering 17 Sep 2026 6 min read

Linux mmap Keeps File Lifetime Separate From Descriptor Lifetime

A successful file-backed mmap() creates a virtual-memory mapping that does not depend on keeping the source file descriptor open. Linux explicitly permits the descriptor to be closed immediately after mmap() returns without invalidating the mapping. The mapping and the descriptor are therefore separate references with separate lifetimes. That separation is easy to miss because both originate from the same open file. It becomes operationally important when code closes descriptors aggressively, replaces pathnames, truncates files, or passes mappings across fork(). A mapped address is not a delayed read() through the original descriptor; it participates in the virtual-memory system under its own mapping contract.

Software Engineering 17 Sep 2026 6 min read

Linux memfd Seals Turn Mutable File State Into a Restricted Contract

Linux memfd Seals Turn Mutable File State Into a Restricted Contract A file returned by memfd_create() begins as mutable file state backed by memory-oriented storage, but Linux can progressively remove mutation operations from that file. When creation uses MFD_ALLOW_SEALING, fcntl() with F_ADD_SEALS can prohibit shrinking, growth, writes, future writes, or further changes to the seal set. The resulting restrictions belong to the inode, not to one descriptor, so passing another descriptor for the same object does not restore operations that a seal removed.

Software Engineering 17 Sep 2026 7 min read

Linux memfd Seals Turn Mutable Bytes Into a Kernel-Enforced Contract

A memfd can begin as a writable anonymous file and later reject whole classes of mutation through kernel-enforced seals. The transition is attached to the inode, not to one descriptor, so a process cannot preserve an unrestricted duplicate descriptor and use it to bypass a seal added through another reference. This makes sealing materially different from handing another component a descriptor opened with narrower access. Descriptor access mode constrains one open file description. A seal changes which operations the kernel permits against the file itself, including operations attempted through other descriptors that refer to the same inode.

Software Engineering 17 Sep 2026 5 min read

Linux io_uring Separates Submission From Completion Ownership

Linux io_uring Separates Submission From Completion Ownership An io_uring request can remain in flight after the application has finished constructing its submission queue entry. That creates a lifetime boundary absent from a simple synchronous call: request metadata may become stable at submission, while memory used as the actual I/O payload can still be accessed until the operation completes. The completion queue is therefore not only a result channel. For many operations, it marks the point at which application-owned operation state can be reclaimed or reused.

Software Engineering 17 Sep 2026 6 min read

Linux inotify Events Are a Lossy Change Stream

An inotify file descriptor exposes filesystem activity as an ordered queue of event records, but that queue is not an authoritative history of namespace state. Identical unread events may be coalesced, queue capacity is bounded, and an overflow explicitly means events have been lost. A process that treats the stream as a complete transaction log can therefore preserve a state that no longer matches the filesystem. The interface is better modeled as change notification with recovery obligations. Events can make a cache current incrementally while the stream remains intact; some conditions invalidate that incremental history and require reconciliation against filesystem state.

Software Engineering 17 Sep 2026 5 min read

Linux eventfd Makes Counter State Pollable

A Linux eventfd can collapse many notifications into one kernel-maintained counter while still participating in poll(), select(), and epoll. Writers add unsigned 64-bit values to the counter; readiness reports whether that state can be consumed. The interface carries arithmetic state rather than a byte stream or a queue of individual messages. That distinction matters at process and thread boundaries. A wakeup says that the counter is nonzero. It does not preserve the number of write operations, writer identities, or ordering among independent notification sources.

Software Engineering 17 Sep 2026 6 min read

Linux eventfd Couples Counter State With Descriptor Readiness

An eventfd object combines a kernel-maintained unsigned 64-bit counter with file-descriptor readiness. A write adds to the counter when the addition is permitted; a read consumes counter state. Because the same object participates in poll(), select(), and epoll(), a counter transition can also become an event-loop notification without a byte stream or message framing layer. That compact interface has sharp semantics. Default reads drain the current value to zero, EFD_SEMAPHORE reads consume one unit, writes can block near the counter limit, and readiness indicates which operation can proceed rather than the number of logical events an application may have assigned to the counter.

Software Engineering 17 Sep 2026 6 min read

Linux epoll Edge Triggering Reports Readiness Transitions, Not Work Units

Linux epoll Edge Triggering Reports Readiness Transitions, Not Work Units With EPOLLET, an epoll interest does not behave like a queue containing one event for each byte, packet, connection, or application message. It reports changes in readiness state. Once a file descriptor is ready, additional work can accumulate without producing another edge that an application may rely on. The handler therefore has to consume available work until the nonblocking operation reports that progress would block.

Software Engineering 17 Sep 2026 8 min read

Linux Direct I/O Makes Alignment Part of the File Interface

Opening a regular file with O_DIRECT can make the address of a user-space buffer, the file offset, and the transfer length observable parts of the file interface. A read() or write() that is otherwise valid may fail with EINVAL when one of those values violates the direct-I/O constraints for that file. On some combinations of filesystem and kernel behavior, a misaligned operation can instead use buffered I/O. That boundary is easy to miss because ordinary buffered file I/O largely hides physical transfer geometry. The page cache and filesystem can accept an application buffer at an arbitrary address and mediate the transfer internally. Direct I/O reduces that mediation, so constraints that normally remain below the system-call boundary can become requirements on application memory and request shape.

Software Engineering 17 Sep 2026 8 min read

Linux copy_file_range Separates Copy Semantics From Copy Implementation

Linux copy_file_range Separates Copy Semantics From Copy Implementation A successful copy_file_range() call reports a byte count, not a promise about the physical path those bytes took. Linux can satisfy the request through filesystem-specific acceleration, an in-kernel transfer path, or another implementation permitted by the active filesystem interfaces. The application receives a range-copy operation with defined offset and return-value semantics; it does not receive a guarantee that storage blocks were physically duplicated.

Software Engineering 17 Sep 2026 8 min read

io_uring Shared Rings Make Memory Ordering Part of the ABI

An io_uring queue is shared memory with two independent execution domains changing its state. User space prepares submission entries and advances queue metadata; the kernel consumes those submissions and later publishes completion entries. The ring layout removes a copy boundary, but it also makes memory visibility part of the interface contract. A plain source-level assignment to a queue tail is not sufficient as a portable model of publication. The entry data must become visible before the tail value that makes the entry eligible for consumption. On the completion side, user space must observe the kernel’s publication of a completion before reading fields from that completion. The ordering relation is part of correctness, not merely an optimization detail.

Software Engineering 17 Sep 2026 4 min read

io_uring Multishot Accept Keeps One Request Active Across Connections

A Linux io_uring multishot accept request can produce several completion queue entries from one submission queue entry. The kernel keeps the accept operation active after a successful completion when the CQE carries IORING_CQE_F_MORE, so a server does not need to submit a fresh accept SQE for every connection. This changes the lifetime contract between submission and completion. A normal oneshot request is finished after its CQE. A multishot accept can remain in flight across many accepted connections, and the CQE flags determine whether that request still exists.

Software Engineering 17 Sep 2026 4 min read

inotify Rename Cookies Correlate Move Events Without Making Them Atomic

A Linux rename() observed through inotify can produce two records carrying the same nonzero cookie: IN_MOVED_FROM for the old directory entry and IN_MOVED_TO for the new one. The cookie correlates those records, but it does not turn them into one atomic queue item. That boundary matters for software maintaining a pathname index, synchronizing directory state, or converting filesystem notifications into higher-level change records. A rename is one filesystem operation while its inotify representation can be a pair whose delivery has weaker grouping properties.