pipeバッファ数の更新
Rev.3を表示中。最新版はこちら。
バージョンのgccとカーネルマター故の実装fcntl.h fcntl()が定義されているがF_SETPIPE_SZが定義されていない。
linux/fcntl.h F_SETPIPE_SZが定義されているがfcntl()が定義されていない。
サンプル
[root@localhost v.north]# cat set-psize.c#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <linux/fcntl.h> /* #include <fcntl.h> #define F_LINUX_SPECIFIC_BASE 1024 #define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) */ #define PAGE_SIZE 4096 void allbuff_write(int pfd); void main(int argc, char *argv[]) { int i, res, psize, pfd[2]; psize = atoi(argv[1]); pipe2(pfd, O_NONBLOCK); if (!psize) { allbuff_write(pfd[1]); } if (psize > 0) { res = fcntl(pfd[1], F_SETPIPE_SZ, psize); printf(" setsize:%d\n", res); allbuff_write(pfd[1]); } if (psize < 0) { for (i = 0; i < PAGE_SIZE + 1; i++) { write(pfd[1], "1", 1); } res = fcntl(pfd[1], F_SETPIPE_SZ, 1); if (res < 0) { printf(" not update pipe-size because do not write data into new aloc buff\n"); } } } void allbuff_write(int pfd) { int cnt, tcnt=0; while(1) { cnt = write(pfd, "1", 1); if(cnt <= 0) { break; } tcnt += cnt; } printf(" writed size:%d writed page=%d\n", tcnt, tcnt/PAGE_SIZE); }
[root@localhost v.north]# ./set-psize.out 0 <=デフォルトpipeのサイズ/page数 writed size:65536 writed page=16 [root@localhost v.north]# ./set-psize.out 10 <=pipeサイズをPAGE_SIZE単位での更新 setsize:4096 writed size:4096 writed page=1
[root@localhost v.north]# ./set-psize.out 4096 setsize:4096 writed size:4096 writed page=1 [root@localhost v.north]# ./set-psize.out 4097 setsize:8192 writed size:8192 writed page=2
CAP_SYS_RESOURCEでないならpipe_max_size[1048576]を超える更新はできない
[root@localhost v.north]# ./set-psize.out 1048577 setsize:2097152 writed size:2097152 writed page=512 [root@localhost v.north]# ./set-psize.out -1 <=2PAGE_SIZE利用時の1PAGE_SIZEバッファ変更は、バッファデータを複写できない故、変更できない not update pipe-size because do not write data into new aloc buff
カーネル
unsigned int pipe_max_size = 1048576; static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { long err = -EINVAL; switch (cmd) { case F_DUPFD: err = f_dupfd(arg, filp, 0); break; case F_DUPFD_CLOEXEC: err = f_dupfd(arg, filp, O_CLOEXEC); break; case F_GETFD: err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; break; case F_SETFD: err = 0; set_close_on_exec(fd, arg & FD_CLOEXEC); break; case F_GETFL: err = filp->f_flags; break; case F_SETFL: err = setfl(fd, filp, arg); break; case F_GETLK: err = fcntl_getlk(filp, (struct flock __user *) arg); break; case F_SETLK: case F_SETLKW: err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg); break; case F_GETOWN: err = f_getown(filp); force_successful_syscall_return(); break; case F_SETOWN: err = f_setown(filp, arg, 1); break; case F_GETOWN_EX: err = f_getown_ex(filp, arg); break; case F_SETOWN_EX: err = f_setown_ex(filp, arg); break; case F_GETOWNER_UIDS: err = f_getowner_uids(filp, arg); break; case F_GETSIG: err = filp->f_owner.signum; break; case F_SETSIG: if (!valid_signal(arg)) { break; } err = 0; filp->f_owner.signum = arg; break; case F_GETLEASE: err = fcntl_getlease(filp); break; case F_SETLEASE: err = fcntl_setlease(fd, filp, arg); break; case F_NOTIFY: err = fcntl_dirnotify(fd, filp, arg); break; case F_SETPIPE_SZ: case F_GETPIPE_SZ: err = pipe_fcntl(filp, cmd, arg); break; default: break; } return err; } 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; }spipe->buffers ---->バッファの総数
spipe->curbuf ---->読込み開始pageインデックス
spipe->nrbufs ---->読込まれていないpage数
end-position = spipe->curbuf + spipe->nrbufs ---->複写するバッファの終端pageインデックス
if (end-position < pipe->buffers) {
spipe->buffersの終端までの書き込み}
else {
spipe->buffersの終端までの書き込みと 係る残りはspipe->buffersの先頭から書き込み}
書き込み先の新規に取得したpipeバッファはspipe->buffersの先頭でpipe->curbuf = 0となる。
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;}