2012-02-17 9 views
7

writev関数は、入力引数リストI/O writevは内部でどのように機能しますか?

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

入力が(例えば)ファイルに書き込む必要メモリ・バッファのリストであるとして、構造体IOVECの配列を受け取ります。私が知りたいことは次のとおりです。

for (each element in iov) write(element)

iovのすべての要素が別のI/O呼び出しでファイルに書き込まれるように:内部的にこれを行う

writevしていますか?または、writevは、のファイルにすべてを書き込みます。 I/Oコール?規格毎の

答えて

6

いくつかの理由のために、あなたが言及したためのループは、writevの有効な実装ではありません。

  1. ループが発生した場合、次へ進む前に1 IOVを書き終えるために失敗する可能性があります短い書込みですが、これはループをより精巧にすることで回避できます。
  2. パイプのアトミック性に関して、ループの動作が正しくない可能性があります。書き込みの合計長がPIPE_BUFより小さい場合、パイプライトはアトミックである必要がありますが、ループはアトミック性要件を壊します。この問題は、全長が最大でもPIPE_BUFのときに書き込む前に、すべてのiovエントリを単一のバッファに移動すること以外は、回避できません。
  3. ループでブロッキングが発生する可能性があります。ブロッキングなしで部分書き込みを実行するには、単一のwritevコールが必要です。私が知る限り、この問題は一般的なケースでは回避することは不可能です。
  4. 他の理由が考えられていない可能性があります。

私はポイント#3については分かりませんが、読んでいるときは逆の方向にあります。 readをループで呼び出すと、端末に利用可能な(iovの長さより短い)いくつかのデータがあり、それに続けてEOFインジケータがあるとブロックできます。 readvにする必要があります。しかし、Linuxのバグのため、端末上のreadvは実際にはカーネル空間のreadループとして実装されており、このブロッキングバグを示しています。私はMUSLの標準入出力を実装するには、このバグを回避する必要がありました:

http://git.etalabs.net/cgi-bin/gitweb.cgi?p=musl;a=commit;h=2cff36a84f268c09f4c9dc5a1340652c8e298dc0

あなたの質問の最後の部分に答えるために:

それともwritevは、単一のI/O呼び出しでファイルにすべてを書くん?

すべての場合、準拠したwritevの実装は1つのシステムコールになります。 Linux上での実装方法を理解する:通常のファイルやほとんどのデバイスでは、基本的なファイルドライバにはiov-style ioを実装するメソッドがあり、内部ループは一切ありません。しかし、Linux上のターミナルドライバは古くなっており、最新のioメソッドがないため、ターミナルで動作しているときにカーネルはwritev/readvの書き込み/読み取りループにフォールバックします。

+0

「端末で操作しているとき」の最後の行はわかりません。また、正確にはlinux srcのどこでwritevの実装をチェックしますか? – jitihsk

+0

「ターミナルで操作するとき」は、ファイルディスクリプタがターミナルデバイスを参照するときを意味します。ソースのどこにあるかについては、http://lxr.linux.no/#linux+v3.2.6/fs/read_write.c#L809 –

3
Or does writev write everything to file in a single I/O call? 

sys_writevはすべてを1回の呼び出しで書き込もうとしていますが、すべてではありません。それはvfsの実装に依存します.vfsがwritevの実装を与えない場合、kenerlはループ内でvfsのwrite()を呼び出します。 writev/readvの戻り値をチェックして、write()で行ったときのバイト数を確認する方が良いでしょう。

カーネルのfs/read_write.c:do_readv_writevに、writevのコードがあります。

5

コードの仕組みを知るための直接的な方法は、ソースコードです。

http://www.oschina.net/code/explore/glibc-2.9/sysdeps/posix/writev.c

を参照してくださいそれsimplelyのalloca()またはのmalloc()バッファ、そこにすべてのベクトルをコピーして、1回)(書き込みを呼び出します。

それはどのように動作しますか。何も神秘的ではありません。

+0

これはLinuxカーネルのwritevではなくglibc writevです。 – Eloff

+0

そのリンクからmempcpyについて学ぶだけで壮大な勝利です。 – RishiD

関連する問題