pipeバッファ
Rev.3を表示中。最新版はこちら。
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; } }