sendfile
Rev.5を表示中。最新版はこちら。
サンプル
[root@north sendfile]# cat a.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sendfile.h>
void get_fdpos(char* msg, int fd);
void main(int argc, char *argv[])
{
off_t *pos, cnt, param;
int fdr = open("babakaka1", O_RDONLY);
int fdw = open("babakaka2", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXO);
lseek(fdr, atoi(argv[1]), SEEK_SET);
if (!strcmp(argv[2], "NULL")) {
pos = NULL;
}
else {
param = atoi(argv[2]);
pos = ¶m;
}
cnt = atoi(argv[3]);
get_fdpos("current->files->fdt->fd[rfd]->f_pos no sendfile", fdr);
sendfile(fdw, fdr, pos, cnt);
if (pos) {
printf("%-50s%d=%d+%d\n", "sendfile(&pos)", *pos, atoi(argv[2]), cnt);
}
else {
printf("sendfile(pos=NULL)\n");
}
get_fdpos("current->files->fdt->fd[rfd]->f_pos by sendfile", fdr);
close(fdr) ; close(fdw); system("grep -H \"\" babakaka2");
}
void get_fdpos(char* msg, int fd)
{
int fpos = lseek(fd, 0, SEEK_CUR);
printf("%-50s%d\n", msg, fpos);
}
argv[1]:lseek()によるsendfile読込ファイルfile->f_posargv[2]:sendfileのファイル読込位置(NULLなら読込位置はfile->f_pos=argv[1])
argv[3]:sendfileのファイル読込サイズ
sendfileのファイル読込後、argv[2]=NULLなら、file->f_posに係る読込故に、読込後のポジションをfile->f_posに更新する。argv[2]!=NULLならfile->f_posに関わらない読込故に、読込後の位置はfile->f_posを更新せず、引数のargv[2]に設定される。
[root@north sendfile]# cat babakaka1
0123456789
[root@north sendfile]# ./a.out 0 3 5
current->files->fdt->fd[rfd]->f_pos no sendfile 0
sendfile(&pos) 8=3+5
current->files->fdt->fd[rfd]->f_pos by sendfile 0
babakaka2:34567
[root@north sendfile]# ./a.out 5 3 5
current->files->fdt->fd[rfd]->f_pos no sendfile 5
sendfile(&pos) 8=3+5
current->files->fdt->fd[rfd]->f_pos by sendfile 5
babakaka2:34567
[root@north sendfile]# ./a.out 5 2 4
current->files->fdt->fd[rfd]->f_pos no sendfile 5
sendfile(&pos) 6=2+4
current->files->fdt->fd[rfd]->f_pos by sendfile 5
babakaka2:2345
[root@north sendfile]# ./a.out 0 NULL 5
current->files->fdt->fd[rfd]->f_pos no sendfile 0
sendfile(pos=NULL)
current->files->fdt->fd[rfd]->f_pos by sendfile 5
babakaka2:01234
[root@north sendfile]# ./a.out 5 NULL 5
current->files->fdt->fd[rfd]->f_pos no sendfile 5
sendfile(pos=NULL)
current->files->fdt->fd[rfd]->f_pos by sendfile 10
babakaka2:56789
捕捉
読込位置引数の同変数の多重sendfile()は、引数の設定に関わらず、追加書込みでのファイル更新[root@north sendfile]# cat append.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sendfile.h>
void main(int argc, char *argv[])
{
off_t *pos, param;
int fdr = open("babakaka1", O_RDONLY);
int fdw = open("babakaka2", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXO);
if (!strcmp(argv[1], "NULL")) {
pos = NULL;
}
else {
param = 0;
pos = ¶m;
}
int cnt = atoi(argv[2]);
while (1) {
if (sendfile(fdw, fdr, pos, cnt) < cnt) {
break;
}
}
close(fdr) ; close(fdw);
system("grep -H \"\" babakaka2");
}
[root@north sendfile]# cat babakaka1
0123456789
[root@north sendfile]# ./append.out NONULL 1
babakaka2:0123456789
[root@north sendfile]# ./append.out NULL 1
babakaka2:0123456789
カーネル
読込位置引数のoff_t __user *, offsetがNULLなら do_sendfile()はppos = &in.file->f_posで、読込後pposに読込位置が設定され、故にposがNULLなら、in.file->f_posに設定。
SYSCALL_DEFINE4(sendfile, int, out_fd, int, in_fd, off_t __user *, offset, size_t, count)
{
loff_t pos;
off_t off;
ssize_t ret;
if (offset) {
if (unlikely(get_user(off, offset)))
return -EFAULT;
pos = off;
ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS);
if (unlikely(put_user(pos, offset)))
return -EFAULT;
return ret;
}
return do_sendfile(out_fd, in_fd, NULL, count, 0);
}
ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count,
loff_t max)
{
struct fd in, out;
struct inode *in_inode, *out_inode;
loff_t pos;
ssize_t retval;
int fl;
retval = -EBADF;
in = fdget(in_fd);
if (!in.file)
goto out;
if (!(in.file->f_mode & FMODE_READ))
goto fput_in;
retval = -ESPIPE;
if (!ppos)
ppos = &in.file->f_pos;
else
if (!(in.file->f_mode & FMODE_PREAD))
goto fput_in;
retval = rw_verify_area(READ, in.file, ppos, count);
if (retval < 0)
goto fput_in;
count = retval;
retval = -EBADF;
out = fdget(out_fd);
if (!out.file)
goto fput_in;
if (!(out.file->f_mode & FMODE_WRITE))
goto fput_out;
retval = -EINVAL;
in_inode = in.file->f_path.dentry->d_inode;
out_inode = out.file->f_path.dentry->d_inode;
retval = rw_verify_area(WRITE, out.file, &out.file->f_pos, count);
if (retval < 0)
goto fput_out;
count = retval;
if (!max)
max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes);
pos = *ppos;
if (unlikely(pos + count > max)) {
retval = -EOVERFLOW;
if (pos >= max)
goto fput_out;
count = max - pos;
}
fl = 0;
#if 0
if (in.file->f_flags & O_NONBLOCK)
fl = SPLICE_F_NONBLOCK;
#endif
retval = do_splice_direct(in.file, ppos, out.file, count, fl);
if (retval > 0) {
add_rchar(current, retval);
add_wchar(current, retval);
}
inc_syscr(current);
inc_syscw(current);
if (*ppos > max)
retval = -EOVERFLOW;
fput_out:
fdput(out);
fput_in:
fdput(in);
out:
return retval;
}
long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
size_t len, unsigned int flags)
{
struct splice_desc sd = {
.len = len,
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out,
};
long ret;
ret = splice_direct_to_actor(in, &sd, direct_splice_actor);
if (ret > 0)
*ppos = sd.pos;
return ret;
}
struct pipe_inode_info current->splice_pipe=pipe、splice_pipe->bufsを読書き共有バッファとし、ファイル読書き共有位置のfile->f_posでなく、pipe実装に係るpipe属性の読込位置pipe->curbuf 読込サイズpipe->nrbufsの、書き込み位置pipe->curbuf+pipe->nrbufsとする、読書き位置を共有しない実装。故に係るバッファに書き込んだデータのバッファ先頭から読込みファイルに書込む。読込位置:pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1) (バッファ終端を超える位置なら先頭バッファに回する)
書込位置:pipe->curbuf + pipe->nrbufs (読込位置+未読バッファ数が書込み位置、書込みはpipe->nrbufsを更新)
int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
struct pipe_buffer *buf = pipe->bufs + newbuf;
ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
splice_direct_actor *actor)
{
struct pipe_inode_info *pipe;
long ret, bytes;
umode_t i_mode;
size_t len;
int i, flags;
i_mode = in->f_path.dentry->d_inode->i_mode;
if (unlikely(!S_ISREG(i_mode) && !S_ISBLK(i_mode)))
return -EINVAL;
pipe = current->splice_pipe;
if (unlikely(!pipe)) {
pipe = alloc_pipe_info(NULL);
if (!pipe)
return -ENOMEM;
pipe->readers = 1;
current->splice_pipe = pipe;
}
ret = 0;
bytes = 0;
len = sd->total_len;
flags = sd->flags;
sd->flags &= ~SPLICE_F_NONBLOCK;
while (len) {
size_t read_len;
loff_t pos = sd->pos, prev_pos = pos;
ret = do_splice_to(in, &pos, pipe, len, flags);
if (unlikely(ret <= 0))
goto out_release;
read_len = ret;
sd->total_len = read_len;
ret = actor(pipe, sd);
if (unlikely(ret <= 0)) {
sd->pos = prev_pos;
goto out_release;
}
bytes += ret;
len -= ret;
sd->pos = pos;
if (ret < read_len) {
sd->pos = prev_pos + ret;
goto out_release;
}
}
done:
pipe->nrbufs = pipe->curbuf = 0;
file_accessed(in);
return bytes;
out_release:
for (i = 0; i < pipe->buffers; i++) {
struct pipe_buffer *buf = pipe->bufs + i;
if (buf->ops) {
buf->ops->release(pipe, buf);
buf->ops = NULL;
}
}
if (!bytes)
bytes = ret;
goto done;
}
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);
}
static int direct_splice_actor(struct pipe_inode_info *pipe,
struct splice_desc *sd)
{
struct file *file = sd->u.file;
return do_splice_from(pipe, file, &file->f_pos, sd->total_len,
sd->flags);
}
捕捉
ファイルポジションは、read/writeシステムコール下によるカーネルstruct fileに係る実装で、カーネル下のファイル読書きのvfs_read()/vfs_write()実装下では、file->f_posは更新されない。故に更新するにfile->f_pos更新を処理毎の独自追加する。
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct fd f = fdget(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_read(f.file, buf, count, &pos);
file_pos_write(f.file, pos);
fdput(f);
}
return ret;
}
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
struct fd f = fdget(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_write(f.file, buf, count, &pos);
file_pos_write(f.file, pos);
fdput(f);
}
return ret;
}
static inline void file_pos_write(struct file *file, loff_t pos)
{
file->f_pos = pos;
}





