tee
Rev.2を表示中。最新版はこちら。
teeコマンドは画面表示をファイルにも書き出す。もので、teeシステムコールが行っています。実装的には共通pipeの内部バッファを2つのファイル出力先に書き出すというもので、画面表示とファイルという2つに限ったものでありません。teeという経路をイメージさせるネーミングから、共通のinodeを2つのfileidに割り当て、共有先fileidが参照しているものと思っていましたが、そうでなく共有元pipeのバッファデータを、共有先pipeのバッファに複写しているに過ぎません。
従って、teeコール後に共有元のpipeに出力しても、そのpipeから読み出す事は可能ですが、共有先のpipeから読み出す事はできません。teeはコールした時点の共有元pipeが有するバッファーデータが、共有先pipeから読み出されるからです。
共有元fdinがFMODE_READ、共有先fdoutがFMODE_WRITEならdo_tee()をコールします。
SYSCALL_DEFINE4(tee, int, fdin, int, fdout, size_t, len, unsigned int, flags)
{
struct file *in;
int error, fput_in;
if (unlikely(!len))
return 0;
error = -EBADF;
in = fget_light(fdin, &fput_in);
if (in) {
if (in->f_mode & FMODE_READ) {
int fput_out;
struct file *out = fget_light(fdout, &fput_out);
if (out) {
if (out->f_mode & FMODE_WRITE)
error = do_tee(in, out, len, flags);
fput_light(out, fput_out);
}
}
fput_light(in, fput_in);
}
return error;
}
ipipe_prep()は共有元pipeをチェックし、opipe_prep()は共有先pipeをチェックします。OKならlink_pipe()で共有します。
static long do_tee(struct file *in, struct file *out, size_t len,
unsigned int flags)
{
struct pipe_inode_info *ipipe = get_pipe_info(in);
struct pipe_inode_info *opipe = get_pipe_info(out);
int ret = -EINVAL;
if (ipipe && opipe && ipipe != opipe) {
ret = ipipe_prep(ipipe, flags);
if (!ret) {
ret = opipe_prep(opipe, flags);
if (!ret)
ret = link_pipe(ipipe, opipe, len, flags);
}
}
return ret;
}
共有元にデータがあればOKです。データがなければデータが書き込まれるまでウエイトします。ただし、書き込みプロセスがウエイトしておらず、PLICE_F_NONBLOCKの時、エラー復帰となります。if (!pipe->writers)なら書き込みとしてこのpipeのinodeを有しているプロセスがいないということで、ウエイトする意味がありません。そのまま復帰します。ただしエラーとならず、共有先には何も書き出されないだけです。
static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
int ret;
if (pipe->nrbufs)
return 0;
ret = 0;
pipe_lock(pipe);
while (!pipe->nrbufs) {
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
if (!pipe->writers)
break;
if (!pipe->waiting_writers) {
if (flags & SPLICE_F_NONBLOCK) {
ret = -EAGAIN;
break;
}
}
pipe_wait(pipe);
}
pipe_unlock(pipe);
return ret;
}
if (pipe->nrbufs < pipe->buffers)で書き込みスペースがあればOKです。なければスペースができるまで、ウエイトします。ただしSPLICE_F_NONBLOCKならエラーで復帰します。if (!pipe->readers)ではいくらウエイトしても、読み込まれることはありません。ipipe_prep()と異なり、このケースではエラーです。
static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
int ret;
if (pipe->nrbufs < pipe->buffers)
return 0;
ret = 0;
pipe_lock(pipe);
while (pipe->nrbufs >= pipe->buffers) {
if (!pipe->readers) {
send_sig(SIGPIPE, current, 0);
ret = -EPIPE;
break;
}
if (flags & SPLICE_F_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
pipe->waiting_writers++;
pipe_wait(pipe);
pipe->waiting_writers--;
}
pipe_unlock(pipe);
return ret;
}
共有元pipeのipipe->nrbufs分、共有先pipeのopipeに設定します。opipe->buffersを超える内容は破棄されます。リングバッファの実装に準拠する内容で、obuf = opipe->bufs + nbufとし、共有元pipeのバッファを共有先pipeのバッファにコピーしています。
static int link_pipe(struct pipe_inode_info *ipipe,
struct pipe_inode_info *opipe,
size_t len, unsigned int flags)
{
struct pipe_buffer *ibuf, *obuf;
int ret = 0, i = 0, nbuf;
pipe_double_lock(ipipe, opipe);
do {
if (!opipe->readers) {
send_sig(SIGPIPE, current, 0);
if (!ret)
ret = -EPIPE;
break;
}
if (i >= ipipe->nrbufs || opipe->nrbufs >= opipe->buffers)
break;
ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (ipipe->buffers-1));
nbuf = (opipe->curbuf + opipe->nrbufs) & (opipe->buffers - 1);
ibuf->ops->get(ipipe, ibuf);
obuf = opipe->bufs + nbuf;
*obuf = *ibuf;
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
if (obuf->len > len)
obuf->len = len;
opipe->nrbufs++;
ret += obuf->len;
len -= obuf->len;
i++;
} while (len);
if (!ret && ipipe->waiting_writers && (flags & SPLICE_F_NONBLOCK))
ret = -EAGAIN;
pipe_unlock(ipipe);
pipe_unlock(opipe);
if (ret > 0)
wakeup_pipe_readers(opipe);
return ret;
}
static inline void get_page(struct page *page)
{
if (unlikely(PageTail(page)))
if (likely(__get_page_tail(page)))
return;
VM_BUG_ON(atomic_read(&page->_count) <= 0);
atomic_inc(&page->_count);
}





