An io_uring request that uses a normal file descriptor still has to resolve that descriptor through the submitting task’s file table. A registered file takes a different path: the ring holds a reference to the open file, and an SQE names a slot in that ring-local table.

That distinction removes repeated descriptor lookup from the request path. It also changes resource lifetime, update semantics, and the meaning of the SQE fd field.

A fixed-file slot is not a process file descriptor

IORING_REGISTER_FILES installs an array of file descriptors into an io_uring instance. Registration resolves each descriptor and retains the corresponding kernel file reference. Later requests can set IOSQE_FIXED_FILE and place the registered-table index in sqe->fd.

The same integer field therefore has two interpretations:

IOSQE_FIXED_FILE clear: sqe->fd is a process file descriptor
IOSQE_FIXED_FILE set:   sqe->fd is a registered-file table index

A slot index is meaningful only in the ring that owns the table. It is not accepted by ordinary system calls such as read(2), write(2), or close(2).

The table can contain sparse entries. An empty slot has no file attached and can later receive one through a registered-file update.

Registration moves work out of the hot request path

Normal descriptor use requires the kernel to translate an integer descriptor into a referenced open file for an operation. That work is usually small, but it remains per-operation work and can become visible in workloads built from very frequent short I/O requests.

Registered files shift that lookup and reference setup toward table registration or table update. The I/O request then addresses an already retained file reference by index.

This optimization does not make storage, network, filesystem, or device latency disappear. Its value is concentrated in workloads where submission-side CPU cost is material relative to the operation itself.

The effect is also distinct from registered buffers. Registered files cache file references; registered buffers establish reusable memory mappings and page references for fixed-buffer operations. A ring can use either mechanism independently or combine them when both costs matter.

File lifetime follows the ring reference

Closing the original process descriptor does not immediately invalidate a file that remains registered. The ring holds its own reference.

Conceptually, registration creates this relationship:

process fd table --registration--> io_uring file table --> open file
       |
       +-- close(fd) removes this process-table reference

The registered slot remains usable until it is replaced, unregistered, or released with the ring. This property can simplify ownership boundaries, but it also means that closing the original descriptor is not sufficient to release the underlying file resource.

Resource accounting must include the ring’s registered table. A service that rotates files or connections while leaving obsolete slots populated can retain resources longer than its process-level descriptor inventory suggests.

Updates separate slot identity from file identity

A registered-file table can be updated without rebuilding the entire ring. An update replaces the file associated with one or more slots.

The slot number remains stable while the referenced file can change:

slot 7 -> file A
update
slot 7 -> file B

That stable numeric namespace is useful for long-lived rings, but concurrent activity imposes a boundary. Requests already using an older registered resource must remain safe while an update installs a replacement. Kernel resource management preserves the old reference until operations that depend on it have finished.

Applications therefore should not treat a successful table update as proof that every earlier operation against that slot has completed. Completion queue entries remain the operation-level source of completion state.

Modern interfaces also support tagged registered resources. Tags allow a completion notification to be associated with resource replacement, which is useful when userspace needs an explicit signal tied to retirement of an old registered entry.

Direct descriptors extend the same table model

Some io_uring operations can create files directly into the registered table. Direct open and direct accept variants avoid first creating a normal process descriptor and then registering it.

A direct descriptor is still a table index, not an entry in the process file-descriptor table. Subsequent SQEs use it with IOSQE_FIXED_FILE.

This matters for connection-heavy servers. A normal accept path creates a descriptor in the shared process table, while a direct accept can place the accepted socket into the ring’s private table. The latter can reduce interaction with a heavily shared descriptor table.

Table capacity becomes an explicit limit. Operations that request automatic allocation of a direct descriptor need a free registered slot; exhaustion can fail even when the process could otherwise allocate another ordinary descriptor. Allocation ranges can reserve part of a registered table for dynamic direct descriptors while leaving other slots for fixed placement.

Ring-local references change sharing boundaries

Registered files belong to an io_uring instance. Threads that submit to the same ring can use the same registered slots, while another ring has a separate table unless resources are explicitly arranged there as well.

This makes the ring a resource namespace in addition to a submission and completion mechanism. Designs with one ring per worker can keep file tables local to each worker. Designs with a shared ring can centralize the table but must coordinate slot allocation and replacement across submitters.

The choice affects contention and ownership more than API syntax. A slot allocator that is safe for a single submitting thread may become a synchronization point when many threads share one ring.

Fixed files are an optimization with management cost

Registered files reduce repeated descriptor-resolution work, but they introduce persistent state. Slots need capacity planning, lifecycle rules, update coordination, and observability.

They are most compelling when a stable or intentionally managed set of files receives enough operations for per-request overhead to matter. They are less attractive when files are touched once, descriptor lookup is negligible beside I/O latency, or table management would add more complexity than the saved CPU work justifies.

The boundary is mechanical rather than universal: normal descriptors resolve through the process file table for requests, while fixed-file SQEs address references already retained by the ring. Performance gains depend on whether removing that repeated work is significant in the actual request path.