- 高性能Webサーバーを作るため、従来は select()、poll()、epoll などさまざまなイベント駆動モデルが使われてきた
- しかし、これらのシステムコールには性能上の限界があり、io_uring が登場して、リクエストをキューに入れてカーネルが非同期に処理する方式が導入された
- kTLS はカーネルがTLS暗号化処理を担当し、sendfile() の利用可能性やハードウェアオフロード など追加の最適化が可能になる
- Descriptorless files の導入により、ファイルディスクリプタを直接渡さずに io_uring に最適化されたアクセス方式を提供する
- Rust、io_uring、kTLS を組み合わせた tarweb オープンソースプロジェクト を通じて、リクエストごとの追加システムコールなしでHTTPSを提供し、安全性とメモリ管理に関する課題も議論している
高性能Webサーバー構造の進化
- 2000年代初頭から大規模Webサーバーへの要求が高まった
- 初期には各リクエストごとに新しいプロセスを生成する方式が一般的だったが、高コストという問題から preforking 手法が登場した
- その後、スレッド の導入や select()、poll() の活用を経て、コンテキストスイッチのコストを減らす方向へ発展した
- ただし、select() や poll() 方式も接続数が増えるほどカーネルに大きな配列を頻繁に渡す必要があるため、スケーラビリティに限界がある
epollの登場
- Linux環境では epoll が導入され、従来方式より効率的な多重接続処理が可能になった
- epoll は変更点(デルタ)だけを処理することで、不要なリソース消費を減らす
- すべてのシステムコールが完全になくなるわけではないが、コストは大幅に下がる
io_uring 概要
- io_uring は各リクエストごとにシステムコールを呼ぶ代わりに、カーネルが非同期に処理できるようリクエストをメモリ上のキューに追加する
- たとえば accept() をキューに入れておけば、カーネルが処理後に完了キューへ結果を返す
- Webサーバーはキューにリクエストを追加し、結果は別のメモリ領域で確認する仕組みで動作する
- ビジーループを避けるため、キューに変化がなければWebサーバーとカーネルの双方が必要なときにだけシステムコールを呼び、省電力効果を得る
- 適切なライブラリを使えば、稼働中のサーバーはリクエスト処理中に追加のシステムコールなしで動作できる
マルチコアとNUMA環境
- 現代CPUのマルチコア環境を踏まえ、コアごとに単一スレッド で実行し、データ構造の共有を最小限にする戦略が有効である
- NUMA環境では各スレッドが自分のローカルノードのメモリにのみアクセスするよう最適化する
- リクエスト分配を完全に均等化するには追加の研究が必要である
メモリ割り当て
- カーネル側とWebサーバー側の両方でメモリ割り当ては依然として必要であり、ユーザー空間での割り当ても最終的にはシステムコールにつながる
- Webサーバー側では接続ごとに固定サイズのメモリブロックをあらかじめ割り当て、断片化や不足の問題を防ぐ
- カーネル側でも接続ごとに入出力バッファが必要であり、ソケットオプションなどで一部調整できる
- メモリ不足が発生すると深刻な障害につながる可能性がある
kTLS(カーネルTLS)紹介
- kTLS は Linuxカーネルで暗号化および復号処理を担う機能である
- ハンドシェイクはアプリケーション側で処理するが、その後はカーネルが平文を扱うようにデータ転送を処理する
- sendfile() が利用可能になり、ユーザー空間とカーネル空間の間のメモリコピーを減らせる
- ネットワークカードが対応していれば、暗号化処理までハードウェアにオフロードできる利点がある
Descriptorless Files
- ユーザー空間からカーネル空間へファイルディスクリプタを直接渡す際に発生するオーバーヘッドを減らすために登場した方式である
- register_files を使って io_uring にのみ有効な別の「整数」ファイル番号を用い、/proc/pid/fd には表示されない
- システムの ulimit 制限は引き続き適用される
tarwebプロジェクト紹介
- tarweb は上記すべての技術を適用した例示的なWebサーバーのオープンソースプロジェクトである
- 単一の tar ファイルの内容を配信する構造で、Rust、io_uring、kTLS など最新の高性能技術が組み合わされている
- 実運用の過程で io_uring と kTLS の互換性問題(setsockopt 未対応など)があり、Pull Request によって一部の問題を解決した
- プロジェクトはまだ未完成段階であり、Rust の rustls ライブラリがハンドシェイク過程でメモリ割り当てを行う場合がある
- 核心は 各リクエストごとに追加システムコールなしでHTTPSサービスが可能 だという点である
ベンチマークと性能測定
- 著者はまだ十分なベンチマークを行っておらず、コード整備後に性能テストを実施する予定である
io_uring と Rust の安全性の問題
- 同期システムコールとは異なり、io_uring では完了イベント以前にメモリバッファが解放されてはならない
- io-uring クレートは Rust のコンパイル時安全性を保証せず、ランタイムチェックも不十分である
- 誤用すると C++ に似た深刻な問題につながる可能性があり、Rust 本来の安全性が弱まる
- pinning と borrow checker を積極的に活用する別の safer-ring クレートが必要である
- この問題はすでにコミュニティで議論されている
参考および追加リンク
- 本内容は 2025-08-22 時点で HackerNews で議論された投稿である
まだコメントはありません。