EN JA
AIO(7)
AIO(7) Linux Programmer's Manual AIO(7)

名前

aio - POSIX 非同期 I/O の概要

説明

POSIX 非同期 (AIO) インターフェースを使うと、アプリケーションは、非同期に (つまり、バックグラウンドで) 実行されるI/O 操作を一つ以上発行できるようになる。アプリケーションは I/O 操作の完了の通知方法を選択することができる。選択できる通知方法は、シグナルの配送、スレッドの起動、通知を行わないである。
 
POSIX AIO インターフェースは以下の関数で構成されている。
aio_read(3)
読み出しリクエストをキューに入れる。 read(2) の非同期版である。
aio_write(3)
書き込みリクエストをキューに入れる。 write(2) の非同期版である。
aio_fsync(3)
ファイルディスクリプタに対して行われた I/O 操作の同期 (sync) リクエストをキューに入れる。 fsync(2) や fdatasync(2) の非同期版である。
aio_error(3)
キューに入れられた I/O リクエストのエラー状態を取得する。
aio_return(3)
完了した I/O リクエストの終了ステータスを取得する。
aio_suspend(3)
指定された I/O リクエストの集合 (要素は一つ以上) が完了するまで、呼び出し側の実行を停止 (suspend) する。
aio_cancel(3)
指定されたファイルディスクリプタに関する完了していない I/O リクエストのキャンセルを試みる。
lio_listio(3)
一回の関数呼び出しで複数の I/O リクエストをキューに入れる。

aiocb ("非同期 I/O 制御ブロック (asynchronous I/O control block)") 構造体は、I/O 操作を制御するパラメータを定義する。この型の引き数は上記の全ての関数で使用されている。この構造体は以下の通りである。



#include <aiocb.h>


struct aiocb {
/* The order of these fields is implementation-dependent */


int aio_fildes; /* File descriptor */
off_t aio_offset; /* File offset */
volatile void *aio_buf; /* Location of buffer */
size_t aio_nbytes; /* Length of transfer */
int aio_reqprio; /* Request priority */
struct sigevent aio_sigevent; /* Notification method */
int aio_lio_opcode; /* Operation to be performed;
lio_listio() only */


/* Various implementation-internal fields not shown */
};


/* Operation codes for 'aio_lio_opcode': */


enum { LIO_READ, LIO_WRITE, LIO_NOP };



この構造体のフィールドは以下の通りである。

aio_filedes
I/O 操作の実行対象となるファイルディスクリプタ。
aio_offset
I/O 操作を行うファイルオフセットを示す。
aio_buf
読み出し操作、書き込み操作でデータ転送に使用されるバッファ。
aio_nbytes
aio_buf が指すバッファのサイズ。
aio_reqprio
このフィールドでは、呼び出したスレッドのリアルタイム優先度から減算する値を指定する。この I/O リクエストの実行の優先度を決定するために使用される ( pthread_setschedparam(3) 参照)。指定する値は 0 と sysconf(_SC_AIO_PRIO_DELTA_MAX) が返す値の間でなければならない。このフィールドは、ファイル同期操作では無視される。
aio_sigevent
このフィールドは、非同期 I/O 操作が完了した際に呼び出し側にどのように通知を行うかを指定する構造体である。 aio_sigevent.sigev_notify に指定できる値は、 SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD である。詳細は sigevent(7) を参照。
aio_lio_opcode
実行される操作の種別。 lio_listio(3) でのみ使用される。

上記のリストにある標準の関数に加えて、GNU C ライブラリでは以下に示す POSIX AIO API に対する拡張が提供されている。

aio_init(3)
glibc の POSIX AIO 実装の動作を調整するパラメータを設定する。

エラー

EINVAL
aiocb 構造体の aio_reqprio フィールドが、0 より小さいか、 sysconf(_SC_AIO_PRIO_DELTA_MAX) が返す上限よりも大きかった。

バージョン

POSIX AIO インターフェイスは glibc バージョン 2.1 以降で提供されている。

準拠

POSIX.1-2001, POSIX.1-2008.

注意

