simple implementation of linux command cat using io_uring
gcc ./cat_with_io_uring.c -o catrun the program just like regular cat: ./cat <file>
This implementation demonstrates how to use Linux's io_uring interface for asynchronous I/O operations to read and display file contents. The program uses a low-level approach by directly interfacing with the kernel's io_uring system calls.
io_uring is a Linux kernel interface that provides efficient asynchronous I/O operations through a pair of ring buffers:
- Submission Queue (SQ): Where the application submits I/O requests
- Completion Queue (CQ): Where the kernel places completed I/O results
Represents the user-space submission queue ring buffer with pointers to:
head,tail: Ring buffer position indicatorsring_mask,ring_entries: Ring buffer size and masking informationflags: Control flags for the ringarray: Array of submission queue entry indices
Represents the user-space completion queue ring buffer with pointers to:
head,tail: Ring buffer position indicatorsring_mask,ring_entries: Ring buffer size and masking informationcqes: Array of completion queue entries
Main context structure containing:
ring_fd: File descriptor for the io_uring instancesq_ring: Submission queue ring buffersqes: Array of submission queue entriescq_ring: Completion queue ring buffer
Holds file data and metadata:
file_size: Total size of the fileiovecs[]: Array of I/O vectors for scatter-gather operations
Initializes the io_uring interface:
- Calls
io_uring_setup()system call to create io_uring instance - Maps ring buffers into user space using
mmap() - Sets up pointers to ring buffer fields using kernel-provided offsets
- Handles both single-mmap and dual-mmap modes for compatibility
Submits a file read request to the submission queue:
- Opens the file and determines its size
- Allocates memory for file data in 4KB blocks using
aligned_alloc() - Creates I/O vectors (
iovecs) for each block - Prepares a submission queue entry (
io_uring_sqe) with:- File descriptor
- Operation type (
IORING_OP_READVfor vectored read) - Buffer addresses and sizes
- User data pointer for result correlation
- Updates the submission queue tail and submits via
io_uring_enter()
Processes completed I/O operations from the completion queue:
- Reads completion queue entries in order
- Retrieves file data using the user data pointer
- Outputs file contents to stdout block by block
- Updates completion queue head to mark entries as consumed
Simple output function that writes characters to stdout one by one.
- Initialization: Allocate submitter structure and setup io_uring rings
- File Processing: For each command-line argument:
- Submit file read request to submission queue
- Wait for completion and read results from completion queue
- Output file contents to console
- Cleanup: Implicit cleanup when program exits
- Files are read in 4KB blocks (
FILE_BLOCK_SIZE) for efficient I/O - Uses
aligned_alloc()for proper memory alignment required by io_uring - Employs scatter-gather I/O with
iovecstructures for handling large files - Ring buffers are memory-mapped from kernel space for zero-copy operation
The code uses memory barriers (read_barrier() and write_barrier()) implemented as compiler memory barriers to ensure proper ordering of memory operations when accessing shared ring buffers between user and kernel space.