Linux-4.x が何をしているかをまとめるwiki

ページキャッシュについて


ページキャッシュとはディスクキャッシュのこと。
ディスクへの読み書きはページキャッシュを通して行われる。

例えば、ファイルへの読み書きは、ファイルが持つページ(インメモリ)に書いて、幾つかのページをあるタイミングで
いっぺんにディスクに書き出す。
こうすることで、I/Oを効率的に実施する

adress_space 構造体


は、2.6の頃から対して変わっていないようだ。
詳解Linuxの解説と同じ。

ページの検索


本体は pagecache_get_page()関数

例えば、block_write_begin()なんかでは、次のような感じで呼ばれる。

/*
 * block_write_begin takes care of the basic task of block allocation and
 * bringing partial write blocks uptodate first.
 *
 * The filesystem needs to handle block truncation upon failure.
 */
int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
                unsigned flags, struct page **pagep, get_block_t *get_block)
{
        pgoff_t index = pos >> PAGE_SHIFT;
        struct page *page;
        int status;

        page = grab_cache_page_write_begin(mapping, index, flags); ★ページの検索
        if (!page)
                return -ENOMEM;

        status = __block_write_begin(page, pos, len, get_block);
        if (unlikely(status)) {
                unlock_page(page);
                put_page(page);
                page = NULL;
        }

        *pagep = page;

----------------------------
/*
 * Find or create a page at the given pagecache position. Return the locked
 * page. This function is specifically for buffered writes.
 */
struct page *grab_cache_page_write_begin(struct address_space *mapping,
                                        pgoff_t index, unsigned flags)
{
        struct page *page;
        int fgp_flags = FGP_LOCK|FGP_WRITE|FGP_CREAT; // ページを探す時のフラグ

        if (flags & AOP_FLAG_NOFS)
                fgp_flags |= FGP_NOFS;

        page = pagecache_get_page(mapping, index, fgp_flags,
                        mapping_gfp_mask(mapping)); // 上位から受け取ったマッピングを元にページキャッシュを取得
        if (page)
                wait_for_stable_page(page);

        return page;
}
EXPORT_SYMBOL(grab_cache_page_write_begin);

pagecache_get_page()がページキャッシュ検索の実体。
find_get_page() もこいつを呼び出すだけの実装。
pagecache_get_page()

pagecache_get_page()は address_space構造体と offset を受け取り、該当のページを返す。
もし該当するページがない場合は、新しいページを作成して返す。
フラグを幾つか指定する。
大事なのは動きが特に変わるのは次の幾つか

#Flag意味
1FGP_LOCK見つけたページのロックをとってから返す
2FGP_NOWAITFGP_LOCK と一緒に使う。ロックが取れない場合に取れるまで待たない
3FGP_CREATページが見つからない場合はページを作る

struct page *pagecache_get_page(struct address_space *mapping, pgoff_t offset,
        int fgp_flags, gfp_t gfp_mask)
{
        struct page *page;

repeat:
        page = find_get_entry(mapping, offset); // ★ページ検索の本体
        if (radix_tree_exceptional_entry(page)) // ★ なぞ
                page = NULL;
        if (!page)
                goto no_page;

        if (fgp_flags & FGP_LOCK) { // ★上位が FGP_LOCK を指定して検索してきたら
                if (fgp_flags & FGP_NOWAIT) { // ★でも待たないよと行ってきたら
                        if (!trylock_page(page)) { // ★ロック取れなかったらNULLを返す
                                put_page(page);  // ★その前にページの参照カウンタを下げる
                                return NULL;
                        }
                } else {
                        lock_page(page); // ★ページのロックをとって
                }

                /* Has the page been truncated? */
                if (unlikely(page->mapping != mapping)) {
                        unlock_page(page);
                        put_page(page);
                        goto repeat;
                }
                }
                VM_BUG_ON_PAGE(page->index != offset, page);
        }

        if (page && (fgp_flags & FGP_ACCESSED))
                mark_page_accessed(page); // ★ここでページをLRUにいれたり、キャッシュに突っ込んだりするっぽい。pagevec がよくわからん

no_page: // ★ページが見つからない場合はここまで飛ぶ
        if (!page && (fgp_flags & FGP_CREAT)) { FGP_CREATが指定されてない場合はそのまま NULL を返す。
                int err;
                if ((fgp_flags & FGP_WRITE) && mapping_cap_account_dirty(mapping))
                        gfp_mask |= __GFP_WRITE;
                if (fgp_flags & FGP_NOFS)
                        gfp_mask &= ~__GFP_FS;
                page = __page_cache_alloc(gfp_mask); // ★ここでページを作る。
                if (!page)
                        return NULL;

                if (WARN_ON_ONCE(!(fgp_flags & FGP_LOCK)))
                        fgp_flags |= FGP_LOCK;

                /* Init accessed so avoid atomic mark_page_accessed later */
                if (fgp_flags & FGP_ACCESSED)
                        __SetPageReferenced(page);

                err = add_to_page_cache_lru(page, mapping, offset,
                                gfp_mask & GFP_RECLAIM_MASK); // ★作ったら LRU に入れとく
                if (unlikely(err)) {
                        put_page(page);
                        page = NULL;
                        if (err == -EEXIST)

--------------------
struct page *find_get_entry(struct address_space *mapping, pgoff_t offset)
{
        void **pagep;
        struct page *page;

        rcu_read_lock();
repeat:
        page = NULL;
        pagep = radix_tree_lookup_slot(&mapping->page_tree, offset); // ★ radix_tree のスロットを探してきて?
        if (pagep) {
                page = radix_tree_deref_slot(pagep); // ★ 実際のページを取得
                if (unlikely(!page))

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

どなたでも編集できます