page _refcount和_mapcount字段

linux page有两个非常重要的引用计数字段_refcount和_mapcount,都是atomic_t类型,其中,_refcount表示内核中应用该page的次数。当_refcount = 0时,表示该page为空闲或者将要被释放。当_refcount > 0,表示该page页面已经被分配且内核正在使用,暂时不会被释放。

_refcount

内核中常用的加减_refcount应用计数的API:get_page()和put_page()


static inline void get_page(struct page *page)
{
    page = compound_head(page);
    /*
     * Getting a normal page or the head of a compound page
     * requires to already have an elevated page->_refcount.
     */
    VM_BUG_ON_PAGE(page_ref_count(page) <= 0, page);
    page_ref_inc(page);
}

static inline void put_page(struct page *page)
{
    page = compound_head(page);

    /*
     * For private device pages we need to catch refcount transition from
     * 2 to 1, when refcount reach one it means the private device page is
     * free and we need to inform the device driver through callback. See
     * include/linux/memremap.h and HMM for details.
     */
    if (IS_HMM_ENABLED && unlikely(is_device_private_page(page) ||
        unlikely(is_device_public_page(page)))) {
        put_zone_device_private_or_public_page(page);
        return;
    }

    if (put_page_testzero(page))
        __put_page(page);
}

增加_refcount场景
1)alloc_pages 分配成功_refcount = 1

2)加入到lru链表

3)加入到address_space

上面三个路径可以通过write写数据场景结合源码分析,我们知道write系统调用可以使用page cache加速写性能,我们就以该场景看下page->_refcount引用计数的变化情况。write写数据场景的page cache创建是mm/filemap.c : grab_cache_page_write_begin

/*
 * 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;
    ...

no_page:
	if (!page && (fgp_flags & FGP_CREAT)) {
        ..
		page = __page_cache_alloc(gfp_mask);
		if (!page)
			return NULL;

		if (WARN_ON_ONCE(!(fgp_flags & (FGP_LOCK | FGP_FOR_MMAP))))
			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, index, gfp_mask);
		if (unlikely(err)) {
			put_page(page);
			page = NULL;
			if (err == -EEXIST)
				goto repeat;
		}

		/*
		 * add_to_page_cache_lru locks the page, and for mmap we expect
		 * an unlocked page.
		 */
		if (page && (fgp_flags & FGP_FOR_MMAP))
			unlock_page(page);
	}

	return page;
}
  1. __page_cache_alloc通过alloc_page创建页面,page刚刚创建_refcount = 1
  2. add_to_page_cache_lru将page分别加入lru链表和address_space,这两个步骤中都会增加_refcount,该函数返回后_refcount = 3

add_to_page_cache_lru


int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
				pgoff_t offset, gfp_t gfp_mask)
{
	void *shadow = NULL;
	int ret;

	__SetPageLocked(page);
	ret = __add_to_page_cache_locked(page, mapping, offset,
					 gfp_mask, &shadow);
	if (unlikely(ret))
		__ClearPageLocked(page);
	else {
		...
		if (!(gfp_mask & __GFP_WRITE) && shadow)
			workingset_refault(page, shadow);
		lru_cache_add(page);
	}
	return ret;
}


static int __add_to_page_cache_locked(struct page *page,
				      struct address_space *mapping,
				      pgoff_t offset, gfp_t gfp_mask,
				      void **shadowp)
{
    ...
	get_page(page);
    ...
error:
	page->mapping = NULL;
	/* Leave page->index set: truncation relies upon it */
	put_page(page);
	return error;
}

void lru_cache_add(struct page *page)
{
    ...
	get_page(page);
    ...
}

_add_to_page_cache_locked和lru_cache_add都会增加_refcount引用计数。

4)page被映射到其他用户进程pte时,_refcount引用技数会加1。

例如子进程创建时共享父进程的地址空间,设置父进程的pte页表项内容到子进程中并增加该页的_refcount计数:

do_fork->copy_process->copy_mm->dup_mmap->copy_page_range->...->copy_pte_range->copy_one_pte函数。

5) 对于PG_swapable的页面,_add_to_swap_cache会增加_refcount引用计数

6)内核对页面进程操作的一些关键路径上也会增加_refcount。比如内核的follow_page和get_user_pages

_mapcount

_mapcount表示这个页面被进程映射的个数,即已经映射了多少个用户pte页表。

  • _mapcount = -1,表示没有pte映射到该页面中
  • _mapcount = 0,表示只有父进程映射了页面,匿名页面刚分配时,_mapcount = 0
  • _mapcount > 0,表示除了父进程外还有其他进程映射了这个页面,同样以子进程创建共享父进程地址空间为例,设置父进程的pte页表项到子进程中并增加该页面的_mapcount。

page _refcount和_mapcount字段_第1张图片

get_page增加_refcount,page_dump_rmap增加_mapcount

 

你可能感兴趣的:(内存子系统,linux,linux操作系统,内核,内存)