Help us understand the problem. What is going on with this article?

システム間通信 パイプの実装について調べた

More than 3 years have passed since last update.

Linuxのパイプの実装について調べた。

カーネルバージョン

v4.5.0

パイプ、名前付きパイプとは

VFSのオペレーションで扱うことが出来る、メモリ上の一時ファイルみたいなもの。
pipefsに作られる場合は普通のパイプで、ファイルシステム上に次のoperationを持って作られた場合、名前付きパイプと呼ぶ。要するにinodeがどこから辿れるか、という違いだけで内部の実装はおなじ。

const struct file_operations pipefifo_fops = {
    .open       = fifo_open,
    .llseek     = no_llseek,
    .read_iter  = pipe_read,
    .write_iter = pipe_write,
    .poll       = pipe_poll,
    .unlocked_ioctl = pipe_ioctl,
    .release    = pipe_release,
    .fasync     = pipe_fasync,
};

パイプの作成はpipeシステムコールの延長の、__do_pipe_flags()によって行われる。
inode番号は、get_next_ino()によって確保される。CONFIG_SMPの場合、CPUごとにプールを作ったりしているが、要するにシステム全体でインクリメントしていく数を順番に割り当てているだけである。オーバーフローすると重複するが、パイプはfdでやり取りされるだけなので、問題ないのだろう。

unsigned int get_next_ino(void)
{
    unsigned int *p = &get_cpu_var(last_ino);
    unsigned int res = *p;

#ifdef CONFIG_SMP
    if (unlikely((res & (LAST_INO_BATCH-1)) == 0)) {
        static atomic_t shared_last_ino;
        int next = atomic_add_return(LAST_INO_BATCH, &shared_last_ino);

        res = next - LAST_INO_BATCH;
    }
#endif

    res++;
    /* get_next_ino should not provide a 0 inode number */
    if (unlikely(!res))
        res++;
    *p = res;
    put_cpu_var(last_ino);
    return res;
}
EXPORT_SYMBOL(get_next_ino);

ちなみに、パイプのファイル名を参照すると、pipe:[inode番号]のような表示になる。

juntaki@lab% sleep 100 | cat &
[2] 1822 1823
juntaki@lab% ls -la /proc/1822/fd
total 0
dr-x------ 2 juntaki juntaki  0  4月 10 15:09 .
dr-xr-xr-x 9 juntaki juntaki  0  4月 10 15:09 ..
lrwx------ 1 juntaki juntaki 64  4月 10 15:09 0 -> /dev/pts/3
l-wx------ 1 juntaki juntaki 64  4月 10 15:09 1 -> pipe:[10810]
lrwx------ 1 juntaki juntaki 64  4月 10 15:09 2 -> /dev/pts/3

pipefsは外部から参照されないので、パス名は不要なためである。
pipefs_dname()で生成している。

static char *pipefs_dname(struct dentry *dentry, char *buffer, int buflen)
{
    return dynamic_dname(dentry, buffer, buflen, "pipe:[%lu]",
                d_inode(dentry)->i_ino);
}

パイプバッファ

パイプを書き込んでから別のプロセスが受け取るまでの間、データを置いておくのがパイプバッファである。
pipe_inode_infostruct pipe_buffer *bufsからポイントされる場所にある。サイズはalloc_pipe_info()で初期化され、サイズはPIPE_DEF_BUFFERS * 4KB = 64KBとなる。

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
m3dev
インターネット、最新IT技術を活用し日本・世界の医療を改善することを目指します
https://m3.recruitment.jp/engineer/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away