マルチスレッドのプログラミング

非同期入出力

各スレッドの同期入出力で同じ効果を実現できるため、非同期入出力が必要になることはほとんどありません。ただし、スレッドで実現できない非同期入出力機能もあります。

簡単な例は、ストリームとしてテープドライブへ書き込みを行う場合です。この場合、テープに書き込まれている間はテープドライブを停止させないようにし、テープに書き込むデータをストリームとして送っている間は高速にテープを先送りします。

これを行うためにカーネル内のテープドライバは、以前のテープへの書き込み操作が完了したことを知らせる割り込みに応答する時に、待ち行列に入っている書き込み要求を発行する必要があります。

スレッドでは、書き込み順序を保証できません。スレッドの実行される順序が不定だからです。たとえば、テープに対して順番どおり書き込みを行おうとしても不可能です。

非同期入出力操作


#include <sys/asynch.h>

int aioread(int fildes, char *bufp, int bufs, off_t offset,
    int whence, aio_result_t *resultp);

int aiowrite(int filedes, const char *bufp, int bufs,
    off_t offset, int whence, aio_result_t *resultp);

aio_result_t *aiowait(const struct timeval *timeout);
int aiocancel(aio_result_t *resultp);

aioread(3)aiowrite(3) の形式は、pread(2)pwrite(2) の形式にそれぞれ似ています。違いは、引数リストの最後に引数が 1 つ追加されていることです。aioread() または aiowrite() を呼び出すと、入出力操作が開始されます (あるいは、入出力要求が待ち行列に入れられます)。

この呼び出しはブロックされずに復帰し、resultp の指す構造体に終了状態が戻されます。これは aio_result_t 型の項目で、次のフィールドで構成されています。


int aio_return;
int aio_errno;

呼び出しが失敗すると、aio_errno にエラーコードが設定されます。そうでない場合は、このフィールドには操作要求が正常に待ち行列に入れられたことを示す AIO_INPROGRESS が設定されます。

非同期入出力操作の完了は、aiowait(3) で待つことができます。この関数は、最初の aioread(3)、または aiowrite(3) で指定した aio_result_t 構造体へのポインタを返します。

この時点で aio_result_t には、read(2) または write(2) のどちらかが非同期バージョン以外で呼ばれた時と同じ情報が設定されます。この read または write が正常終了した場合、aio_return には読み書きされたバイト数が設定されます。異常終了した場合、aio_return には -1、aio_errno にはエラーコードが設定されます。

aiowait() には timeout 引数があり、呼び出し側の待ち時間を設定できます。ここに NULL ポインタを指定すれば、無期限に待つという意味になります。また、値 0 が設定されている構造体を指すポインタの場合は、まったく待たないという意味になります。

非同期入出力操作を開始し別の処理を行なって aiowait() で操作の完了を待つ、あるいは操作完了時に非同期的に送られてくる SIGIO を利用するという方法もあります。

保留状態の入出力操作を取り消すときは、aiocancel() を使用します。このルーチンを呼び出すときは、取り消そうとする非同期入出力操作の結果を格納するアドレスを引数で指定します。