無料Wikiサービス | デモページ
検索

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

sendfile


sendfileは、current->splice_pipeに設定したpipe介して、do_splice_to()でin_fdデータをpipeへ、do_splice_from()でそのpipeからout_fdへ出力します。従ってsendfile()実装はsplice()に準じます。current->splice_pipe設定されたpipeはプロセスがexit()するまで保持されます。

offsetのサイズはlongで、do_sendfile()の最大位置はMAX_NON_LFS( ((1UL<<31) - 1))で、NULLの時はデバイスの最大サイズとなり、O_LARGEFILEのファイル/ブロックデバイス(ブロックデバイスはOKです。)の、offset指定してのMAX_NON_LFSを超えた領域は読書きできません。

サンプル

#include <stdio.h>
#include <fcntl.h>

int     main(int argc, char *argv[])
{
       int     fdr, fdw;
       char    buff[2];
       loff_t off_in = -1;

       fdr = open("babakaka1.txt", O_RDONLY);
       fdw = open("babakaka2.txt", O_CREAT | O_RDWR);

       if (argc != 2) {
               sendfile(fdw, fdr, NULL, 1);
       }
       else {
               off_in = atol(argv[1]);
               sendfile(fdw, fdr, &off_in, 1);
       }

       buff[1] = 0;
       read(fdr, buff, 1);
       printf("babakaka1.txt:%s [off_in:%d]\n", buff, off_in);

       lseek(fdw, 0, SEEK_SET);
       buff[1] = 0;
       read(fdw, buff, 1);
       printf("babakaka2.txt:%s\n", buff);

       close(fdr);
       close(fdw);
}

[root@localhost c]# cat babakaka1.txt
123456789

[root@localhost c]# ./a.out
babakaka1.txt:2 [off_in:-1]
babakaka2.txt:1

[root@localhost c]# ./a.out 0
babakaka1.txt:1 [off_in:1]
babakaka2.txt:1

[root@localhost c]# ./a.out 5
babakaka1.txt:1 [off_in:6]
babakaka2.txt:6

実装

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);
}

static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
                          size_t count, loff_t max)
{
       struct file * in_file, * out_file;
       struct inode * in_inode, * out_inode;
       loff_t pos;
       ssize_t retval;
       int fput_needed_in, fput_needed_out, fl;

       retval = -EBADF;
       in_file = fget_light(in_fd, &fput_needed_in);
       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_file = fget_light(out_fd, &fput_needed_out);
       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:
       fput_light(out_file, fput_needed_out);
fput_in:
       fput_light(in_file, fput_needed_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;
}

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 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);
}

補足

sendfile64だとoffsetはlong longで、offset指定してのデバイスのファイル最大値領域での読書きが可能となります。
SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count)
{
       loff_t pos;
       ssize_t ret;

       if (offset) {
               if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
                       return -EFAULT;
               ret = do_sendfile(out_fd, in_fd, &pos, count, 0);
               if (unlikely(put_user(pos, offset)))
                       return -EFAULT;
               return ret;
       }

       return do_sendfile(out_fd, in_fd, NULL, count, 0);
}

最終更新 2016/06/03 22:31:39 - north
(2016/06/03 22:31:39 作成)