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; }