Zero-Copy High-Performance I/O

glibcLinux カーネルなどに多大な貢献をしている Ulrich Drepper の「ホームページのようなもの」(邦題) は何かと面白いものがいっぱいありますが、今日は kevent について調べていて、Drepper 氏のページにたどり着きました。"The Need for Asynchronous, Zero-Copy I/O" がそれです。(スライドはここ)。

ちゃんと要約できていないかもしれないけど、議論されている内容は

  1. POSIX AIO のシグナル通知について
  2. さまざまなオブジェクトのイベントハンドリングについて
  3. ユーザ空間に対する DMA で Zero-Copy を達成するためのページング抑止策について

で、

  1. はシグナル通知というのがどんな場合でも有用とは限らないと語っている。非同期 I/O を実装するライブラリが勝手にシグナルハンドラを登録したら、他のライブラリと競合してしまうかもしれない。POSIX には通知時に勝手にスレッドを作るオプション (SIGEV_THREAD) があるが、これはコスト高い。
  2. は、ファイルディスクリプタが関連付けられているカーネルオブジェクトとそうでない (POSIX メッセージキューなどの) カーネルオブジェクトのイベントハンドリングをどう統一していくか、のアイディアについて。
    1. まずはいろいろなイベントをラップする構造体 (event_data_t) を作る。
    2. イベント通知方法 (イベントチャネル)
      1. netlink (7) で行っているみたいに PF_EVENT というプロトコルファミリーを導入し、SOCK_SEQPACKET でソケットをメッセージバスに仕立ててイベント通知する。
      2. まったく別のイベント通知フレームワークを用意する。必要に応じて fd に変換して poll() できるようにする。
    3. I/O API の対応
      1. 引数にイベントチャネルが指定できるような I/O API のバリアントを作る (ec_recv 等) → なんで ec_read() がないんだ?とか、ネットワーク I/O だけじゃなくてパイプとかにも「あったほうがいいよね」(nice to have) という話になるのでいまいち。
      2. 既存の POSIX Async I/O フレームワークに、ネットワーク I/O 用の API を追加する。これなら aiocb に msghdr を追加するだけでいいし、あとは sigevent で通知先としてシグナルの代わりにイベントチャネルを指定できるようなスキームを追加すればいい。
  3. は、I/O の読み出しオペレーションにおいて、原則的にバッファは呼び出し元が供給するようになっているから、せっかくカーネル空間までは DMA によって転送されてくるのに、またユーザ空間に CPU 力 (りょく) で blit *1 しないといけないのがもったいない、という話。
    1. で、DMA を行うには、DMA 先がページアウトされる事態を防がなきゃいけない。
    2. これには mlock() が使えるけど、特権の必要な操作だ *2。また、mlock() はページ単位にしか行えない。
    3. mmap() のオプションに MAP_DMA を追加し、I/O の状況に応じて自動的にロックするような戦略をカーネルに指示できるようにしたらいいのではなかろうか。
      1. 権限の問題は解決する。
      2. mlock() には fork() したときに領域のロック状態が子プロセスに引き継がれるかどうかが仕様に定義されていないという問題があるが、この挙動に関しても明確になる。
      3. しかし、つねに I/O がペンディングしているような状況で MAP_DMA とマークされた全ブロックをロックしなければならないのはコストになる。(fd と mmap された領域が関連付けられていないから。)v
      4. もちろん、そのようなロック戦略がすべてのアプリケーションのセマンティクスで有効とは限らない。
      5. 物理メモリの断片化が起こるかもしれない。
    4. DMA 用のメモリを確保する専用の API を用意する。
      1. メモリ領域と fd を直接結び付けられるようにする。
      2. ユーザ空間からアクセスするときは、アクセッサを使う。

読んでみて思ったのは、DRM (Direct Rendering Manager の方) で採用しているアドホックなメモリ管理方式も考慮したらいいなあということ。まだ DRM まわりはコードが読みきれてないので、あまりはっきりしたことはいえませんが…。

*1:BitBLT の BLT の発音から。bitblt=BIT-BLock-Transfer

*2:mlock() されまくったら物理メモリが枯渇してしまうのを恐れてだろうか。あくまでプロセス毎だが、RLIMIT_MEMLOCK で制限はできる。