Linux的Cache和Buffer理解( 三 )


/* => 清除缓存页(除了脏页、上锁的、正在回写的或映射在页表中的)*/unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end){ struct pagevec pvec; pgoff_t index = start; unsigned long ret; unsigned long count = 0; int i;/* * Note: this function may get called on a shmem/tmpfs mapping: * pagevec_lookup() might then return 0 prematurely (because it * got a gangful of swap entries); but it's hardly worth worrying * about - it can rarely have anything to free from such a mapping * (most pages are dirty), and already skips over any difficulties. */pagevec_init(&pvec, 0); while (index <= end && pagevec_lookup(&pvec, mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i];/* We rely upon deletion not changing page->index */ index = page->index; if (index > end) break;if (!trylock_page(page)) continue; WARN_ON(page->index != index); /* => 无效一个文件的缓存 */ ret = invalidate_inode_page(page); unlock_page(page); /* * Invalidation is a hint that the page is no longer * of interest and try to speed up its reclaim. */ if (!ret) deactivate_page(page); count += ret; } pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); index++; } return count;} /* * Safely invalidate one page from its pagecache mapping. * It only drops clean, unused pages. The page must be locked. * * Returns 1 if the page is successfully invalidated, otherwise 0. *//* => 无效一个文件的缓存 */int invalidate_inode_page(struct page *page){ struct address_space *mapping = page_mapping(page); if (!mapping) return 0; /* => 若当前页是脏页或正在写回的页 , 直接返回 */ if (PageDirty(page) || PageWriteback(page)) return 0; /* => 若已经被映射到页表了 , 则直接返回 */ if (page_mapped(page)) return 0; /* => 如果满足了以上条件就调用invalidate_complete_page继续 */ return invalidate_complete_page(mapping, page);}从上面的代码可以看到清除相关的页面要满足二个条件: 1. 不脏且没在回写; 2. 未被使用 。如果满足了这二个条件就调用invalidate_complete_page继续:/* => 无效一个完整的页 */static intinvalidate_complete_page(struct address_space *mapping, struct page *page){ int ret;if (page->mapping != mapping) return 0;if (page_has_private(page) && !try_to_release_page(page, 0)) return 0;/* => 若满足以上更多条件 , 则从地址空间中解除该页 */ ret = remove_mapping(mapping, page);return ret;} /* * Attempt to detach a locked page from its ->mapping. If it is dirty or if * someone else has a ref on the page, abort and return 0. If it was * successfully detached, return 1. Assumes the caller has a single ref on * this page. *//* => 从地址空间中解除该页 */int remove_mapping(struct address_space *mapping, struct page *page){ if (__remove_mapping(mapping, page)) { /* * Unfreezing the refcount with 1 rather than 2 effectively * drops the pagecache ref for us without requiring another * atomic operation. */ page_unfreeze_refs(page, 1); return 1; } return 0;} /* * Same as remove_mapping, but if the page is removed from the mapping, it * gets returned with a refcount of 0. *//* => 从地址空间中解除该页 */static int __remove_mapping(struct address_space *mapping, struct page *page){ BUG_ON(!PageLocked(page)); BUG_ON(mapping != page_mapping(page));spin_lock_irq(&mapping->tree_lock); /* * The non racy check for a busy page. * * Must be careful with the order of the tests. When someone has * a ref to the page, it may be possible that they dirty it then * drop the reference. So if PageDirty is tested before page_count * here, then the following race may occur: * * get_user_pages(&page); * [user mapping goes away] * write_to(page); * !PageDirty(page) [good] * SetPageDirty(page); * put_page(page); * !page_count(page) [good, discard it] * * [oops, our write_to data is lost] * * Reversing the order of the tests ensures such a situation cannot * escape unnoticed. The smp_rmb is needed to ensure the page->flags * load is not satisfied before that of page->_count. * * Note that if SetPageDirty is always performed via set_page_dirty, * and thus under tree_lock, then this ordering is not required. */ if (!page_freeze_refs(page, 2)) goto cannot_free; /* note: atomic_cmpxchg in page_freeze_refs provides the smp_rmb */ if (unlikely(PageDirty(page))) { page_unfreeze_refs(page, 2); goto cannot_free; }if (PageSwapCache(page)) { swp_entry_t swap = { .val = page_private(page) }; __delete_from_swap_cache(page); spin_unlock_irq(&mapping->tree_lock); swapcache_free(swap, page); } else { void (*freepage)(struct page *);freepage = mapping->a_ops->freepage;/* => 从页缓存中删除和释放该页 */ __delete_from_page_cache(page); spin_unlock_irq(&mapping->tree_lock); mem_cgroup_uncharge_cache_page(page);if (freepage != NULL) freepage(page); }return 1; cannot_free: spin_unlock_irq(&mapping->tree_lock); return 0;} /* * Delete a page from the page cache and free it. Caller has to make * sure the page is locked and that nobody else uses it - or that usage * is safe. The caller must hold the mapping's tree_lock. *//* => 从页缓存中删除和释放该页 */void __delete_from_page_cache(struct page *page){ struct address_space *mapping = page->mapping;trace_mm_filemap_delete_from_page_cache(page); /* * if we're uptodate, flush out into the cleancache, otherwise * invalidate any existing cleancache entries. We can't leave * stale data around in the cleancache once our page is gone */ if (PageUptodate(page) && PageMappedToDisk(page)) cleancache_put_page(page); else cleancache_invalidate_page(mapping, page);radix_tree_delete(&mapping->page_tree, page->index); /* => 解除与之绑定的地址空间结构 */ page->mapping = NULL; /* Leave page->index set: truncation lookup relies upon it */ /* => 减少地址空间中的页计数 */ mapping->nrpages--; __dec_zone_page_state(page, NR_FILE_PAGES); if (PageSwapBacked(page)) __dec_zone_page_state(page, NR_SHMEM); BUG_ON(page_mapped(page));/* * Some filesystems seem to re-dirty the page even after * the VM has canceled the dirty bit (eg ext3 journaling). * * Fix it up by doing a final dirty accounting check after * having removed the page entirely. */ if (PageDirty(page) && mapping_cap_account_dirty(mapping)) { dec_zone_page_state(page, NR_FILE_DIRTY); dec_bdi_stat(mapping->backing_dev_info, BDI_RECLAIMABLE); }}


推荐阅读