writev(2) 複数のバッファへの読み書きを行なう

Other Alias

readv, preadv, pwritev

書式

#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);

ssize_t preadv(int fd, const struct iovec *iov, int iovcnt,
off_t offset);

ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt,
off_t offset);

glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):

preadv(), pwritev(): _BSD_SOURCE

説明

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[1] などに進むことを意味する (データが十分ない場合は、 iov が指すバッファのいずれも一杯にならない)。 同様に、 writev() は iov[0] の内容を全部書き出してから iov[1] などに進む。

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

preadv() と pwritev()

preadv() システムコールは readv() と preadv(2) の機能を 組み合わせたものである。 readv() と同じ処理を実行するが、 4 番目の引き数 offset が追加されており、 この引き数は入力操作を行うファイルオフセットを指定する。

pwritev() システムコールは writev() と pwrite(2) の機能を 組み合わせたものである。 writev() と同じ処理を実行するが、 4 番目の引き数 offset が追加されており、 この引き数は出力操作を行うファイルオフセットを指定する。

これらのシステムコールで、ファイルオフセットは変更されない。 fd が参照するファイルは seek 可能でなければならない。

返り値

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

エラー

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

バージョン

preadv() と pwritev() は Linux 2.6.30 で初めて登場した。 ライブラリによるサポートは glibc 2.10 で追加された。

準拠

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

preadv(), pwritev(): 非標準だが、最近の BSD にも存在する。

注意

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);

この文書について

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