無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

tee


spliceのpipe間のやり取りは、in_pipeからout_pipeへ転送されたin_pipeバッファのデータは削除されますが、teeはin_pipeからout_pipeへ転送したin_pipeのバッファのデータは削除されません。

サンプル
[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
^C
pipeは送信データが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;
}

補足

データ転送は、データその物を複写するのでなく、*obuf = *ibufでpipe_buffer自身を代入する事で実装されており、データのstruct page *pageを共有する事で実装します。


最終更新 2016/10/18 15:19:49 - north
(2014/04/07 18:18:21 作成)