lio-uring
A production-ready, safe, and ergonomic Rust interface to Linux's io_uring asynchronous I/O framework.
Features
- Safe API: Minimal unsafe code with clear safety requirements
- Zero overhead: Thin wrapper around liburing with no runtime cost
- Complete: Support for all major io_uring operations
- Production-ready: Comprehensive error handling and documentation
- Advanced features:
- Submission queue polling (SQPOLL)
- IO polling (IOPOLL)
- Linked operations
- Fixed files and buffers
- Batch submissions
- Non-blocking completions
Requirements
- Linux kernel 5.1+ (5.19+ recommended for full feature support)
- liburing is built from source during compilation
Core Concepts
Split API
The io_uring instance is split into two handles:
- SubmissionQueue: For submitting operations to the submission queue
- CompletionQueue: For retrieving completions from the completion queue
This design prevents accidental concurrent access and allows each half to be used from separate threads safely.
Operations
All io_uring operations are defined in the operation module:
use *;
use File;
use AsRawFd;
let = with_capacity?;
let file = create?;
let data = b"Hello, world!";
let write_op = Write ;
unsafe ?;
Safety
Operations contain raw pointers and file descriptors. When submitting, you must ensure:
- All pointers remain valid until the operation completes
- Buffers are not accessed mutably while operations are in flight
- File descriptors remain valid until operations complete
Error Handling
All operations return io::Result with proper error propagation:
let completion = cq.next?;
match completion.result
Advanced Features
Linked Operations
Chain operations to execute sequentially:
use SqeFlags;
// Write followed by fsync - fsync only runs if write succeeds
unsafe ?;
unsafe ?;
sq.submit?;
Batch Submission
Submit multiple operations at once for better performance:
for in operations.iter.enumerate
let submitted = sq.submit?; // Submit all at once
Non-blocking Completions
Check for completions without blocking:
while let Some = cq.try_next?
Fixed Buffers and Files
Pre-register resources for zero-copy operations and improved performance.
Registered Buffers
Register buffers once, then reference them by index for zero-copy I/O:
use IoSlice;
// Prepare your buffers
let mut buffer1 = vec!;
let mut buffer2 = vec!;
// Register them with io_uring (pins them in kernel memory)
let buffers = vec!;
unsafe ?;
// Use ReadFixed/WriteFixed with buf_index to reference registered buffers
let write_op = WriteFixed ;
unsafe ?;
sq.submit?;
// When done, unregister
sq.unregister_buffers?;
Benefits:
- Zero-copy I/O (data doesn't cross user/kernel boundary)
- Buffers are pinned in physical memory (no page faults)
- Reduced CPU overhead
- Significant performance improvement for frequently used buffers
Registered Files
Register file descriptors once, then reference by index:
// Register files
sq.register_files?;
// Use operations with SqeFlags::FIXED_FILE and fd index
let read_op = Read ;
unsafe ?;
// Update specific indices
sq.register_files_update?; // Replace fd at index 1
// Unregister when done
sq.unregister_files?;
Benefits:
- Faster fd lookup (no fd table traversal)
- Reduced per-operation overhead
IO Polling
Enable busy-polling for lowest latency:
use IoUringParams;
let params = default.iopoll;
let = with_params?;
Supported Operations
File I/O
Read,Write,ReadFixed,WriteFixedReadv,Writev(vectored I/O)Openat,Openat2,Close,CloseFixedFsync,Fadvise,Fallocate,FtruncateStatx
Network I/O
Socket,SocketDirectAccept,AcceptMulti(multishot)Connect,Bind,Listen,ShutdownSend,Recv,SendZc,RecvMulti(multishot)Sendmsg,Recvmsg,SendmsgZc
File System
Unlinkat,Renameat,MkdiratLinkat,SymlinkatGetxattr,Setxattr,Fgetxattr,Fsetxattr
Advanced
Nop(no operation, useful for testing)Timeout,TimeoutRemove,LinkTimeoutPollAdd,PollRemove,PollUpdateCancel(cancel pending operation)Splice,Tee(zero-copy data transfer)SyncFileRangeMsgRing(cross-ring communication)Futexoperations (futex wait/wake)ProvideBuffers,RemoveBuffers(buffer rings)
Examples
See the examples/ directory for complete working examples:
basic_io.rs- Basic file I/O operations (open, read, write, fsync, unlink)linked_ops.rs- Linked operations and batch submissiontcp_echo.rs- TCP echo server using socketsregistered_buffers.rs- Zero-copy I/O with registered buffers (ReadFixed/WriteFixed)
Run an example:
Performance Tips
- Batch submissions: Submit multiple operations at once with a single
submit()call - Use fixed buffers/files: Pre-register frequently used resources
- Enable IOPOLL: For lowest latency on fast storage
- Enable SQPOLL: Offload submission to a kernel thread
- Link dependent operations: Reduce round-trips for sequential operations
- Size the ring appropriately: Balance memory usage vs. batching opportunities
Testing
Run the test suite:
Note: Tests require a Linux system with io_uring support (kernel 5.1+).
Contributing
Contributions are welcome! Please ensure:
- All tests pass
- New features include tests
- Public APIs are documented
- Code follows Rust style guidelines
License
MIT License - see LICENSE file for details