sendfileシステムコール


sendfileシステムコールは、 ファイルの複写をカーネル内で行うもので、do_sendfile()からdo_splice_direct()/splice_direct_to_actor()をコールし、spliceのpipeによる連結によって実装しています。spliceのサンプルをカーネル内で実装すると言うものです。

cpコマンドは入力ファイルを読み込み(ユーザプロセスのメモリ空間)、それを出力ファイルに書き出します(ユーザプロセスのメモリ空間をカーネルメモリに出力)。以前のカーネルでのsendfile実装による(下記参照)ものと思います。
[root@localhost test]# strace cp hoge1 hoge2
 :
open("hoge1", O_RDONLY|O_LARGEFILE)     = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=13, ...}) = 0
open("hoge2", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4
fstat64(4, {st_mode=S_IFREG|S_ISUID|0350, st_size=0, ...}) = 0
read(3, "hogehogehoge\n", 32768)        = 13
write(4, "hogehogehoge\n", 13)          = 13
read(3, "", 32768)                      = 0
close(4)                                = 0
close(3)                                = 0
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
サンプル
#include <stdio.h>
#include <fcntl.h>

int
main(int argc, char *argv[])
{
       struct  stat    tmp;
       int     fdr, fdw;

       fdr = open("hoge1", O_RDONLY);
       fdw = open("hoge2", O_CREAT | O_WRONLY);

       fstat(fdr, &tmp);
       sendfile(fdw, fdr, NULL, tmp.st_size);

       close(fdr);
       close(fdw);
}

[root@localhost kitamura]# echo hogehogehoge > hoge1
[root@localhost kitamura]# ./a.out
[root@localhost kitamura]# cat hoge2
hogehogehoge
spliceはpipeを介しての実装で、spliceシステムコールの引数の1つはpipeでなければなりませんが、
splice_direct_to_actor()はファイル対ファイルの連結で、従って、splice_direct_to_actor()内で、新規にpipeを取得しcurrent->splice_pipeに設定し、これを介してファイルの読み書きを行います。

do_splice_to()でpipeに読み込んで、actor(){direct_splice_actor()}でpipeに書き出しことでファイルへ書き出します。
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);
}

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

備考

linux 2.6.0では、入力側ファイルのf_op->sendfile()がコールされ、do_generic_mapping_read()で、入力ファイルのfilp->f_dentry->d_inode->i_mappingにファイルデータが読み込まれ、書き出し側ファイルのfile->f_op->sendpage()で、filp->f_dentry->d_inode->i_mappingをpage単位で書き出すことで実現しています。ここでの書き出しはファイルへのそれででなく、ソケット通信への書き出しです。

f_op->sendpage()のコールバック関数は、ファイルオペレーションとして定義されてなく、ソケットオペレーションで定義されるコールバック関数で、ファイルシステムにソケットオペレーションがバインドされるNFSでしかダメだということです。(f_op->sendpage()が定義されていないならエラーとなります。)

検証してませんが、以前はsendfileシステムコールはソケット通信を介するnfsでの機能だったと思われます。


最終更新 2014/07/28 03:26:14 - north
(2014/07/27 18:08:36 作成)


検索

アクセス数
3702869
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。