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_pos
argv[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; }