splice
パイプはパイプ間のデータバッファを共有しますが、spliceはパイプと他の任意のファイルとのバッファを共有します。従って、1つは必ずパイプでないとエラーとなります。
サンプル1(spliceの実装イメージ)
パイプからファイルへの書込みならdo_splice_from() [from pipe]、ファイルからパイプへの書込みならdo_splice_to() [to pipe]がコールされます。パイプのバッファに読み書きに掛かるバッファサイズを満たしていなければウエイトし、従ってファイルからパイプへの書込みせず、パイプからファイルへの書込みを行うと、パイプに掛かるデータが設定されるまでウエイトします。
サンプル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; }