io_uring Async File I/O

Latest version: pecl install openswoole-26.2.0 | composer require openswoole/core:26.2.0

Since: OpenSwoole v26.2.0, Linux 5.1+

Description

OpenSwoole 26.2.0 adds a dedicated io_uring ring for asynchronous file I/O operations. When io_uring is enabled, file operations bypass the traditional thread pool and use Linux's io_uring subsystem for significantly better performance.

Supported File Operations

The following file operations are handled natively by io_uring:

  • open / close
  • read / write
  • fstat
  • fsync
  • unlink
  • rename
  • mkdir / rmdir

Unsupported operations gracefully fall back to the thread pool, so existing code continues to work without changes.

Prerequisites

  1. Linux kernel 5.1 or later
  2. liburing installed
  3. OpenSwoole compiled with --enable-io-uring
# Install liburing
## Debian & Ubuntu
apt-get install -y liburing-dev

## CentOS / RHEL
yum install -y liburing-devel

# Compile OpenSwoole with io_uring support
phpize && ./configure --enable-io-uring --enable-openssl --enable-http2 && make && sudo make install

Example: Async File Read/Write

<?php
use OpenSwoole\Coroutine;

Coroutine::set([
    'reactor_type' => OPENSWOOLE_IO_URING,
    'hook_flags' => OpenSwoole\Runtime::HOOK_ALL,
]);

co::run(function () {
    // These file operations use io_uring under the hood
    $data = "Hello from io_uring async file I/O\n";

    // Async write
    file_put_contents('/tmp/iouring_test.txt', $data);
    echo "File written\n";

    // Async read
    $content = file_get_contents('/tmp/iouring_test.txt');
    echo "File read: $content";

    // Async unlink
    unlink('/tmp/iouring_test.txt');
    echo "File deleted\n";
});

Example: Async File I/O in HTTP Server

<?php
use OpenSwoole\Coroutine;
use OpenSwoole\Http\Server;
use OpenSwoole\Http\Request;
use OpenSwoole\Http\Response;

Coroutine::set([
    'reactor_type' => OPENSWOOLE_IO_URING,
    'hook_flags' => OpenSwoole\Runtime::HOOK_ALL,
]);

$server = new Server("0.0.0.0", 9501);

$server->set([
    'worker_num' => 4,
]);

$server->on("request", function (Request $request, Response $response) {
    $path = $request->get['file'] ?? 'default.json';
    $filePath = '/var/data/' . basename($path);

    if (!file_exists($filePath)) {
        $response->status(404);
        $response->end("File not found\n");
        return;
    }

    // File read is async via io_uring - no blocking
    $content = file_get_contents($filePath);

    $response->header('Content-Type', 'application/json');
    $response->end($content);
});

$server->start();

Example: Coroutine System File APIs with io_uring

<?php
use OpenSwoole\Coroutine;
use OpenSwoole\Coroutine\System;

Coroutine::set([
    'reactor_type' => OPENSWOOLE_IO_URING,
]);

co::run(function () {
    // Use OpenSwoole's built-in async file APIs
    $written = System::writeFile('/tmp/iouring_data.txt', 'async data');
    echo "Bytes written: $written\n";

    $content = System::readFile('/tmp/iouring_data.txt');
    echo "Read: $content\n";

    // Directory operations are also async
    mkdir('/tmp/iouring_test_dir');
    rmdir('/tmp/iouring_test_dir');
});

Notes

  • io_uring async file I/O is independent of the reactor type; it can be used even with the epoll reactor, but using OPENSWOOLE_IO_URING reactor enables io_uring for both event polling and file I/O
  • On kernels older than 5.13, single-shot poll is used instead of multishot poll
  • If an io_uring file operation is not supported, it transparently falls back to the thread pool
  • io_uring provides the biggest performance gains for workloads with heavy file I/O
Last updated on February 28, 2026