splice
Rev.1を表示中。最新版はこちら。
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 abcdefgspliceシステムコールは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;
}