使用前に制御ブロックバッファを 0 で埋めるのはよい考えである ( memset(3) 参照)。I/O 操作が実行中の間は、制御ブロックバッファと aio_buf が指すバッファを変更してはならない。I/O 操作が完了するまで、これらのバッファは有効な状態に保たなければならない。
 
同じ aiocb 構造体を使って、同時に複数の非同期の読み出し操作や書き込み操作を行った場合に、どのような結果になるかは未定義である。
 
現在の Linux では、POSIX AIO 実装は glibc によりユーザ空間で提供されている。このため、制限がいくつかあり、最も顕著なものは、I/O 操作を実行する複数のスレッドの管理コストが高く、スケーラビリティに欠けることである。しばらくの間、カーネルのステートマシンによる非同期 I/O の実装の作業が行われているが ( io_submit(2), io_setup(2), io_cancel(2), io_destroy(2), io_getevents(2) 参照)、この実装はまだ POSIX AIO 実装をカーネルシステムコールにより再実装するほど成熟したものてはない。

下記のプログラムは、コマンドライン引き数で指定された名前のファイルをそれぞれオープンし、得られたファイルディスクリプタに対するリクエストを aio_read(3) を使ってキューに入れる。その後、このプログラムはループに入り、定期的に aio_error(3) を使ってまだ実行中の各 I/O 操作を監視する。各 I/O リクエストは、シグナルの配送による完了通知が行われるように設定される。全ての I/O リクエストが完了した後、 aio_return(3) を使ってそれぞれのステータスを取得する。
 
SIGQUIT シグナル (control-\をタイプすると生成できる) を送ると、このプログラムは aio_cancel(3) を使って完了していない各リクエストにキャンセル要求を送る。
 
以下はこのプログラムを実行した際の出力例である。この例では、標準入力に対して 2 つのリクエストを行い、 "abc"と "x"という 2 行の入力を行っている。
 


$ ./a.out /dev/stdin /dev/stdin
opened /dev/stdin on descriptor 3
opened /dev/stdin on descriptor 4
aio_error():
for request 0 (descriptor 3): In progress
for request 1 (descriptor 4): In progress
abc
I/O completion signal received
aio_error():
for request 0 (descriptor 3): I/O succeeded
for request 1 (descriptor 4): In progress
aio_error():
for request 1 (descriptor 4): In progress
x
I/O completion signal received
aio_error():
for request 1 (descriptor 4): I/O succeeded
All I/O requests completed
aio_return():
for request 0 (descriptor 3): 4
for request 1 (descriptor 4): 2

プログラムのソース


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <aio.h>
#include <signal.h>


#define BUF_SIZE 20 /* Size of buffers for read operations */


#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)


#define errMsg(msg) do { perror(msg); } while (0)


struct ioRequest { /* Application-defined structure for tracking
I/O requests */
int reqNum;
int status;
struct aiocb *aiocbp;
};


static volatile sig_atomic_t gotSIGQUIT = 0;
/* On delivery of SIGQUIT, we attempt to
cancel all outstanding I/O requests */


static void /* Handler for SIGQUIT */
quitHandler(int sig)
{
gotSIGQUIT = 1;
}


#define IO_SIGNAL SIGUSR1 /* Signal used to notify I/O completion */


static void /* Handler for I/O completion signal */
aioSigHandler(int sig, siginfo_t *si, void *ucontext)
{
write(STDOUT_FILENO, "I/O completion signal received\n", 31);


/* The corresponding ioRequest structure would be available as
struct ioRequest *ioReq = si->si_value.sival_ptr;
and the file descriptor would then be available via
ioReq->aiocbp->aio_fildes */
}


int
main(int argc, char *argv[])
{
struct ioRequest *ioList;
struct aiocb *aiocbList;
struct sigaction sa;
int s, j;
int numReqs; /* Total number of queued I/O requests */
int openReqs; /* Number of I/O requests still in progress */


if (argc < 2) {
fprintf(stderr, "Usage: %s <pathname> <pathname>...\n",
argv[0]);
exit(EXIT_FAILURE);
}


numReqs = argc - 1;


/* Allocate our arrays */


ioList = calloc(numReqs, sizeof(struct ioRequest));
if (ioList == NULL)
errExit("calloc");


aiocbList = calloc(numReqs, sizeof(struct aiocb));
if (aiocbList == NULL)
errExit("calloc");


/* Establish handlers for SIGQUIT and the I/O completion signal */


sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);


