この記事は「渡部 Advent Calendar 2025」の8日目の記事です。
io_uringって?
Linux カーネルが提供する 超高速・低オーバーヘッドな非同期 I/O インターフェイス です。2019 年に Linux 5.1 の一部として導入され、従来の epoll、aio、read/write システムコールよりも高速に I/O を処理できます。
io_uring は何を解決するのか
io_uring が提供するのは、次のような「I/O パスの改革」です。
特に「システムコール削減」のインパクトは大きく、CPU の文脈切り替えを減らすことで、極めて低いレイテンシを実現します。
私も過去に色々言及していたりするのでその辺も合わせて見てもらえると嬉しいです。
Pythonでの対応状況
そんなio_uringですがPython におけるのサポート状況は「可能ではあるが、まだ発展途上かつ限定的」というのが現状のようでした。以下に調べた際の内容を記載しておきます。
liburing をラップする Python バインディング/ラッパー
例えば Liburing というプロジェクトがあって、これは C の liburing を Cython + Python バインディング でラップしています。 これを使えば、Python から比較的直接に io_uring の機能を呼び出すことができ、「低レイヤーな非同期 I/O」を試せます。
io_uring を使った Python 用ライブラリ/実験的な async ランタイム
Python 組込みの標準 I/O モデル(たとえば asyncio)は、ファイル I/O に対しては本来の非同期を提供していないため、従来はスレッドプールに頼るのが一般的でした。そこで io_uring を使って「真の非同期ファイル I/O」を Python で実現しようという試みがあり、少数ながら実装・実験があります。たとえ「Exploring io_uring with Python」のようなドキュメントも存在します。
Python を使って、io_uring の基本的なシミュレーションを行う簡単なコードを紹介しています。これは本物の io_uring の完全な実装ではなく、概念を理解するための簡略化されたものです。以下のコードでは、Submission Queue Entry(SQE) と Completion Queue Entry(CQE) の管理を行います。
from dataclasses import dataclass from typing import Any, Optional @dataclass class SQE: code: int data: Any user_data: Any # ユーザーデータ @dataclass class CQE: data: Any user_data: Any # SQE と対応するユーザーデータ class IOUring: def __init__(self, queue_size=32): self.submission_queue = [] self.completion_queue = [] self.queue_size = queue_size def enqueueSQE(self, sqe: SQE): if len(self.submission_queue) < self.queue_size: self.submission_queue.append(sqe) return True return False def enqueueCQE(self, cqe: CQE): self.completion_queue.append(cqe) def dequeueSQE(self) -> Optional[SQE]: if self.submission_queue: return self.submission_queue.pop(0) return None def dequeueCQE(self) -> Optional[CQE]: if self.completion_queue: return self.completion_queue.pop(0) return None def step(self): sqe = self.dequeueSQE() if sqe: # I/O 操作をシミュレート cqe = CQE(data=f"Processed: {sqe.data}", user_data=sqe.user_data) self.enqueueCQE(cqe) return True return False
Pythonはすでにaiofilesがあるじゃん
というのがあります。これで完全に非同期サポートしているかと思ったのですがこれは実はaiofiles 自身のドキュメントにあるように、 aiofiles は “delegating operations to a separate thread pool” を使っており、ローカルファイル I/O をスレッドプールにオフロードする方式です。
つまり、Python のコードからは await f.read() のように await 可能だが、実際には Python 標準の blocking read()/write() を別スレッドで実行しているだけ であり、OS やファイルシステムにおける non‑blocking ファイル I/O(例えば Linux の io_uring/AIO、Windows の Overlapped I/O)とは別物。実際、標準の非同期 I/O ライブラリである asyncio のドキュメントにおいても、「ファイル I/O は基本的に blocking」であり、OS による非同期ファイル I/O が広くサポートされていないために asyncio 自体はファイル用のネイティブ async I/O を提供していない、という注意書きがあります。
→ つまり、aiofiles の “async file I/O” は「Python コードから見た非同期感」= 「メインスレッドをブロックせずにファイル操作を待てる」 という意味であって、 「カーネル↔ユーザ領域の非同期 I/O パス(zero syscall, shared‑memory queue など)」を使った true non‑blocking I/O ではありません。
私もaiofiles使っておけばいいじゃんくらいでずっと思っていたのでちょっとびっくりでした。ちなみにio_uring関連のissueは定期的に作成されてはcloseされているように見えます。
まとめ
現時点ではPytonではio_uringを完全にサポートしているわけではなくサードパーティのバインディングなどを使って使用する必要があります。ファイルIOがブロッキング操作で困るケースはasyncioを使ったアプリケーションだとある気がするので現在はスレッド生成でなんとかなっているのですが真の非同期IOについては今後に期待です。