AsyncIOについて(その1)

最近のOSにはAsyncIO(AIO)という新しいI/Oの仕組みが導入されているようだ.lighttpdの次期バージョンではAIOを導入することで8割もパフォーマンスが上がったようで非常に興味深い.
またあちこちのBlogを見る限りNonBlockingI/OやNonBlockingI/O+シグナルとAIOが混同されている気がしたので,それら整理してみたい.

はじめに

I/O処理であるシステムコールのread/writeは対象がディスクだったり,ソケットだったりデバイスだったりするわけだが,通常これらのIO処理はCPU処理やメモリ処理に比べ非常に遅いことが知られている.


通常readが行われるとreadが終わるまで,永遠に処理は戻ってこず,プロセス的には待ち状態になってしまう.これは「Blocking」と呼ばれる.


遅いディスクやデータがいつ来るかわからないソケットなどに対するIO処理ではこれだと困るケースがあるので,旧来のUnixにはI/Oに対し,ブロックせずにI/O処理を行う為のオプションが用意されている.
これは「NonBlocking」と呼ばれ,ファイルのI/O処理ならばopenシステムコールのO_NONBLOCKオプションで行うことができる.この状態でIO処理を行うと返値とともに直ちに処理が戻ってくる.これによりプロセスの待ちが発生しなくなるので,他の処理を行えることになる.


ただここで一つ疑問がおきる.NonBlockingで呼び出したreadがデータが読み出せる状態になったことをどうやって知ることができるのだろう?この疑問に対して旧来のUnix技法では2つの方法が用意されている.一つはselect(2)を使ったポーリングを行う方法で,もう一つはシグナルを使った方法だ.

NonBlocking(selectを使った方法)

select(2)はI/Oの多重化を行うための仕組みで,selectを呼び出すことにより,readが可能になったファイルディスクリプタ(以下FD)を取得することができるようになる.

疑似コードは下記のような感じ.

while(まだ読み切ってないFDがある){
  if(readが可能になったFDがあれば){
    readが可能になったfd全部に対して読み込み処理
  }
}


「readが可能になったFDがあれば」を調べるのがselectで,readが終わらないFDがある限りは結果的にこれはビジーループになる.一見これはBlockされたreadの終了を待つのと変わらないように思えるが,一度にたくさんのIOの終了をチェックできるため,1つ非常に時間のかかるreadがあるが為に全体が待たされてしまうということがないなどのメリットがある.

NonBlocking(シグナルを使った場合)

もう一つの旧来の技法はシグナルを使った方法だ.これはFDに対してシグナルハンドラを割り当てる方法で
,これによりFDが読み出せる状態になった時点でシグナルハンドラが起動されるので,selectの時のようなビジーループは発生しない.


ただしシグナルハンドラを割り当てられるFDの種類に制限があったり,シグナルハンドラからどのFDからのシグナルかを取得する方法がないため,selectなどと組み合わせる必要があると思われる(ここはちょっと自信なしなので知ってる人がいたら教えてください).


なおこのシグナルを使った方法は非同期IO(AsyncronousI/O)と呼ばれることもあるようだ.ただ今回説明するAIOとは別物なので注意が必要になる.

(つづく)