tee
spliceのpipe間のやり取りは、in_pipeからout_pipeへ転送されたin_pipeバッファのデータは削除されますが、teeはin_pipeからout_pipeへ転送したin_pipeのバッファのデータは削除されません。
サンプル
ipipe.nrbufsはリスト済み(送信)pipe_buffer総数、ipipe.curbufはリストされている先頭pipe_bufferの位置で、ipipe.curbuf + ipipe.nrbufsは書込位置となり、ipipe.nrbufs--とすると最後に送信された送信済みデータの1つを削除します。ipipe.curbuf++とすれば読み込み先頭データの削除となります。
ibuf = ipipe->bufs + ipipe->curbuf : 読込pipe_buffer
obuf = opipe->bufs + opipe->curbuf + opipe->nrbufs: 書込pipe_buffer
ipipe->nrbufs--/ipipe->curbuf++でipipe->curbufのpipe_bufferは削除されます。spliceのサイズがpipe_bufferサイズより小さい時は、ipipeに掛かる変更はなく、pipe_bufferのoffset/lenに掛かる実装となります。
サンプル
[root@localhost c]# cat tee_splice.c #include <stdio.h> #include <stdlib.h> #include <string.h> void splice_pipe() { int pifd1[2], pifd2[2]; char buff[10]; pipe(pifd1); pipe(pifd2); printf("splice\n"); write(pifd1[1], "01234", 5); splice(pifd1[0], NULL, pifd2[1], NULL, 5, 0); memset(buff, 0, sizeof(buff)); read(pifd2[0], buff, 5); printf("read from pifd2:%s\n", buff); memset(buff, 0, sizeof(buff)); read(pifd1[0], buff, 5); printf("read from pifd1:%s\n", buff); close(pifd1[0]); close(pifd1[1]); close(pifd2[0]); close(pifd2[1]); } void tee_pipe() { int pifd1[2], pifd2[2]; char buff[10]; pipe(pifd1); pipe(pifd2); printf("tee\n"); write(pifd1[1], "01234", 5); tee(pifd1[0], pifd2[1], 5,0); memset(buff, 0, sizeof(buff)); read(pifd2[0], buff, 5); printf("read from pifd2:%s\n", buff); memset(buff, 0, sizeof(buff)); read(pifd1[0], buff, 5); printf("read from pifd1:%s\n", buff); close(pifd1[0]); close(pifd1[1]); close(pifd2[0]); close(pifd2[1]); } void main(void) { tee_pipe(); printf("\n"); splice_pipe(); }
[root@localhost c]# ./tee_splice.o tee read from pifd2:01234 read from pifd1:01234 splice read from pifd2:01234 ^Cpipeは送信データがpipe_inode_infoのpipe inodeの同じバッファー領域のシーケンシャルなデータと管理されるのでなく、送信毎のpipe_bufferデータブロック単位で管理されます。
struct pipe_inode_info { wait_queue_head_t wait; unsigned int nrbufs, curbuf, buffers; unsigned int readers; unsigned int writers; unsigned int waiting_writers; unsigned int r_counter; unsigned int w_counter; struct page *tmp_page; struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct inode *inode; struct pipe_buffer *bufs; }; struct pipe_buffer { struct page *page; unsigned int offset, len; const struct pipe_buf_operations *ops; unsigned int flags; unsigned long private; };spliceのpipe時にコールされ、ipipeに送信されているデータは送信毎に設定されたpipe_bufferをipipe.bufsにリスト(配列)されています。
ipipe.nrbufsはリスト済み(送信)pipe_buffer総数、ipipe.curbufはリストされている先頭pipe_bufferの位置で、ipipe.curbuf + ipipe.nrbufsは書込位置となり、ipipe.nrbufs--とすると最後に送信された送信済みデータの1つを削除します。ipipe.curbuf++とすれば読み込み先頭データの削除となります。
ibuf = ipipe->bufs + ipipe->curbuf : 読込pipe_buffer
obuf = opipe->bufs + opipe->curbuf + opipe->nrbufs: 書込pipe_buffer
ipipe->nrbufs--/ipipe->curbuf++でipipe->curbufのpipe_bufferは削除されます。spliceのサイズがpipe_bufferサイズより小さい時は、ipipeに掛かる変更はなく、pipe_bufferのoffset/lenに掛かる実装となります。
static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, struct pipe_inode_info *opipe, size_t len, unsigned int flags) { struct pipe_buffer *ibuf, *obuf; int ret = 0, nbuf; bool input_wakeup = false; retry: ret = ipipe_prep(ipipe, flags); if (ret) return ret; ret = opipe_prep(opipe, flags); if (ret) return ret; pipe_double_lock(ipipe, opipe); do { if (!opipe->readers) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; break; } if (!ipipe->nrbufs && !ipipe->writers) break; if (!ipipe->nrbufs || opipe->nrbufs >= opipe->buffers) { if (ret) break; if (flags & SPLICE_F_NONBLOCK) { ret = -EAGAIN; break; } pipe_unlock(ipipe); pipe_unlock(opipe); goto retry; } ibuf = ipipe->bufs + ipipe->curbuf; nbuf = (opipe->curbuf + opipe->nrbufs) & (opipe->buffers - 1); obuf = opipe->bufs + nbuf; if (len >= ibuf->len) <--- 転送サイズが対象pipe_bufferのサイズ以上なら、ibuf単位での取得 *obuf = *ibuf; ibuf->ops = NULL; opipe->nrbufs++; ipipe->curbuf = (ipipe->curbuf + 1) & (ipipe->buffers - 1); <-- 次入力pipe_buffe位置を更新 ipipe->nrbufs--; <-- 転送したpipe_buffer数(1ケ)削除 input_wakeup = true; } else { <--- 転送サイズが対象pipe_bufferのサイズ以下なら、対象ibufからの取得 ibuf->ops->get(ipipe, ibuf); *obuf = *ibuf; obuf->flags &= ~PIPE_BUF_FLAG_GIFT; obuf->len = len; opipe->nrbufs++; ibuf->offset += obuf->len; ibuf->len -= obuf->len; } ret += obuf->len; len -= obuf->len; } while (len); pipe_unlock(ipipe); pipe_unlock(opipe); if (ret > 0) wakeup_pipe_readers(opipe); if (input_wakeup) wakeup_pipe_writers(ipipe); return ret; }treeからコールされ、spliceと異なり、in/outともpipeでないとエラーです。*obuf = *ibuf / opipe->nrbufs+でopipeに複写し、spliceと異なりipipe->nrbufがデクリメントされに故、ipipeのデータは削除されません。
static int link_pipe(struct pipe_inode_info *ipipe, struct pipe_inode_info *opipe, size_t len, unsigned int flags) { struct pipe_buffer *ibuf, *obuf; int ret = 0, i = 0, nbuf; pipe_double_lock(ipipe, opipe); do { if (!opipe->readers) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; break; } if (i >= ipipe->nrbufs || opipe->nrbufs >= opipe->buffers) break; ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (ipipe->buffers-1)); nbuf = (opipe->curbuf + opipe->nrbufs) & (opipe->buffers - 1); ibuf->ops->get(ipipe, ibuf); obuf = opipe->bufs + nbuf; *obuf = *ibuf; obuf->flags &= ~PIPE_BUF_FLAG_GIFT; if (obuf->len > len) obuf->len = len; opipe->nrbufs++; ret += obuf->len; len -= obuf->len; i++; } while (len); if (!ret && ipipe->waiting_writers && (flags & SPLICE_F_NONBLOCK)) ret = -EAGAIN; pipe_unlock(ipipe); pipe_unlock(opipe); if (ret > 0) wakeup_pipe_readers(opipe); return ret; }