tee


Rev.5を表示中。最新版はこちら

teeはpipe間の連結でなく、他のpipeからのデータ取得(writeに相当)を行います。従って読込み元にデータが無いならwaitします。teeで書込まれた元データは削除されません。従って両pipeからの読込みが可能となります。pipe間の連結でないため、tee後のpip間の相互やり取りをできません。

サンプル(pifd1に書込んだデータは、pifd1/pifd2から読込めます。)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void    main(void)
{
   int     pifd1[2], pifd2[2];
   char    buff[10];

   pipe(pifd1);
   pipe(pifd2);

   write(pifd1[1], "babakaka", 8);
   tee(pifd1[0], pifd2[1], 8,0);

   memset(buff, 0, sizeof(buff));
   read(pifd1[0], buff, 8);
   printf("pipe1:%s\n", buff);

   memset(buff, 0, sizeof(buff));
   read(pifd2[0], buff, 8);
   printf("pipe2:%s\n", buff);

   write(pifd1[1], "babakaka", 8);
   memset(buff, 0, sizeof(buff));
   printf("pipe read\n");
   read(pifd2[0], buff, 8); 
}

[root@localhost c]# ./a.out
pipe1:babakaka
pipe2:babakaka
pipe read
^C           <- pifd2[0]にデータがなく、read待機中故ctr Cで終了

実装

fdinが読込み元パイプID/fdoutが出力先パイプID
SYSCALL_DEFINE4(tee, int, fdin, int, fdout, size_t, len, unsigned int, flags)
{
       struct file *in;
       int error, fput_in;

       if (unlikely(!len))
               return 0;

       error = -EBADF;
       in = fget_light(fdin, &fput_in);
       if (in) {
               if (in->f_mode & FMODE_READ) {
                       int fput_out;
                       struct file *out = fget_light(fdout, &fput_out);

                       if (out) {
                               if (out->f_mode & FMODE_WRITE)
                                       error = do_tee(in, out, len, flags);
                               fput_light(out, fput_out);
                       }
               }
               fput_light(in, fput_in);
       }

       return error;
}
in/outは異なるpipeでないとエラーです。ipipe_prep()はipipeが読み込みpipeとして、opipe_prep()はopipeが書込みpipeとしての要件をチェックし、OKならlink_pipe()でipipeのデータをopipeに出力します。
static long do_tee(struct file *in, struct file *out, size_t len,
                  unsigned int flags)
{
       struct pipe_inode_info *ipipe = get_pipe_info(in);
       struct pipe_inode_info *opipe = get_pipe_info(out);
       int ret = -EINVAL;

       if (ipipe && opipe && ipipe != opipe) {
               ret = ipipe_prep(ipipe, flags);
               if (!ret) {
                       ret = opipe_prep(opipe, flags);
                       if (!ret)
                               ret = link_pipe(ipipe, opipe, len, flags);
               }
       }

       return ret;
}
読込みパイプにデータが無いなら(pipe->nrbufs=0)書込みされるまでwaitします。
static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
       int ret;

       if (pipe->nrbufs)
               return 0;

       ret = 0;
       pipe_lock(pipe);

       while (!pipe->nrbufs) {
               if (signal_pending(current)) {
                       ret = -ERESTARTSYS;
                       break;
               }
               if (!pipe->writers)
                       break;
               if (!pipe->waiting_writers) {
                       if (flags & SPLICE_F_NONBLOCK) {
                               ret = -EAGAIN;
                               break;
                       }
               }
               pipe_wait(pipe);
       }

       pipe_unlock(pipe);
       return ret;
}
書込みパイプにデータがpipe->buffers書込み済なら、そのデータがに読み込まれpipe->buffersに空きができるまででwaitします。
static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
       int ret;

       if (pipe->nrbufs < pipe->buffers)
               return 0;

       ret = 0;
       pipe_lock(pipe);

       while (pipe->nrbufs >= pipe->buffers) {
               if (!pipe->readers) {
                       send_sig(SIGPIPE, current, 0);
                       ret = -EPIPE;
                       break;
               }
               if (flags & SPLICE_F_NONBLOCK) {
                       ret = -EAGAIN;
                       break;
               }
               if (signal_pending(current)) {
                       ret = -ERESTARTSYS;
                       break;
               }
               pipe->waiting_writers++;
               pipe_wait(pipe);
               pipe->waiting_writers--;
       }

       pipe_unlock(pipe);
       return ret;
}

読込み元パイプデータのipipe->bufsを書込みパイプバッファのopipe->curbufにlenまで複写します。ipipe->bufsが読込みサイズ(len)を有してないならwaitします。

if (ret > 0)はopipeは読込み可能という事で、teeでの処理中に、他プロセスよりopipeパイプ自身がreadした場合を想定しての実装です。
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;
}

補足

本サンプルは新規pipe()で作成したパイプでの処理ですが、teeコマンドのように、シェルからコマンド | サンプルとすれば、|はpipeの実装故、標準入力/出力をtee()の引数とする事で、teeコマンドのようにコマンドの画面表示をファイルにも出力させる機能が実現できます。


最終更新 2016/05/30 12:01:03 - north
(2014/04/07 18:18:21 作成)


検索

アクセス数
3689577
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。