最終更新:ID:ODc61HTzlw 2016年06月12日(日) 16:25:23履歴
ページキャッシュとはディスクキャッシュのこと。
ディスクへの読み書きはページキャッシュを通して行われる。
例えば、ファイルへの読み書きは、ファイルが持つページ(インメモリ)に書いて、幾つかのページをあるタイミングで
いっぺんにディスクに書き出す。
こうすることで、I/Oを効率的に実施する
本体は 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()は address_space構造体と offset を受け取り、該当のページを返す。
もし該当するページがない場合は、新しいページを作成して返す。
フラグを幾つか指定する。
大事なのは動きが特に変わるのは次の幾つか
# | Flag | 意味 |
1 | FGP_LOCK | 見つけたページのロックをとってから返す |
2 | FGP_NOWAIT | FGP_LOCK と一緒に使う。ロックが取れない場合に取れるまで待たない |
3 | FGP_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))
コメントをかく