⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 025_mm_vmscan_c.html

📁 重读linux 2.4.2o所写的笔记
💻 HTML
📖 第 1 页 / 共 2 页
字号:
    }    /* used to insert page numbers */    div.google_header::before, div.google_footer::before {      position: absolute;      top: 0;    }    div.google_footer {      flow: static(footer);    }    /* always consider this element at the start of the doc */    div#google_footer {      flow: static(footer, start);    }    span.google_pagenumber {      content: counter(page);    }    span.google_pagecount {      content: counter(pages);    }  }  @page {    @top {      content: flow(header);    }    @bottom {      content: flow(footer);    }  }  /* end default print css */ /* custom css *//* end custom css */  /* ui edited css */    body {    font-family: Verdana;        font-size: 10.0pt;    line-height: normal;    background-color: #ffffff;  }    .documentBG {    background-color: #ffffff;  }  /* end ui edited css */</style>   </head>  <body  revision="dcbsxfpf_66dzt89wc3:10">      <div align=center>
  <table align=center border=0 cellpadding=0 cellspacing=0 height=5716 width=768>
    <tbody>
    <tr>
      <td height=5716 valign=top width=100%>
        <pre>2006-8-11   <br> mm/vmscan.c                                                                                                    <br>                                                  <br>                            <br>                            I)概述            <br>                             <br>在分析page alloc的时候,分析过linux对内存页面的空闲量有些什么样的要求:<br><br>1)可分配页面的保有量要求:inactive_clean+free pages(in buddy pages)<br>   系统的期望值是freepages.high + inactive_target / 3,inactive_target就是<br>min((memory_pressure &gt;&gt; INACTIVE_SHIFT),num_physpages / 4)).可见期望的保有量有动态的因素在内.因为memory_pressure<br>是一段时间内的平均值,根据内存需求的不同所期望的保有量也不一样.<br>   <br>   而现在的保有量是nr_free_pages() + nr_inactive_clean_pages();<br>   函数free_shortage,计算期望的可分配页面和现实之差距.如果保有量合格,就看zone中的inbuddy free pages是否比期望值少.<br>只要有一个保有量不合格,就必须立即加以调整.<br><br>2)潜在可分配页面的保有量要求(inactive_shortage):<br>    潜在可分配页面就是:buddyfree+inactiveclean+inactive_dirty<br>    期望保有量:freepages.high+inactive_target<br>    现存量:<br>      nr_free_pages()+nr_inactive_clean_pages()+nr_inactive_dirty_pages.<br>  <br> 保证这两种页面有一个合理的水平,这样在分配内存的时候,(期望)一般不会出现内存紧缺.free_shortage,inactive_shortage这两<br>个函数不复杂,略.<br>                                                    <br> 这个文件就是处理lru,负责页面的回收. 包括映射断裂,逐级的调整页面所在的lru 队列,<br>直至最后回到buddy 系统. 另外对于dcache,icache, slab系统的空闲页面,在内存紧张的<br>时候也要进行回收.从而保证空闲内存保有量得到满足.<br><br>lru cache的组成,前面已经有叙述了:<br>   struct list_head active_list;<br>   struct list_head inactive_dirty_list;<br>   每个zone 中的inactive clean 队列.<br>                                                  <br> 为了完成这些任务,有两个内核线程,kswapd和kreclaimd,见下图. 图中忽略了调用的条件<br>简单的列出了每个线程的作用:                                                  <br><br>kswapd                                                                      <br>    |----do_try_to_free_pages                                                <br>    |             page_launder (inactive_dirty---&gt;zone clean)               <br>                  shrink_dcache_memory                                      <br>                  shrink_icache_memory                                      <br>                  refill_inactive                                           <br>                        kmem_cache_reap                                      <br>                        refill_inactive_scan  (active----&gt;inactive)          <br>                        shrink_dcache_memory                                 <br>                        shrink_icache_memory                                 <br>                        swap_out  (prcess vss-&gt; active list or inactive)     <br>                  kmem_cache_reap (slab)                                          <br>                                                                            <br>    +--&gt; refill_inactive_scan (active----&gt;inactive)                          <br>                                                                             <br>    +--&gt; run_task_queue                                                      <br>                                                                             <br>    +--&gt; oom_kill                                                            <br>                                                                             <br>                                                                          <br>                                                                             <br> kreclaimd ( zone_t-&gt;inactive_clean_pages  ----&gt; zone_t buddy )<br>      reclaim_page                                                       <br>      _free_page                                                         <br>                                                                             <br>                                            <br>                                                  <br>    为了更加直观,请看下面的图:(page alloc中也有)                                               <br>                                                                                                     <br>mm-&gt;rss(进程页面) ************************************************************                             <br>                                                                      |swap_out-&gt;try_to_swap_out     <br>active_list-&gt;*********************** ********                         |                              <br>                        /\                |refill_inactive_scan       |                              <br>                         |page_launder    |                           |                           <br>                         |               \ /                         \ / <br>inactive_dirty_list-&gt;*****************************************************                         <br>                                    |page_launder (wite back to disk if necessary)                   <br>                                   \ /                                                               <br>zone_t-&gt;inactive_clean_pages-&gt;*********************** (__alloc_pages: reclaim_page)                 <br>                                     |kreclaimd-&gt;reclaim_page, __free_pages                          <br>                                    \ /                                                              <br>zone_t-&gt;free_area(buddy)-&gt;***********************                                                   <br><br><br>                          II)swap out<br>                          <br>   一次扫描一定量的进程页面,从不太忙碌的进程中回收可用页面是保证空闲页面保有量,<br>实现VM(back store)的一个重要操作.<br>   swap_out选择一个进程(当然不包括内核),通过try_to_swap_out断开此进程对页面的映<br>射.根据页面的熟悉(是否已经在swap,是否是page cache(page-&gt;mapping)),采取不同操作.<br>   进程有一个rss统计,还有一个swap_cnt=(mm-&gt;rss &gt;&gt; SWAP_SHIFT),swap cnt代表的是<br>这个进程在swap out的过程中应该贡献的页面数量.(虽然不一定能找到合适页面)所以swap<br>cnt大的进程优先被选中.<br>    <br>/*<br> * 选择swap_cnt 值最大的任务试着交换出一些空间.swap_cnt 最大表明他最应该受到检查.<br> * 看看能不能换出一些页面. (try_to_swap_out)<br> * N.B. This function returns only 0 or 1.  Return values != 1 from<br> * the lower level routines result in continued processing.<br> */<br>#define SWAP_SHIFT 5<br>#define SWAP_MIN 8<br>static int swap_out(unsigned int priority, int gfp_mask)<br>{<br>	int counter;<br>	int __ret = 0;<br><br>	/* <br>	 * 进行两种方式的扫描, assign = 0,按照swap_cnt选择进程.<br>	 * assign = 1,重新计算rss 和swap_cnt 的值,然后同时根据<br>	 * swp_cnt选择合适进程.<br>	 *<br>	 * 如果选中的进程不能提供空闲页面,则清楚swap_cnt(swap_out_mm),这样 <br>	 * 任务就不会再次被选中.<br>	 */<br>	counter = (nr_threads &lt;&lt; SWAP_SHIFT) &gt;&gt; priority;<br>	if (counter &lt; 1)<br>		counter = 1;<br><br>	for (; counter &gt;= 0; counter--) { //这个循环次数受优秀级控制<br>		struct list_head *p;          //代表了努力程度<br>		unsigned long max_cnt = 0;<br>		struct mm_struct *best = NULL;<br>		int assign = 0; /*assign 在某一轮尝试如果不能找到一个best,<br>		                           则进行第二种方式的扫描*/<br>		int found_task = 0;<br>	select:<br>		spin_lock(&amp;mmlist_lock);<br>		p = init_mm.mmlist.next;<br>		for (; p != &amp;init_mm.mmlist; p = p-&gt;next) {<br>			struct mm_struct *mm = list_entry(p, struct mm_struct, mmlist);<br>	 		if (mm-&gt;rss &lt;= 0)<br>				continue;<br>			found_task++;<br>			/* Refresh swap_cnt? */<br>			if (assign == 1) { //选择进程同时重新计算swap cnt<br>				mm-&gt;swap_cnt = (mm-&gt;rss &gt;&gt; SWAP_SHIFT);<br>				if (mm-&gt;swap_cnt &lt; SWAP_MIN)<br>					mm-&gt;swap_cnt = SWAP_MIN;<br>			}<br>			if (mm-&gt;swap_cnt &gt; max_cnt) {<br>				max_cnt = mm-&gt;swap_cnt;<br>				best = mm;<br>			}<br>		}<br><br>		/* Make sure it doesn't disappear */<br>		if (best) <br>			atomic_inc(&amp;best-&gt;mm_users);<br>		spin_unlock(&amp;mmlist_lock);<br><br>		/*<br>		 * We have dropped the tasklist_lock, but we<br>		 * know that "mm" still exists: we are running<br>		 * with the big kernel lock, and exit_mm()<br>		 * cannot race with us.<br>		 */<br>		if (!best) { /* 找不到best 说明swap_cnt 都变成了0*/<br>			if (!assign &amp;&amp; found_task &gt; 0) {<br>				assign = 1;<br>				goto select;<br>			}<br>			break;<br>		} else {<br>			__ret = swap_out_mm(best, gfp_mask);<br>			mmput(best);<br>			break;<br>		}<br>	}<br>	return __ret;<br>}<br><br>     然后看看try_to_swap_out,其他swap out相关的函数不再分析.<br>/*<br> * rss(驻留页面集合)<br> *<br> *   希望caller 继续就返回0, 否则返回1.<br> *<br> * NOTE! If it sleeps, it *must* return 1 to make sure we<br> * don't continue with the swap-out. Otherwise we may be<br> * using a process that no longer actually exists (it might<br> * have died while we slept).<br> */<br>static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, <br>                           unsigned long address, pte_t * page_table, <br>                           int gfp_mask)<br>{<br><br><br>	pte_t pte;<br>	swp_entry_t entry;<br>	struct page * page;<br>	int onlist;<br><br>	pte = *page_table;<br>	if (!pte_present(pte))<br>		goto out_failed;<br>	page = pte_page(pte);<br>	if ((!VALID_PAGE(page)) || PageReserved(page))<br>		goto out_failed;<br><br>	if (!mm-&gt;swap_cnt)<br>		return 1;<br><br>	mm-&gt;swap_cnt--; /*swap_cnt: 根据rss,有个初始值每检查一个页面减1 */<br>	       <br><br>  //是否在activ list<br>	onlist = PageActive(page);<br>    <br>	/* 如果最近被访问过,提升age */<br>	if (ptep_test_and_clear_young(page_table)) {<br>		age_page_up(page);<br>		goto out_failed;<br>	}<br><br><br>  /*最近未被访问,则降低age*/<br>	if (!onlist) /* 不在activ list age down 由swap_out负责, refill_inactive_scan <br>                     负责active list 的age down*/<br>	    age_page_down_ageonly(page);<br><br>	<br>	 /*寿命未尽,不管*/<br>	if (page-&gt;age &gt; 0)<br>		goto out_failed;<br>	<br><br>	if (TryLockPage(page))<br>		goto out_failed;<br><br><br>  /* age 耗尽可以断开映射了*/<br>  pte = ptep_get_and_clear(page_table);<br>	flush_tlb_page(vma, address);<br><br><br>	<br>	if (PageSwapCache(page)) {<br>		/* <br>		 * 页面已经在交换空间(swap cache)了,<br>		 * 返回0, 通知上层继续扫描.<br>		 */<br>		entry.val = page-&gt;index;<br>		if (pte_dirty(pte))<br>			set_page_dirty(page);<br>set_swap_pte:<br>		swap_duplicate(entry); /*此进程引用此swap entry*/<br>		set_pte(page_table, swp_entry_to_pte(entry));<br>drop_pte:<br>		UnlockPage(page);<br>		mm-&gt;rss--;<br><br>		<br>		deactivate_page(page); /* active-&gt;inactive, if on list*/<br><br>		page_cache_release(page); /*此进程不再能够使用此页面*/<br>out_failed:<br>		return 0;<br>	}<br><br>	/*<br>	 * 下面判断一下这个page 是否是一个clean page.<br>	 * 如果是我们就可以断开映射了.<br>	 * <br>	 * 如果不是的话我们应该将page-&gt;flags <br>	 * 的dity标志置位.<br>	 */<br>	flush_cache_page(vma, address);<br>	if (!pte_dirty(pte))<br>		goto drop_pte;<br><br>	/*<br>	 * dity 页,看看是否属于一个mapping,上面的PageSwapCache<br>	 * 只是判断是否在swapper_space,只是mapping的一个特例.<br>	 */<br>	if (page-&gt;mapping) {<br>		set_page_dirty(page);<br>		goto drop_pte;<br>	}<br><br>	/*<br>	 * dity, 还没有交换空间<br>	 * 分配一个交换空间给他<br>	 */<br>	entry = get_swap_page();<br>	if (!entry.val)<br>		goto out_unlock_restore; /* No swap space left */<br><br>	<br>	/* 挂入swap cache, 因为page-&gt;age 为零, <br>	 * 不会挂入入全局active 队列.<br>     * 同时置位flag 的dity 位.<br>	 */<br>	add_to_swap_cache(page, entry);<br><br>	set_page_dirty(page);<br><br>	goto set_swap_pte;<br><br>out_unlock_restore:<br>	set_pte(page_table, pte);<br>	UnlockPage(page);<br>	return 0;<br>}<br><br><br>   swap out扫描的过程中根据页面最近是否被访问过,调整页面的age值,对于age=0的页面<br>可以断开其映射. 可以看到page cache(mapping)和swap cache的不同处理.<br>   swap空间的页面,pte记录的是swap entry. 普通maping则直接断开映射. pte置0.<br>   <br>   <br>             <br>                    III)  page_launder<br> <br>   将dity 的页面清洗成clean 并移入clean 队列. 对inactive list 扫描两次,第一次把已<br>经干净的页面移入inactive_clean 链表,第二次异步回写dirty 页面.当kswapd 无法供应所需<br>的页面时,则进行同步的回写. 原作者已经写了很多注释.仔细看看吧.有问题则讨论.<br>#define MAX_LAUNDER 		(4 * (1 &lt;&lt; page_cluster))<br>int page_launder(int gfp_mask, int sync)<br>{<br>	int launder_loop, maxscan, cleaned_pages, maxlaunder;<br>	int can_get_io_locks;<br>	struct list_head * page_lru;<br>	struct page * page;<br><br>	/*<br>	 * We can only grab the IO locks (eg. for flushing dirty<br>	 * buffers to disk) if __GFP_IO is set.<br>	 */<br>	can_get_io_locks = gfp_mask &amp; __GFP_IO;<br><br>	launder_loop = 0;<br>	maxlaunder = 0;<br>	cleaned_pages = 0;<br><br>dirty_page_rescan:<br>	spin_lock(&amp;pagemap_lru_lock);<br>	maxscan = nr_inactive_dirty_pages;<br>	while ((page_lru = inactive_dirty_list.prev) != &amp;inactive_dirty_list &amp;&amp;<br>				maxscan-- &gt; 0) {<br>		page = list_entry(page_lru, struct page, lru);<br><br>		/* Wrong page on list?! (list corruption, should not happen) */<br>		if (!PageInactiveDirty(page)) {<br>			printk("VM: page_launder, wrong page on list.\n");<br>			list_del(page_lru);<br>			nr_inactive_dirty_pages--;<br>			page-&gt;zone-&gt;inactive_dirty_pages--;<br>			continue;<br>		}<br><br>		/* Page is or was in use?  Move it to the active list. */<br>		if (PageTestandClearReferenced(page) || page-&gt;age &gt; 0 ||<br>				(!page-&gt;buffers &amp;&amp; page_count(page) &gt; 1) ||<br>				page_ramdisk(page)) {<br>			del_page_from_inactive_dirty_list(page);<br>			add_page_to_active_list(page);<br>			continue;<br>		}<br><br>		/*<br>		 * The page is locked. IO in progress?<br>		 * Move it to the back of the list.<br>		 */<br>		if (TryLockPage(page)) {<br>			list_del(page_lru);<br>			list_add(page_lru, &amp;inactive_dirty_list);<br>			continue;<br>		}<br><br>		/*<br>		 * Dirty swap-cache page? Write it out if<br>		 * last copy..<br>		 */<br>		if (PageDirty(page)) {<br>			int (*writepage)(struct page *) = page-&gt;mapping-&gt;a_ops-&gt;writepage;<br>			int result;<br><br>			if (!writepage)<br>				goto page_active;<br><br>			/* First time through? Move it to the back of the list */<br>			if (!launder_loop) {<br>				list_del(page_lru);<br>				list_add(page_lru, &amp;inactive_dirty_list);<br>				UnlockPage(page);<br>				continue;<br>			}<br><br>			/* OK, do a physical asynchronous write to swap.  */<br>			ClearPageDirty(page);<br>			page_cache_get(page);<br>			spin_unlock(&amp;pagemap_lru_lock);<br><br>			result = writepage(page);<br>			page_cache_release(page);<br><br>			/* And re-start the thing.. */<br>			spin_lock(&amp;pagemap_lru_lock);<br>			if (result != 1)<br>				continue;<br>			/* writepage refused to do anything */<br>			set_page_dirty(page);<br>			goto page_active;<br>		}<br><br>		/*<br>		 * If the page has buffers, try to free the buffer mappings<br>		 * associated with this page. If we succeed we either free<br>		 * the page (in case it was a buffercache only page) or we<br>		 * move the page to the inactive_clean list.<br>		 *<br>		 * On the first round, we should free all previously cleaned<br>		 * buffer pages<br>		 */<br>		if (page-&gt;buffers) {<br>			int wait, clearedbuf;<br>			int freed_page = 0;<br>			/*<br>			 * Since we might be doing disk IO, we have to<br>			 * drop the spinlock and take an extra reference<br>			 * on the page so it doesn't go away from under us.<br>			 */<br>			del_page_from_inactive_dirty_list(page);<br>			page_cache_get(page);<br>			spin_unlock(&amp;pagemap_lru_lock);<br><br>			/* Will we do (asynchronous) IO? */<br>			if (launder_loop &amp;&amp; maxlaunder == 0 &amp;&amp; sync)<br>				wait = 2;	/* Synchrounous IO */<br>			else if (launder_loop &amp;&amp; maxlaunder-- &gt; 0)<br>				wait = 1;	/* Async IO */<br>			else<br>				wait = 0;	/* No IO */<br><br>			/* Try to free the page buffers. */<br>			clearedbuf = try_to_free_buffers(page, wait);<br><br>			/*<br>			 * Re-take the spinlock. Note that we cannot<br>			 * unlock the page yet since we're still<br>			 * accessing the page_struct here...<br>			 */<br>			spin_lock(&amp;pagemap_lru_lock);<br><br>			/* The buffers were not freed. */<br>			if (!clearedbuf) {<br>				add_page_to_inactive_dirty_list(page);<br><br>			/* The page was only in the buffer cache. */<br>			} else if (!page-&gt;mapping) {<br>				atomic_dec(&amp;buffermem_pages);<br>				freed_page = 1;<br>				cleaned_pages++;<br><br>			/* The page has more users besides the cache and us. */<br>			} else if (page_count(page) &gt; 2) {<br>				add_page_to_active_list(page);<br><br>			/* OK, we "created" a freeable page. */<br>			} else /* page-&gt;mapping &amp;&amp; page_count(page) == 2 */ {<br>				add_page_to_inactive_clean_list(page);<br>				cleaned_pages++;<br>			}<br><br>			/*<br>			 * Unlock the page and drop the extra reference.<br>			 * We can only do it here because we ar accessing<br>			 * the page struct above.<br>			 */<br>			UnlockPage(page);<br>			page_cache_release(page);<br><br>			/* <br>			 * If we're freeing buffer cache pages, stop when<br>			 * we've got enough free memory.<br>			 */<br>			if (freed_page &amp;&amp; !free_shortage())<br>				break;<br>			continue;<br>		} else if (page-&gt;mapping &amp;&amp; !PageDirty(page)) {<br>			/*<br>			 * If a page had an extra reference in<br>			 * deactivate_page(), we will find it here.<br>			 * Now the page is really freeable, so we<br>			 * move it to the inactive_clean list.<br>			 */<br>			del_page_from_inactive_dirty_list(page);<br>			add_page_to_inactive_clean_list(page);<br>			UnlockPage(page);<br>			cleaned_pages++;<br>		} else {<br>page_active:<br>			/*<br>			 * OK, we don't know what to do with the page.<br>			 * It's no use keeping it here, so we move it to<br>			 * the active list.<br>			 */<br>			del_page_from_inactive_dirty_list(page);<br>			add_page_to_active_list(page);<br>			UnlockPage(page);<br>		}<br>	}<br>	spin_unlock(&amp;pagemap_lru_lock);<br><br>	/*<br>	 * If we don't have enough free pages, we loop back once<br>	 * to queue the dirty pages for writeout. When we were called<br>	 * by a user process (that /needs/ a free page) and we didn't<br>	 * free anything yet, we wait synchronously on the writeout of<br>	 * MAX_SYNC_LAUNDER pages.<br>	 *<br>	 * We also wake up bdflush, since bdflush should, under most<br>	 * loads, flush out the dirty pages before we have to wait on<br>	 * IO.<br>	 */<br>	if (can_get_io_locks &amp;&amp; !launder_loop &amp;&amp; free_shortage()) {<br>		launder_loop = 1;<br>		/* If we cleaned pages, never do synchronous IO. */<br>		if (cleaned_pages)<br>			sync = 0;<br>		/* We only do a few "out of order" flushes. */<br>		maxlaunder = MAX_LAUNDER;<br>		/* Kflushd takes care of the rest. */<br>		wakeup_bdflush(0);<br>		goto dirty_page_rescan;<br>	}<br><br>	/* Return the number of pages moved to the inactive_clean list. */<br>	return cleaned_pages;<br>}<br><br><br><br>                 <br> <br>                  IV) refill_inactive_scan<br><br>/*<br> * refill_inactive_scan - 扫描active 列表找到应该deactivate 的页面<br> * @priority: the priority at which to scan<br> * @oneshot: exit after deactivating one page<br> *<br> * This function will scan a portion of the active list to find<br> * unused pages, those pages will then be moved to the inactive list.<br> */<br> /*<br>  * 在acitve_list 中的页面有两种情况:<br>  *<br>  *  1. 拥有mapping 的page <br>  *  2. 后来加入这个队列的(加入swapper mapping).age 为0,<br>  *     但是有可能随时恢复映射  <br>  */<br>int refill_inactive_scan(unsigned int priority, int oneshot)<br>{<br>	struct list_head * page_lru;<br>	struct page * page;<br>	int maxscan, page_active = 0;<br>	int ret = 0;<br><br>	/* Take the lock while messing with the list... */<br>	spin_lock(&amp;pagemap_lru_lock);<br><br>	maxscan = nr_active_pages &gt;&gt; priority; <br>       //队尾的页面被换出的概率小<br>	         <br>	while (maxscan-- &gt; 0 &amp;&amp; (page_lru = active_list.prev) != &amp;active_list) {<br>		page = list_entry(page_lru, struct page, lru);<br><br>		/* Wrong page on list?! (list corruption, should not happen) */<br>		if (!PageActive(page)) {<br>			printk("VM: refill_inactive, wrong page on list.\n");<br>			list_del(page_lru);<br>			nr_active_pages--;<br>			continue;<br>		}<br><br>		/* Do aging on the pages. */<br>		if (PageTestandClearReferenced(page)) {<br>			// 内核持有此页面(如touch_buffer)<br>			age_page_up_nolock(page);<br>			page_active = 1;  <br>			   //置此标记会把page 移到队尾<br>		} else {<br>			age_page_down_ageonly(page);<br>			/*<br>			 * Since we don't hold a reference on the page<br>			 * ourselves, we have to do our test a bit more<br>			 * strict then deactivate_page(). This is needed<br>			 * since otherwise the system could hang shuffling<br>			 * unfreeable pages from the active list to the<br>			 * inactive_dirty list and back again...<br>			 *<br>			 * SUBTLE: we can have buffer pages with count 1.<br>			 */<br>			if (page-&gt;age == 0 &amp;&amp; page_count(page) &lt;=<br>						(page-&gt;buffers ? 2 : 1)) {<br>				deactivate_page_nolock(page); <br>				page_active = 0;<br>			} else {<br>				page_active = 1;<br>			}<br>		}<br>		/*<br>		 * If the page is still on the active list, move it<br>		 * to the other end of the list. Otherwise it was<br>		 * deactivated by age_page_down and we exit successfully.<br>		 */<br>		if (page_active || PageActive(page)) { <br>			// 把页面移到队尾<br>			list_del(page_lru);<br>			list_add(page_lru, &amp;active_list); <br>		} else {<br>			ret = 1;<br>			if (oneshot)<br>				break;<br>		}<br>	}<br>	spin_unlock(&amp;pagemap_lru_lock);<br><br>	return ret;<br>}<br><br> 再次讨论PageTestandClearReferenced.看看2.6关于此位的说明:<br> * For choosing which pages to swap out, inode pages carry a PG_referenced bit,<br> * which is set any time the system accesses that page through the (mapping,<br> * index) hash table.  This referenced bit, together with the referenced bit<br> * in the page tables, is used to manipulate page-&gt;age and move the page across<br> * the active, inactive_dirty and inactive_clean lists.<br> <br>  意思是,对于缓存文件内容的页面,除了来自用户进程的访问,内核本身访问此页面也应该<br>age up.<br>  对于2.4内核,这种页面主要来自buffer,见grow_buffers,getblk. buffer中的页面也使用<br>lru队列进行swap.但是有可能没有映射到用户页面(blk dev 文件的读取),无法age up.只好<br>通过touch_buffer进行by hand的age up.<br><br>     <br>                            page-&gt;count不死<br>                            <br>  关于 page_launder 还有一个话题,就是这个函数标号page_active的地方.论坛中有很多关<br>于这里的讨论.仅发表一下看法,供大家参考.<br>  以前的讨论中提到page_active:处的条件中隐含有<br>  (page-&gt;count==1&amp;&amp;page-&gt;mapping==0&amp;&amp;page-&gt;buffer==0)<br>  其实这个标号的条件是:<br>  1)no Buffer, no mapping(one process)(dirty can't write out or not), <br>	2)no buffer, mapping,dirty but can't write out)*/<br>  	<br>  对于第一种页面,在2.4.0的内核中,似乎就没有可能进入lru cache.(2.4.20有). 因为查看<br>页面所有进入lru的途径,必然是,或者有mapping,或者有buffer. 并且进入lru后,没有找到恢<br>复进程映射时将mapping,buffer去掉而留在lru中的情况.<br>  关于lru, page_launder,linus有一个讨论可以参考一下: <br><a href=http://groups.google.com/group/fa.linux.kernel/browse_thread/thread/2e175262f3af7dc3/7dad9a48a4cf9fc2?q=page_launder+page_active%3A&amp;lnk=ol&amp;hl=zh-CN&amp; id=d6h3 title="page_launder, linux option">page_launder, linux option</a> <br>  linus认为,进入inactive dirty的page,不应该没有mapping的.否则dirty了又能如何.当然<br>2.4.20以后这种页面可以存在的,但是swap out还是会给他分配mapping(swap space),只是临<br>存在.<br>  第二种倒是有,比如ramfs,以前的tmpfs也是.<br>  <br>  本论坛还有一个对这个函数的讨论,值得一看:<br><a href=http://www.linuxforum.net/forum/showthreaded.php?Cat=&amp;Board=linuxK&amp;Number=249364&amp;page=&amp;view=&amp;sb=&amp;o= id=p-bv title=linuxfourm关于这个问题的讨论>linuxfourm关于这个问题的讨论</a> <br>  以上的分析也希望对这个问题有所帮助.     <br>       <br>       <br>   其他函数不再讨论.swap out的过程通过各种手段进行调整,经过不断的演化,成了现在这<br>个样子.我感觉应该把握的是这种思路,然后看代码的时候就不觉得很乱了.<br><br>   linux mm分析暂告一个段落.<br>   <br>   <br>   <br>                                 2006.8.11<br></pre>
      </td>
    </tr>
    </tbody>
  </table>
</div></body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -