splice
Rev.4を表示中。最新版はこちら。
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);
}