sa.sa_handler = quitHandler;
if (sigaction(SIGQUIT, &sa, NULL) == -1)
errExit("sigaction");


sa.sa_flags = SA_RESTART | SA_SIGINFO;
sa.sa_sigaction = aioSigHandler;
if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
errExit("sigaction");


/* Open each file specified on the command line, and queue
a read request on the resulting file descriptor */


for (j = 0; j < numReqs; j++) {
ioList[j].reqNum = j;
ioList[j].status = EINPROGRESS;
ioList[j].aiocbp = &aiocbList[j];


ioList[j].aiocbp->aio_fildes = open(argv[j + 1], O_RDONLY);
if (ioList[j].aiocbp->aio_fildes == -1)
errExit("open");
printf("opened %s on descriptor %d\n", argv[j + 1],
ioList[j].aiocbp->aio_fildes);


ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
if (ioList[j].aiocbp->aio_buf == NULL)
errExit("malloc");


ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
ioList[j].aiocbp->aio_reqprio = 0;
ioList[j].aiocbp->aio_offset = 0;
ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
&ioList[j];


s = aio_read(ioList[j].aiocbp);
if (s == -1)
errExit("aio_read");
}


openReqs = numReqs;


/* Loop, monitoring status of I/O requests */


while (openReqs > 0) {
sleep(3); /* Delay between each monitoring step */


if (gotSIGQUIT) {


/* On receipt of SIGQUIT, attempt to cancel each of the
outstanding I/O requests, and display status returned
from the cancellation requests */


printf("got SIGQUIT; canceling I/O requests: \n");


for (j = 0; j < numReqs; j++) {
if (ioList[j].status == EINPROGRESS) {
printf(" Request %d on descriptor %d:", j,
ioList[j].aiocbp->aio_fildes);
s = aio_cancel(ioList[j].aiocbp->aio_fildes,
ioList[j].aiocbp);
if (s == AIO_CANCELED)
printf("I/O canceled\n");
else if (s == AIO_NOTCANCELED)
printf("I/O not canceled\n");
else if (s == AIO_ALLDONE)
printf("I/O all done\n");
else
errMsg("aio_cancel");
}
}


gotSIGQUIT = 0;
}


/* Check the status of each I/O request that is still
in progress */


printf("aio_error():\n");
for (j = 0; j < numReqs; j++) {
if (ioList[j].status == EINPROGRESS) {
printf(" for request %d (descriptor %d): ",
j, ioList[j].aiocbp->aio_fildes);
ioList[j].status = aio_error(ioList[j].aiocbp);


switch (ioList[j].status) {
case 0:
printf("I/O succeeded\n");
break;
case EINPROGRESS:
printf("In progress\n");
break;
case ECANCELED:
printf("Canceled\n");
break;
default:
errMsg("aio_error");
break;
}


if (ioList[j].status != EINPROGRESS)
openReqs--;
}
}
}


printf("All I/O requests completed\n");


/* Check status return of all I/O requests */


printf("aio_return():\n");
for (j = 0; j < numReqs; j++) {
ssize_t s;


s = aio_return(ioList[j].aiocbp);
printf(" for request %d (descriptor %d): %ld\n",
j, ioList[j].aiocbp->aio_fildes, (long) s);
}


exit(EXIT_SUCCESS);
}

関連項目

io_cancel(2), io_destroy(2), io_getevents(2), io_setup(2), io_submit(2), aio_cancel(3), aio_error(3), aio_init(3), aio_read(3), aio_return(3), aio_write(3), lio_listio(3)
 
http://www.squid-cache.org/~adrian/Reprint-Pulavarty-OLS2003.pdf

この文書について

この man ページは Linux man-pages プロジェクトのリリース 3.51 の一部である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2012-08-05 Linux