名前


readv, writev - 複数のバッファへの読み書きを行なう

書式


#include <sys/uio.h>

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

説明


readv() 関数は、ファイルディスクリプタ fd に関連付けられたファイルから、 iovcnt 個のバッファ分のデータを読み込み、 iov で指定されたバッファに格納する ("scatter input";「ばらまき入力」)。

writev() 関数は、 iov で指定されたバッファから最大 iovcnt 個のバッファ分のデータを取り出し、 ファイルディスクリプタ fd に関連付けられたファイルに書き込む ("gather output";「かき集め出力」)。

ポインタ ioviovec 構造体の配列へのポインタである。 iovec 構造体は <sys/uio.h> で以下のように定義されている:

struct iovec { void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ };

readv() 関数は、複数のバッファにデータを読み込む点を除いて read(2) と全く同様の動作を行う。

writev() 関数は、複数のバッファのデータを書き出す点以外は write(2) と全く同様の動作を行う。

バッファは配列の順序で処理される。これは、 readv() が iov[0] が完全に一杯になるまでデータを詰めてから、 iov[0] などに進むことを意味する (データが十分ない場合は、 iov が指すバッファのいずれも一杯にならない)。 同様に、 writev() は iov[0] の内容を全部書き出してから iov[1] などに進む。

readv() と writev() によるデータ転送は atomic に行われる。つまり、 writev() によるデータ書き込みは一つのブロックとして行われ、他のプロセスの write による書き込みと混ざり合うことはない (例外に関しては pipe(7) を参照のこと)。同様に、 readv() はファイルから連続するデータブロックが読み出すことが保証され、 同じファイル記述 (file description; open(2) 参照) を参照するファイルディスクリプタを持つ他のスレッドやプロセスが 実行した read 操作の影響を受けることはない。

返り値


成功した場合、 readv() 関数は読み込んだバイト数を返し、 writev() 関数は書き込んだバイト数を返す。 エラーの場合 -1 を返し、errno を適切に設定する。

エラー


read(2)write(2) と同じエラーが定義されている。 さらに以下のエラーが定義されている:
EINVAL iov_len の合計が ssize_t の範囲をオーバーフローした。もしくは、 ベクタ数 iovcnt が 0 より小さいか許可された最大値よりも大きかった。

準拠


4.4BSD (readv() と writev() 関数は 4.2BSD で最初に現われた)、POSIX.1-2001。 Linux libc5 では iovcnt 引き数の型として size_t を、 これらの関数の返り値として int を使用していた。

注意


Linux での注意

POSIX.1-2001 では、 iov で渡すことができる要素数に上限を設ける実装が認められている。 実装は、 <limits.h>IOV_MAX を定義することや、実行時に sysconf(_SC_IOV_MAX) の返り値経由で、この上限を広告することができる。 Linux では、この仕組みにより広告される上限は 1024 であり、 この値はカーネルでの上限そのものである。 一方で、glibc のラッパー関数は、その関数の内部で呼ばれるカーネル・ システムコールがこの上限を超過して失敗したことを検出すると、 追加の動作をする。 readv() の場合、ラッパー関数は iov で指定された全ての要素を格納できる大きさの一時バッファを割り当て、 read(2) を呼び出す際にそのバッファを渡し、 そのバッファのデータを iov の各要素の iov_base フィールドが指定する場所にコピーしてから、 そのバッファを解放する。 writev() のラッパー関数も、同じように一時バッファを使って write(2) を呼び出す。

バグ


readv() や writev() のようなファイルディスクリプタに対する操作を行う関数と、 標準入出力ライブラリの関数をごちゃまぜにして呼ぶのはお薦めしない。 どんな結果になるかは定義されておらず、 おそらく欲しい結果は得られないだろう。


以下のサンプルコードは writev() の使用方法を示すものである。

char *str0 = "hello "; char *str1 = "world\n"; struct iovec iov[2]; ssize_t nwritten;

iov[0].iov_base = str0; iov[0].iov_len = strlen(str0); iov[1].iov_base = str1; iov[1].iov_len = strlen(str1);

nwritten = writev(STDOUT_FILENO, iov, 2);

関連項目


read(2), write(2)

openSUSE Logo

コンテンツ