splice


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

spliceシステムコールは、パイプの入力を別のファイルディスクリプタから、またパイプの出力を別のファイルディスクリプタから行います。入力側/出力側を異なるファイルディスクリプタとすると、入力側から出力側へのファイルの複写が行えます。

cpコマンドはシステムコールとして実装されていません。従って掛かる処理はユーザプロセス間の処理となり、かかるやり取りにユーザ空間を介する必要があるわけですが、下記サンプルでは、入力側がpipeバッファへ、そのpipeバッファを出力側へとすることで、カーネル内でのみで行っていると言う事です

cpコマンドに相当するspliceのサンプルです。
#define _GNU_SOURCE
#include "stdio.h"
#include "fcntl.h"

long    fsize(char* fname);

void main(int argc, char *argv[])
{
   int     pipefd[2];
   int     srcfd, desfd;
   int     size;

   if(argc != 3) {
       printf("err\n");
   }
   else {
       pipe(pipefd);
       srcfd =open(argv[1], O_RDONLY);
       desfd =open(argv[2], O_WRONLY | O_CREAT);

       size = fsize(argv[1]);
       splice(srcfd, NULL, pipefd[1], NULL, size, 0);
       splice(pipefd[0], NULL, desfd, NULL, size, 0);
   }
}

long    fsize(char* fname)
{
   FILE *fp;
   long sz;

   fp = fopen(fname, "rb" );
   if( fp == NULL ){
       return -1;
   }

   fseek( fp, 0, SEEK_END );
   sz = ftell( fp );
   fclose( fp );
   return sz;
}
実行結果:/tmp/hoge1を/tmp/hoge2に複写します。
[root@localhost kitamura]# echo abcdefg > /tmp/hoge1
[root@localhost kitamura]# echo 1234567 > /tmp/hoge2
[root@localhost kitamura]# ./splice.out /tmp/hoge1 /tmp/hoge2
[root@localhost kitamura]# cat /tmp/hoge2
abcdefg
spliceシステムコールはdo_splice()をコールし、以下の条件で各対応する関数がコールされます。pipeを共有してのやり取りということで、in/outは少なくとも1つはpipeでなければなりません。flagsはmanを見るかがり、いくつかあるようですが、通常ファイルとのやり取りのでは、まったく影響ありません。

入力:パイプ  出力:パイプ  splice_pipe_to_pipe()
入力:パイプ  出力:ファイル do_splice_from()
入力:ファイル 出力:パイプ  do_splice_to()
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;

               /* Splicing to self would be fun, but... */
               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;
}
入力:パイプで、出力:ファイルの時で、pipeのバッファデータをファイルに書き出します。
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;
}
pipe->nrbufsはデータを有するpipeバッファの配列数で、actorとなるwrite_pipe_buf()を呼び出します。
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;
}
EXPORT_SYMBOL(splice_from_pipe_feed);
kernel_write()でpipeバッファであるdata + buf->offsetを、サンプルでの通常ファイルに相当するsd->u.file書き出しています。
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;
}

補足

pipeバッファの内部構造故、バッファポインタの取得に込み入っていますが、要はシェルコマンドのパイプの通常ファイル版という感じです。
e-learningを参考にさせてもらいました。

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


検索

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