検索

アクセス数
最近のコメント
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

sendfile


サンプル

[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 = &param;
    }

   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 = &param;
   }
   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;
}

最終更新 2018/04/29 19:42:43 - north
(2016/06/03 22:31:39 作成)