splice


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

spliceはteeの機能を通常ファイルにも適用したものです。ifd/ofdの1つはpipeでないとエラーです。読み書き位置のoffは、そのfdがファイルIDの時のみで、pipeならNULLでないとエラーです。

ifd/ofdがpieなら、機能はteeと同じですが、teeはソース元のバッファは無効とならずifdから読込めますが、spliceは無効となりifdから読込めません。

ifd/ofdがファイルならoffがNULLなら、ファイルID管理下のファイル位置に依存し(読み書き後も更新)、そうでないならoff位置からの読み書きとなり、読み書き後はファイルID管理下のファイル位置は更新されません。

パイプからパイプへのサンプル

#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], "01234", 5);
       splice(pifd1[0], NULL, pifd2[1], NULL, 5,0);

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

       printf("reading from pifd1\n");
       memset(buff, 0, sizeof(buff));
       read(pifd1[0], buff, 4);
       printf("from pifd1:%s\n", buff);
}

[root@localhost c]# ./a.out
from pifd2:0123
reading from pifd1
^C                     <- spliceによりpifd1のデータは削除され、wait故のCtr C

ファイルからパイプへのサンプル

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

void    main(int argc,char **argv)
{
       int     pifd2[2];
       char    buff[10];
       int     srcfd;
       loff_t off_in;

       pipe(pifd2);

       srcfd =open("babakaka.txt", O_RDONLY);
    if (argc == 2) {
       	off_in = atoi(argv[1]);
        splice(srcfd, &off_in, pifd2[1], NULL, 4,0);
        printf("off_in:%ld\n", off_in);
    }
    else {
        splice(srcfd, NULL, pifd2[1], NULL, 4,0);
    }
    memset(buff, 0, sizeof(buff));
    read(pifd2[0], buff, 4);
    printf("from pipe:%s\n", buff);

    memset(buff, 0, sizeof(buff));
    read(srcfd, buff, 4);
    printf("from file:%s\n", buff);
    close(srcfd);
}

[root@localhost c]# cat babakaka.txt
0123456789

[root@localhost c]# ./a.out
from pipe:0123
from file:4567

[root@localhost c]# ./a.out 5
off_in:9
from pipe:5678
from file:0123

SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
               int, fd_out, loff_t __user *, off_out,
               size_t, len, unsigned int, flags)
{
       long error;
       struct file *in, *out;
       int fput_in, fput_out;

       if (unlikely(!len))
               return 0;

       error = -EBADF;
       in = fget_light(fd_in, &fput_in);
       if (in) {
               if (in->f_mode & FMODE_READ) {
                       out = fget_light(fd_out, &fput_out);
                       if (out) {
                               if (out->f_mode & FMODE_WRITE)
                                       error = do_splice(in, off_in,
                                                         out, off_out,
                                                         len, flags);
                               fput_light(out, fput_out);
                       }
               }

               fput_light(in, fput_in);
       }

       return error;
}
off_in/off_outを直接do_splice_from()/do_splice_to()の引数とするのでなく、off = &offset/off = &in->f_posとすることで、ソース元読み書きフィル位置が実装され、off = &offsetなら、実行後引数のoffが更新されin->f_posは更新されません。
static long do_splice(struct file *in, loff_t __user *off_in,
                     struct file *out, loff_t __user *off_out,
                     size_t len, unsigned int flags)
{
       struct pipe_inode_info *ipipe;
       struct pipe_inode_info *opipe;
       loff_t offset, *off;
       long ret;

       ipipe = get_pipe_info(in);
       opipe = get_pipe_info(out);

       if (ipipe && opipe) {
               if (off_in || off_out)
                       return -ESPIPE;

               if (!(in->f_mode & FMODE_READ))
                       return -EBADF;

               if (!(out->f_mode & FMODE_WRITE))
                       return -EBADF;

               if (ipipe == opipe)
                       return -EINVAL;

               return splice_pipe_to_pipe(ipipe, opipe, len, flags);
       }

       if (ipipe) {
               if (off_in)
                       return -ESPIPE;
               if (off_out) {
                       if (!(out->f_mode & FMODE_PWRITE))
                               return -EINVAL;
                       if (copy_from_user(&offset, off_out, sizeof(loff_t)))
                               return -EFAULT;
                       off = &offset;               <- out:引数off_outが書込み位置
               } else
                       off = &out->f_pos;           <- out:f_posが書込み位置

               ret = do_splice_from(ipipe, out, off, len, flags);

               if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
                       ret = -EFAULT;

               return ret;
       }

       if (opipe) {
               if (off_out)
                       return -ESPIPE;
               if (off_in) {
                       if (!(in->f_mode & FMODE_PREAD))
                               return -EINVAL;
                       if (copy_from_user(&offset, off_in, sizeof(loff_t)))
                               return -EFAULT;
                       off = &offset;
               } else
                       off = &in->f_pos;

               ret = do_splice_to(in, off, opipe, len, flags);

               if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))
                       ret = -EFAULT;

               return ret;
       }

       return -EINVAL;
}
パイプからパイプ、実装はteeと同じですが、*obuf = *ibuf毎にipipe->nrbufs--でipipeのデータを削除します。
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) {
                       /* Already processed some buffers, break */
                       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) {
                       *obuf = *ibuf;
                       ibuf->ops = NULL;
                       opipe->nrbufs++;
                       ipipe->curbuf = (ipipe->curbuf + 1) & (ipipe->buffers - 1);
                       ipipe->nrbufs--;        <- 設定したソースpipeバッファを削除
                       input_wakeup = true;
               } else {
                       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;
}
パイプからファイル
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
                          loff_t *ppos, size_t len, unsigned int flags)
{
       ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
                               loff_t *, size_t, unsigned int);
       int ret;

       if (unlikely(!(out->f_mode & FMODE_WRITE)))
               return -EBADF;

       if (unlikely(out->f_flags & O_APPEND))
               return -EINVAL;

       ret = rw_verify_area(WRITE, out, ppos, len);
       if (unlikely(ret < 0))
               return ret;

       if (out->f_op && out->f_op->splice_write)
               splice_write = out->f_op->splice_write;
       else
               splice_write = default_file_splice_write;

       return splice_write(pipe, out, ppos, len, flags);
}

static ssize_t default_file_splice_write(struct pipe_inode_info *pipe,
                                        struct file *out, loff_t *ppos,
                                        size_t len, unsigned int flags)
{
       ssize_t ret;

       ret = splice_from_pipe(pipe, out, ppos, len, flags, write_pipe_buf);
       if (ret > 0)
               *ppos += ret;

       return ret;
}

static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
                         struct splice_desc *sd)
{
       int ret;
       void *data;

       data = buf->ops->map(pipe, buf, 0);
       ret = kernel_write(sd->u.file, data + buf->offset, sd->len, sd->pos);
      buf->ops->unmap(pipe, buf, data);

       return ret;
}
ファイルからパイプ
static long do_splice_to(struct file *in, loff_t *ppos,
                        struct pipe_inode_info *pipe, size_t len,
                        unsigned int flags)
{
       ssize_t (*splice_read)(struct file *, loff_t *,
                              struct pipe_inode_info *, size_t, unsigned int);
       int ret;

       if (unlikely(!(in->f_mode & FMODE_READ)))
               return -EBADF;

       ret = rw_verify_area(READ, in, ppos, len);
       if (unlikely(ret < 0))
               return ret;

       if (in->f_op && in->f_op->splice_read)
               splice_read = in->f_op->splice_read;
       else
               splice_read = default_file_splice_read;

       return splice_read(in, ppos, pipe, len, flags);
}

備考

spliceはファイル間データやり取りの効率化と言うのでなく、シェル下のパイプを介してのコマンド間やり取りで,pipeデータをread/writeする事なく、pipeバッファを直接読み込み/書き込みさせる故での実装かと思います。(この点においては効率化とも言えますが、むしろユーザ空間でread/writeさせる処理が必要ない。実装上の故かと)

最終更新 2016/06/02 13:01:27 - north
(2014/04/05 20:07:14 作成)


検索

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