splice


パイプはパイプ間のデータバッファを共有しますが、spliceはパイプと他の任意のファイルとのバッファを共有します。従って、1つは必ずパイプでないとエラーとなります。

サンプル1(spliceの実装イメージ)
[root@localhost c]# cat splice_image.c
#include <stdio.h>
#include <fcntl.h>

void    splice_write(int pf)
{
       int     fd;
       char    buff[16];

       fd =open("babakaka1.txt", O_RDWR);
       read(fd, buff, 11);
       write(pf, buff, 11);
       close(fd);
}

void    splice_read(int pf)
{
       int     fd;
       char    buff[16];

       fd =open("babakaka2.txt", O_RDWR | O_CREAT);
       read(pf, buff, 11);
       write(fd, buff, 11);
       close(fd);
}

void    main(int argc,char **argv)
{
      int     p_fd[2];

       pipe(p_fd);

       splice_write(p_fd[1]);
       splice_read(p_fd[0]);

       printf("babakaka1.txt\n");
       system("cat babakaka1.txt"); 
       printf("babakaka2.txt\n");
       system("cat babakaka2.txt");
}
 
[root@localhost c]# ./splice_image.o
babakaka1.txt
1234567890
babakaka2.txt
1234567890
サンプル2(splice サンプル1のread/writeが不要)
[root@localhost c]# cat splice.c
#include <stdio.h>
#include <fcntl.h>

void    main(int argc,char **argv)
{
       int     p_fd[2];
       int     f_fd[2];
 
       f_fd[0] =open("babakaka1.txt", O_RDWR);
       f_fd[1] =open("babakaka2.txt", O_RDWR | O_CREAT);

       pipe(p_fd);
       splice(f_fd[0], NULL, p_fd[1], NULL, 10, 0);
       splice(p_fd[0], NULL, f_fd[1], NULL, 10, 0);

       printf("babakaka1.txt\n");
       system("cat babakaka1.txt");
       printf("babakaka2.txt\n");
       system("cat babakaka2.txt");
}
 
[root@localhost c]# ./splice.o
babakaka1.txt
1234567890
babakaka2.txt
1234567890
実装
パイプからファイルへの書込みならdo_splice_from() [from pipe]、ファイルからパイプへの書込みならdo_splice_to() [to pipe]がコールされます。パイプのバッファに読み書きに掛かるバッファサイズを満たしていなければウエイトし、従ってファイルからパイプへの書込みせず、パイプからファイルへの書込みを行うと、パイプに掛かるデータが設定されるまでウエイトします。
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;
}

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;
               } else
                       off = &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;
}
 
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;
}

ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
                        loff_t *ppos, size_t len, unsigned int flags,
                        splice_actor *actor)
{
       ssize_t ret;
       struct splice_desc sd = {
               .total_len = len,
               .flags = flags,
               .pos = *ppos,
               .u.file = out,
       };

       pipe_lock(pipe);
       ret = __splice_from_pipe(pipe, &sd, actor);
       pipe_unlock(pipe);

       return ret;
}

ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
                          splice_actor *actor)
{
       int ret;

       splice_from_pipe_begin(sd);
       do {
               ret = splice_from_pipe_next(pipe, sd);
               if (ret > 0)
                       ret = splice_from_pipe_feed(pipe, sd, actor);
       } while (ret > 0);
       splice_from_pipe_end(pipe, sd);

       return sd->num_spliced ? sd->num_spliced : ret;
}

int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
                         splice_actor *actor)
{
       int ret;

       while (pipe->nrbufs) {
               struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
               const struct pipe_buf_operations *ops = buf->ops;

               sd->len = buf->len;
               if (sd->len > sd->total_len)
                       sd->len = sd->total_len;

               ret = buf->ops->confirm(pipe, buf);
               if (unlikely(ret)) {
                       if (ret == -ENODATA)
                               ret = 0;
                       return ret;
               }

               ret = actor(pipe, buf, sd);
               if (ret <= 0)
                       return ret;

               buf->offset += ret;
               buf->len -= ret;

               sd->num_spliced += ret;
               sd->len -= ret;
               sd->pos += ret;
               sd->total_len -= ret;

               if (!buf->len) {
                       buf->ops = NULL;
                       ops->release(pipe, buf);
                       pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
                       pipe->nrbufs--;
                       if (pipe->inode)
                               sd->need_wakeup = true;
               }

               if (!sd->total_len)
                       return 0;
       }

       return 1;
}

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 ssize_t kernel_write(struct file *file, const char *buf, size_t count,
                           loff_t pos)
{
       mm_segment_t old_fs;
       ssize_t res;

       old_fs = get_fs();
       set_fs(get_ds());
       res = vfs_write(file, (const char __user *)buf, count, &pos);
       set_fs(old_fs);

       return res;
}

最終更新 2016/10/14 15:37:57 - north
(2014/04/05 20:07:14 作成)


検索

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