splice -
パイプとの間でデータを継ぎ合わせる
#define _GNU_SOURCE /* feature_test_macros(7) 参照 */
#include <fcntl.h>
ssize_t splice(int fd_in, loff_t *off_in, int fd_out,
loff_t *off_out, size_t len, unsigned int flags);
splice()
は、カーネルアドレス空間とユーザアドレス空間との間のコピーを伴わずに、
2
つのファイルディスクリプタ間でデータの移動を行う。
ファイルディスクリプタ
fd_in
からファイルディスクリプタ
fd_out へ最大
len
バイトを転送する。 2
つのファイルディスクリプタのうち一つは
パイプを参照していなければならない。
fd_in
がパイプを参照している場合、
off_in は NULL
でなければならない。
fd_in
がパイプを参照しておらず、
off_in が NULL の場合、
fd_in
の現在のファイルオフセットから始まるバイトを読み出す。
現在のファイルオフセットは適切に調整される。
fd_in
がパイプを参照しておらず、
off_in が NULL でない場合、
off_in は
fd_in
からのデータ読み出しを開始する先頭オフセットを格納したバッファ
へのポインタでなければならない。この場合、
fd_in
の現在のファイルオフセットは変更されない。
fd_out と
off_out
に関しても同様である。
flags
引き数には、以下の値の
0
個以上をビット毎の論理和の形で指定する。
- SPLICE_F_MOVE
- ページのコピーでなく移動を試みる。
これはカーネルに対するヒントでしかない。
つまり、カーネルがパイプからページを移動できない場合や、
パイプバッファがページ全部を参照していない場合は、
ページのコピーが行われることもある。
このフラグの最初の実装にはバグがあった。そのため、
Linux 2.6.21
以降ではこのフラグの操作はできないようになっている
(ただし、 splice()
コールでこのフラグを指定することは今も認められている)。
将来、正しい実装が行われることだろう。
- SPLICE_F_NONBLOCK
- 入出力時に停止
(block) しない。
このフラグを指定すると、
splice
によるパイプ操作を非停止モード
(nonblocking) で
行おうとするが、その場合でも
splice()
は停止することもある。なぜなら、データのやり取りを行う
ファイルディスクリプタは
( O_NONBLOCK
フラグをセットされていない場合)
停止する可能性があるからである。
- SPLICE_F_MORE
- この後の splice
でさらに転送されるデータがあることを示す。
このフラグは fd_out
がソケットを参照している場合に有用なヒントとなる
( send(2) の MSG_MORE や tcp(7) の
TCP_CORK
の説明も参照)。
- SPLICE_F_GIFT
- splice()
では使用しない。
vmsplice(2) 参照。
返り値¶
成功して完了すると、
splice()
はパイプから出し入れしたバイト数を返す。
返り値 0
はデータの転送が行わなかったことを示す。
この場合、処理を停止
(block)
しても無意味である。
なぜなら、
fd_in
が参照するパイプの書き込み側に接続されている者がいないからである。
エラーの場合、
splice()
は -1 を返し、
errno
にエラーを示す値を設定する。
エラー¶
- EBADF
- ファイルディスクリプタの一方または両方が有効ではない、
もしくは適切な read-write
モードではない。
- EINVAL
- 対象のファイルシステムが
splice
に対応していない、
または対象のファイルが追記モードでオープンされている、
またはディスクリプタのどちらもパイプを参照していない、
または seek
できないデバイスに対してオフセットが指定された。
- ENOMEM
- メモリ不足。
- ESPIPE
- off_in か off_out
のいずれかが NULL
ではないが、対応するファイルディスクリプタが
パイプを参照している。
バージョン¶
splice() システムコールは
Linux 2.6.17
で初めて登場した。
ライブラリによるサポートは
glibc バージョン 2.5
で追加された。
このシステムコールは
Linux 固有である。
3 つのシステムコール (
splice(),
vmsplice(2),
tee(2))
を使うと、ユーザ空間プログラムは任意のカーネルバッファに対する
完全な制御ができる。カーネルバッファは、パイプに使用されているのと
同種のバッファを使ってカーネル内に実装されている。
大まかにいうと、これらのシステムコールは以下の仕事を行う:
- splice()
- バッファから任意のファイルディスクリプタや、その逆方向、
もしくはあるバッファから別のバッファへの、データ移動を行う。
- tee(2)
- あるバッファから別のバッファへのデータ「コピー」を行う。
- vmsplice(2)
- ユーザ空間からバッファへのデータ「コピー」を行う。
ここではコピーの話をしているが、実際のコピーは一般的に回避される。
カーネルは、パイプ・バッファをカーネルメモリのページへのポインタ集合として
実装し、ページへの参照回数を管理することで、これを実現している。
カーネルは、対象となるページを参照する
(出力バッファ用の)
ポインタを
新規に作成することでバッファ内のページの「コピー」を作成し、
そのページの参照回数を増やす。つまり、ポインタだけがコピーされ、
バッファのページはコピーされない。
tee(2) 参照。
関連項目¶
sendfile(2),
tee(2),
vmsplice(2)
この文書について¶
この man ページは Linux
man-pages
プロジェクトのリリース
3.41 の一部
である。プロジェクトの説明とバグ報告に関する情報は
http://www.kernel.org/doc/man-pages/
に書かれている。