splice
Rev.3を表示中。最新版はこちら。
spliceはteeの機能を通常ファイルにも適用したものです。ifd/ofdの1つはpipeでないとエラーです。読み書き位置のoffは、そのfdがファイルIDの時のみで、pipeならNULLでないとエラーです。ifd/ofdがpieなら、機能はteeと同じですが、teeはソース元のバッファは無効とならずifdから読込めますが、spliceは無効となりifdから読込めません。
ifd/ofdがファイルならoffがNULLなら、ファイルID管理下のファイル位置に依存し(読み書き後も更新)、そうでないならoff位置からの読み書きとなり、読み書き後はファイルID管理下のファイル位置は更新されません。
パイプからパイプへのサンプル
#include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { int pifd1[2], pifd2[2]; char buff[10]; pipe(pifd1); pipe(pifd2); write(pifd1[1], "01234", 5); splice(pifd1[0], NULL, pifd2[1], NULL, 5,0); memset(buff, 0, sizeof(buff)); read(pifd2[0], buff, 4); printf("from pifd2:%s\n", buff); printf("reading from pifd1\n"); memset(buff, 0, sizeof(buff)); read(pifd1[0], buff, 4); printf("from pifd1:%s\n", buff); }
[root@localhost c]# ./a.out from pifd2:0123 reading from pifd1 ^C <- spliceによりpifd1のデータは削除され、wait故のCtr C
ファイルからパイプへのサンプル
#include <stdio.h> #include <stdlib.h> #include <string.h> void main(int argc,char **argv) { int pifd2[2]; char buff[10]; int srcfd; loff_t off_in; pipe(pifd2); srcfd =open("babakaka.txt", O_RDONLY); if (argc == 2) { off_in = atoi(argv[1]); splice(srcfd, &off_in, pifd2[1], NULL, 4,0); printf("off_in:%ld\n", off_in); } else { splice(srcfd, NULL, pifd2[1], NULL, 4,0); } memset(buff, 0, sizeof(buff)); read(pifd2[0], buff, 4); printf("from pipe:%s\n", buff); memset(buff, 0, sizeof(buff)); read(srcfd, buff, 4); printf("from file:%s\n", buff); close(srcfd); }
[root@localhost c]# cat babakaka.txt 0123456789
[root@localhost c]# ./a.out from pipe:0123 from file:4567
[root@localhost c]# ./a.out 5 off_in:9 from pipe:5678 from file:0123
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; } off_in/off_outを直接do_splice_from()/do_splice_to()の引数とするのでなく、off = &offset/off = &in->f_posとすることで、ソース元読み書きフィル位置が実装され、off = &offsetなら、実行後引数のoffが更新されin->f_posは更新されません。 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; <- out:引数off_outが書込み位置 } else off = &out->f_pos; <- 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; }
パイプからパイプ、実装はteeと同じですが、*obuf = *ibuf毎にipipe->nrbufs--でipipeのデータを削除します。
static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, struct pipe_inode_info *opipe, size_t len, unsigned int flags) { struct pipe_buffer *ibuf, *obuf; int ret = 0, nbuf; bool input_wakeup = false; retry: ret = ipipe_prep(ipipe, flags); if (ret) return ret; ret = opipe_prep(opipe, flags); if (ret) return ret; pipe_double_lock(ipipe, opipe); do { if (!opipe->readers) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; break; } if (!ipipe->nrbufs && !ipipe->writers) break; if (!ipipe->nrbufs || opipe->nrbufs >= opipe->buffers) { /* Already processed some buffers, break */ if (ret) break; if (flags & SPLICE_F_NONBLOCK) { ret = -EAGAIN; break; } pipe_unlock(ipipe); pipe_unlock(opipe); goto retry; } ibuf = ipipe->bufs + ipipe->curbuf; nbuf = (opipe->curbuf + opipe->nrbufs) & (opipe->buffers - 1); obuf = opipe->bufs + nbuf; if (len >= ibuf->len) { *obuf = *ibuf; ibuf->ops = NULL; opipe->nrbufs++; ipipe->curbuf = (ipipe->curbuf + 1) & (ipipe->buffers - 1); ipipe->nrbufs--; <- 設定したソースpipeバッファを削除 input_wakeup = true; } else { ibuf->ops->get(ipipe, ibuf); *obuf = *ibuf; obuf->flags &= ~PIPE_BUF_FLAG_GIFT; obuf->len = len; opipe->nrbufs++; ibuf->offset += obuf->len; ibuf->len -= obuf->len; } ret += obuf->len; len -= obuf->len; } while (len); pipe_unlock(ipipe); pipe_unlock(opipe); if (ret > 0) wakeup_pipe_readers(opipe); if (input_wakeup) wakeup_pipe_writers(ipipe); return ret; }パイプからファイル
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; } 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 long do_splice_to(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int ret; if (unlikely(!(in->f_mode & FMODE_READ))) return -EBADF; ret = rw_verify_area(READ, in, ppos, len); if (unlikely(ret < 0)) return ret; if (in->f_op && in->f_op->splice_read) splice_read = in->f_op->splice_read; else splice_read = default_file_splice_read; return splice_read(in, ppos, pipe, len, flags); }