pipeバッファ


Rev.4を表示中。最新版はこちら

pipe_inode_info inode->i_pipeのstruct pipe_buffer *bufsに、PIPE_DEF_BUFFERS=16個の配列を設定し、配列毎に1つのpageが設定されており、最大(デフォルト)で4K×16=64Kのサイズとなります。

バッファサイズは、fcntlコマンドで変更することができます。なお、一連のサイズのバッファとして割り当てるのでなく、上記のように単位で割り当てるのは、遅延割り当てと、page単位での読み書き毎で、他のpipeパイプ読み書きで待機してるプロセスを起動を可能とするためです。
nrbufs:書き込み配列数
curbuf:書き込み済み開始位置(読み込み開始位置とも言えます。)
buffers:配列数(デフォルトでPIPE_DEF_BUFFERS。なおfcntlで変更)

struct pipe_inode_info {
       wait_queue_head_t wait;
       unsigned int nrbufs, curbuf, buffers;
       unsigned int readers;
       unsigned int writers;
       unsigned int waiting_writers;
       unsigned int r_counter;
       unsigned int w_counter;
       struct page *tmp_page;
       struct fasync_struct *fasync_readers;
       struct fasync_struct *fasync_writers;
       struct inode *inode;
       struct pipe_buffer *bufs;
};

struct pipe_buffer {
       struct page *page;
       unsigned int offset, len;
       const struct pipe_buf_operations *ops;
       unsigned int flags;
       unsigned long private;
};

#define PIPE_DEF_BUFFERS        16

struct pipe_inode_info * alloc_pipe_info(struct inode *inode)
{
       struct pipe_inode_info *pipe;

       pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
       if (pipe) {
               pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL);
               if (pipe->bufs) {
                       init_waitqueue_head(&pipe->wait);
                       pipe->r_counter = pipe->w_counter = 1;
                       pipe->inode = inode;
                       pipe->buffers = PIPE_DEF_BUFFERS;
                       return pipe;
               }
               kfree(pipe);
       }

       return NULL;
}
pipeのfdで、cmd=F_SETPIPE_SZでfcntlを呼び出すと、パイプのバッファサイズを設定し、F_GETPIPE_SZではpipe->buffers * PAGE_SIZEでバッファサイズを返します。
long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
{
       struct pipe_inode_info *pipe;
       long ret;

       pipe = get_pipe_info(file);
       if (!pipe)
               return -EBADF;

       mutex_lock(&pipe->inode->i_mutex);

       switch (cmd) {
       case F_SETPIPE_SZ: {
               unsigned int size, nr_pages;

               size = round_pipe_size(arg);
               nr_pages = size >> PAGE_SHIFT;

               ret = -EINVAL;
               if (!nr_pages)
                       goto out;

               if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
                       ret = -EPERM;
                       goto out;
               }
               ret = pipe_set_size(pipe, nr_pages);
               break;
               }
       case F_GETPIPE_SZ:
               ret = pipe->buffers * PAGE_SIZE;
               break;
       default:
               ret = -EINVAL;
               break;
       }

out:
       mutex_unlock(&pipe->inode->i_mutex);
       return ret;
}
pipe->nrbufsは使用済み配列数で、それより小さくはできません。pipe->nrbufsが0なら、kcalloc()で割り当てたbufsをpipe->bufs = bufsに設定するだけですが、そうでないと使用済み配列を新規bufsに複写しなければなりません。

リングバッファの処理と同じで、headが読み込み位置、tailが書き込み位置で、if (tail < pipe->buffers)なら、オーバラップしていません。オーバラップしているならtail &= (pipe->buffers - 1)でインデックスを取得し、head分、tail分それぞれをbufsに複写し、pipe->bufs = bufsとします。
static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
{
       struct pipe_buffer *bufs;

       if (nr_pages < pipe->nrbufs)
               return -EBUSY;

       bufs = kcalloc(nr_pages, sizeof(*bufs), GFP_KERNEL | __GFP_NOWARN);
       if (unlikely(!bufs))
               return -ENOMEM;

       if (pipe->nrbufs) {
               unsigned int tail;
               unsigned int head;

               tail = pipe->curbuf + pipe->nrbufs;
               if (tail < pipe->buffers)
                       tail = 0;
               else
                       tail &= (pipe->buffers - 1);

               head = pipe->nrbufs - tail;
               if (head)
                       memcpy(bufs, pipe->bufs + pipe->curbuf, head * sizeof(struct pipe_buffer));
               if (tail)
                       memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
       }

       pipe->curbuf = 0;
       kfree(pipe->bufs);
       pipe->bufs = bufs;
       pipe->buffers = nr_pages;
       return nr_pages * PAGE_SIZE;
}
pipeの読み書きは、通常固定長のサイズで行いますが、サイズに幅があり可変長で処理した方がいい場合、読み込みサイズを取得した後、読み込むといった実装を行う場合、FIONREADでioctlをコールすることで、そのサイズを取得できます。

cmd=FIONREADでpipe_ioctlはioctlからコールされ、pipe->curbufからpipe->nrbufs数分、pipe->bufs[buf].lenを加算することで、書き込み済みバッファサイズを取得します。buf = (buf+1) & (pipe->buffers - 1)はオーバラップの考慮です。
static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
       struct inode *inode = filp->f_path.dentry->d_inode;
       struct pipe_inode_info *pipe;
       int count, buf, nrbufs;

       switch (cmd) {
               case FIONREAD:
                       mutex_lock(&inode->i_mutex);
                       pipe = inode->i_pipe;
                       count = 0;
                       buf = pipe->curbuf;
                       nrbufs = pipe->nrbufs;
                       while (--nrbufs >= 0) {
                               count += pipe->bufs[buf].len;
                               buf = (buf+1) & (pipe->buffers - 1);
                       }
                       mutex_unlock(&inode->i_mutex);

                       return put_user(count, (int __user *)arg);
               default:
                       return -EINVAL;
       }
}

補足

Linux2.6ではstruct pipe_bufferは配列での実装でなく、従ってバッファサイズは4Kです。またpipe_fcntlのバッファサイズを変更することもできません。

追記

pipeサイズは、size < pipe_max_sizeとなりますが、CAP_SYS_RESOURCEを有していれば、pipe_max_sizeを超えての設定が可能です。pipe_max_sizeのデフォルトは1048576で、/proc/sys/fs/pipe-max-sizeです。


[root@localhost ~]# cat /proc/sys/fs/pipe-max-size
1048576

unsigned int pipe_max_size = 1048576;
unsigned int pipe_min_size = PAGE_SIZE;

static struct ctl_table kern_table[] = {
  :
       {
               .procname       = "pipe-max-size",
               .data           = &pipe_max_size,
               .maxlen         = sizeof(int),
               .mode           = 0644,
               .proc_handler   = &pipe_proc_fn,
               .extra1         = &pipe_min_size,
       },
       { }
};


最終更新 2015/11/05 22:43:21 - north
(2014/04/03 23:49:21 作成)


検索

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