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

📄 004_arch_i386_mm_init_c.html

📁 重读linux 2.4.2o所写的笔记
💻 HTML
字号:
  <html lang="zh-CN" xmlns:gdoc="">  <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <style type="text/css">/* default css */table {  font-size: 1em;  line-height: inherit;}div, address, ol, ul, li, option, select {   margin-top: 0px;  margin-bottom: 0px;}p {  margin: 0px;}body {        margin: 0px;          padding: 0px;    font-family: Verdana, sans-serif;  font-size: 10pt;  background-color: #ffffff;}h6 { font-size: 10pt }h5 { font-size: 11pt }h4 { font-size: 12pt }h3 { font-size: 13pt }h2 { font-size: 14pt }h1 { font-size: 16pt }blockquote {padding: 10px; border: 1px #DDD dashed }a img {border: 0}div.google_header, div.google_footer {  position: relative;  margin-top: 1em;  margin-bottom: 1em;}/* end default css */  /* default print css */    @media print {    body {       padding: 0;       margin: 0;     }    div.google_header, div.google_footer {      display: block;      min-height: 0;      border: none;    }    div.google_header {      flow: static(header);    }    /* 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_45jb24hqd3:7">      <table align=center cellpadding=0 cellspacing=0 height=5716 width=768>
  <tbody>
  <tr>
    <td height=5716 valign=top width=100%>
      <pre>2005-10-24<br>arch/i386/mm/init.c   <br>   pmd_bad ....<br>   <br>   <br>   文件的开始的一段注释中,介绍了BAD_PAGE, ZERO PAGE的作用.应该好好读<br>读.<br>   前面已经说起过pmd_bad, 这只是一个例行检查. 但是BAD_PAGE则有另外的<br>作用.再内核内存耗尽的时候,现在的处理策略是将进程相应的pte写成指向此<br>BAD_PAGE. 注释中说,used for page faults,其实内存耗尽了,如何设置pte?<br>无论是不是在page fault的处理过程中,需要pte的时候内存耗尽,分配不出页面<br>就使用此BAD_PAGE. bad pmd 作用是一样的.注释提到,以前,内核内存耗尽就杀<br>掉此进程.<br>   下面通过一个函数,分析以上种种....<br>pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)<br>{<br>	unsigned long pte;<br><br>	pte = (unsigned long) __get_free_page(GFP_KERNEL);<br>	//可能睡醒之后已经被其他流程设置好了,(注意我们使用的是pmd的指针)<br>	if (pmd_none(*pmd)) {//还没有就我们来设置<br>		if (pte) {<br>			clear_page((void *)pte);<br>			set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)));<br>			return (pte_t *)pte + offset;<br>		}<br>		//这里就是内核内存耗尽了,那就就设置成BAD_PAGE<br>		set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table())));<br>		return NULL; //然后返回内存分配失败<br>	}<br>	free_page(pte);<br>	//别人已经代劳了,我们就释放掉刚获取的页面<br>	if (pmd_bad(*pmd)) {//做例行检查,保证页面属性可读写等<br>	   //检查没有通过就奇怪了!也恢复成BAD_PAGE<br>		__handle_bad_pmd(pmd);<br>		return NULL;<br>	}<br>	//一切正常,返回已经设置的pte<br>	return (pte_t *) pmd_page(*pmd) + offset; <br>}<br><br>    此函数中已经祥加注释了,希望有疑问的仔细阅读.<br>    首先要注意,内存分配失败后,返回的是NULL,注意get_bad_pte_table却为<br>页表提供了指向BAD_PAGE的pte.希望你阅读get_bad_pte_table的时候没有困难<br>然后注意,对于其他流程代劳的页面做了检查,pmd_bad,如果检查通不过返回也<br>是NULL,并且也是用BAD_PAGE填充pte,见__handle_bad_pmd,同样希望__handle_<br>bad_pmd你读起来轻松自如.<br>    通过bad_pmd的检查保证页面可读写,拥有DIRTY,ACESS属性,这在最大程度<br>上保证了这个页面"似乎"一切正常可以认为存在.我们怀疑一切,做bad pmd检测<br>但是其实并"没有"bad pmd,起码,我们没有主动破坏过,良好的内核中应该永远没<br>有bad pmd. 在__handle_bad_pmd的时候输出了一个log信息<br>void __handle_bad_pmd(pmd_t *pmd)<br>{<br>	pmd_ERROR(*pmd);<br>	set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table())));<br>}<br><br>     通过这一点,可以知道,如果有bad pmd就一定是有什么不对了!!!(fix me)<br>     <br>     到这里,此文件的前面一部分,关于BAD_PTE相关的部分就不再介绍了,看看<br>cpu quick list的一些东西:<br>int do_check_pgt_cache(int low, int high)<br>{<br>	int freed = 0;<br>	if(pgtable_cache_size &gt; high) {<br>		do {<br>			if(pgd_quicklist)<br>				free_pgd_slow(get_pgd_fast()), freed++;<br>			if(pmd_quicklist)<br>				free_pmd_slow(get_pmd_fast()), freed++;<br>			if(pte_quicklist)<br>				free_pte_slow(get_pte_fast()), freed++;<br>		} while(pgtable_cache_size &gt; low);<br>	}<br>	return freed;<br>}<br>    此函数在cpu idle 的时候从per cpu 的页面缓存链表上取下页面返还给内存<br>管理系统. slow,fast的区别也在与此.(其他情况也有调用此函数释放内存的).<br><br>  <br>    下面的一个函数和HIGHMEM有关.在此之前,借此机会可以梳理一下内核对虚<br>拟地址的使用布局. 参考include/asm-i386/fixmap.h<br><br>  内核在使用虚拟空间之前,先留了8k的空洞,从FIXADDR_TOP(0xffffe000UL)<br>开始使用.从高地址到低地址排列如下:<br>+------------------------------------------------------------------     <br>|   <span style=COLOR:#3333ff>8K空洞</span><br>+------------------------------------------------------------------<br>|   <span style=COLOR:#ff0000>FIXADDR_TOP</span>(0xffffe000UL)            (include/asm-i386/fixmap.h)<br>|   <span style=COLOR:#ff0000>fixed map</span>(每项4k虚存,见FIXADDR_SIZE)      <br>|      { //fix map 内容 (enum fixed_addresses)<br>|         FIX_APIC_BASE,	<br>|  	  FIX_IO_APIC_BASE_0,<br>|	  FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1<br>|	<br>|	  FIX_CO_CPU,	/* Cobalt timer */<br>|	  FIX_CO_APIC,	/* Cobalt APIC Redirection Table */ <br>|	  FIX_LI_PCIA,	/* Lithium PCI Bridge A */<br>|	  FIX_LI_PCIB,	/* Lithium PCI Bridge B */<br>+--------------<br>#ifdef CONFIG_HIGHMEM   /*为fix KMAP预留每cpu 8k的虚存,读写各4k*/<br>|  	   <span style=COLOR:#ff0000>FIX_KMAP_BEGIN</span>,  /* 主要用于kmap_atomic*/<br>|	   FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,<br>#endif<br>+--------------<br>|	   __end_of_fixed_addresses<br>|       }<br>|   FIXADDR_START	(FIXADDR_TOP - FIXADDR_SIZE)<br>+--------------------------------------------------------------------<br>|   <span style=COLOR:#ff0000>VMALLOC_END</span>	(FIXADDR_START)  (include/asm-i386/pgtable.h)<br>|       +------------------<br>|       |  xxxxx: kmap 和 vmalloc 相互重叠,2.6已经修正<br>|       |     kmap 使用的4M虚存  (asm/highmem.h,LAST_PKMAP)<br>|       |  PKMAP_BASE (0xfe000000UL) (距离4G 32M)<br>|       +------------------<br>|     vmalloc 映射区<br>|   <span style=COLOR:#ff0000>VMALLOC_START</span> (((unsigned long) high_memory + 2*VMALLOC_OFFSET-1)<br>|                  &amp; \~(VMALLOC_OFFSET-1)) /*down align 8M */<br>+--------------------------------------------------------------------<br>|   约 8M 空洞<br>+--------------------------------------------------------------------<br>|   high_memory (见003___arch_i386_mm_ioremap.c 对此的分析)  <br>|      内核已经映射了的物理页面 MAX 896M<br>|   3G<br>+--------------------------------------------------------------------<br>|   resoved for app 0-3G<br>+--------------------------------------------------------------------<br>   <br>        003___arch_i386_mm_ioremap.c 对high_memory进行分析的时候提到<br>VMALLOC_RESERVE (ULONG)(128&lt;&lt;20)  arch/i386/kernel/setup.c. 从今天的<br>分析可知,这128M中一部分给了fix map,一部分是kmap,vmallc,还有一部分被<br>空洞占用. 注意2.4中kmap使用的虚存和vmalloc的顶端有重叠,2.6已经修正.<br>    总结一下:<br>    系统初始化的时候预留128M虚存,896M用于"直接"映射物理内存.直接映射<br>就是virt_to_phy可以工作,只差一个常量.见宏MAXMEM,函数setup_arch.这128<br>M内存一部分用于fix map(enum fixed_addresses),一部分用于kmap(PKMAP_BASE)<br>,然后用于vmalloc. kmap_atomic使用了fix map项FIX_KMAP_BEGIN提供可以在<br>中断环境使用的"kmap". HIGHMEM由kmap和kmap_atomic构成,但是vmalloc机制<br>也尽可能的使用HIGHMEM,只是内核要直接访问highmem的物理地址时可以使用<br>kmap,kmap_atomic.<br>     003___arch_i386_mm_ioremap.c 分析__ioremap时提到<br>/*<br> * 映射指定的物理地址的一段内存到内核的虚拟地址.<br> * 内核如果需要直接访问high address的内存则也需要这个函数先映射一下.<br> *<br>     有些不够准确,通过这次分析,概念上更清楚些了.宏vmlloc就是分配的<br>HIGHMEM,如果非得使用__ioremap,也是未尝不可....<br><br>    003___arch_i386_mm_ioremap.c中还提到了page-&gt;virtual,这里借机补充<br>说明一下.在初始化page的mem_map时候,free_area_init_core对非ZONE_HIGHMEM<br>的page-&gt;virtual进行了初始化.而ZONE_HIGHMEM则在需要的时候用kmap来映射<br>页面并初始化page-&gt;virtual,或者使用vmalloc. 所以,在任何时候page-&gt;virtual<br>都可代表页面在内核的虚拟地址(只要非0). <br>   以后分析mm/highmem.c的时候可以轻松多啦.<br>     <br>   回到正题, init.c看函数 <br>#if CONFIG_HIGHMEM<br>pte_t *kmap_pte;<br>pgprot_t kmap_prot;<br>#define kmap_get_fixmap_pte(vaddr)			\<br>   pte_offset(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr))<br>void __init kmap_init(void)<br>{  /*为HIGHMEM kmap_automic 使用的两个变量求值,最多NR_CPU*2项pte*/<br>	unsigned long kmap_vstart;<br><br>	/* cache the first kmap pte */<br>	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);<br>	                     /*Fix KAMP 起始虚拟地址*/<br>	kmap_pte = kmap_get_fixmap_pte(kmap_vstart);<br>                             /*顺着页表找到起始pte*/<br>                             <br>	kmap_prot = PAGE_KERNEL;<br>}<br>#endif /* CONFIG_HIGHMEM */     <br>    其实调用此函数之前已经使用了fixrange_init对三级页表进行了部分初始<br>化. 所谓部分,即,只有pte未设置,也就是说虚拟地址落实到物理地址的最后一<br>步还没有进行. fixrange_init也在此文件中,分析到这里阅读这个没有"逻辑"的<br>函数当然应该没有问题了.不再赘述.<br>    set_pte_phys 逻辑亦是简单,注意__flush_tlb_one在cpu支持invlpg指令的<br>时候只刷新一个页面的映射.<br><br>   pagetable_init 初始化内核页表,之后就可以废弃head.s中的临时表了.同时<br>部分初始化fix map, 并为HIGEMEM初始化Permanent kmap使用的4M虚存(1024项<br>pte,刚好一个页面大小).<br><br>static void __init pagetable_init (void){<br>.............<br>#if CONFIG_HIGHMEM<br>/*<br> * Permanent kmaps: (即,kmap 使用的虚存,2.4中和vmallc使用的虚存有重叠)<br> */<br>	vaddr = PKMAP_BASE;<br>	fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);<br><br>	pgd = swapper_pg_dir + __pgd_offset(vaddr);<br>	pmd = pmd_offset(pgd, vaddr);<br>	pte = pte_offset(pmd, vaddr);<br>	pkmap_page_table = pte;<br>#endif<br>#if CONFIG_X86_PAE<br>	/*<br>	 * Add low memory identity-mappings - SMP needs it when<br>	 * starting up on an AP from real-mode. In the non-PAE<br>	 * case we already have these mappings through head.S.<br>	 * All user-space mappings are explicitly cleared after<br>	 * SMP startup.<br>	 */<br>    /*0-4M , 3G-4M的恒同映射,smp完成后用zap_low_mappings 清除*/<br>	pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];<br>#endif<br><br>}   <br>    <br>   接下来是paging_init,调用上面的pagetable_init,初始化页表然后重新加<br>载cr3,flush tlb,然后使用kmap_init为kmap_automic使用的变量求值,.....<br>不算复杂,重要的是free_area_init,初始化了zone-buddy内存管理系统(设置<br>所有页面都是PG_reserved(free_all_bootmem,负责重置)).<br>   <br>   test_wp_bit --&gt;检查cpu是否支持写保护位,不支持就提示用户重新编译内<br>核.<br><br>  接下来的重要函数就是mem_init:<br>   1. 初始化变量high_memory, 参见以前说明.<br>   2. 调用free_all_bootmem, 清除ram页的PG_reserved位,并"释放"页面到<br>      buddy 系统.<br>   3. boot mem 无法处理HIGHMEM,对highmem,mem_init自己清除PG_reserved位,<br>      并"释放"页面到buddy 系统.<br>   4. 打印一些统计信息,如果不是SMP,就清楚0-8M,3G-8M的恒同映射.(所有用<br>      户空间pgd)<br>      <br>  最后只说一下free_initmem . 释放.data.init段占用的内存. 顺便请注意<br>free_initrd_mem, 看到了initrd,^_^.<br><br>     在整个系统启动过程中,关于内存的初始化如下:<br>  <br> setup.S-&gt;asmlinkage void __init start_kernel(void) (init/main.c)<br>  |<br>  +--&gt;setup_arch ---&gt;  处理e820内存报告<br>                --&gt;   关于内存的提示信息<br>                ---&gt;  初始化bootmem (init_bootmem)<br>                ---&gt;  paging_init--+<br>                         +-------+<br>  |                      +--&gt; pagetable_init(含fix map,vmalloc init)<br> \ /                     +--&gt; load cr3<br>  .                      +--&gt; kmap_init                        <br>  .                      +--&gt; free_area_init(zone-buddy初始化)<br>  .             ---&gt;smp,apic,roms等处理<br>  +--&gt; idt gate modules,kmem_cache_init<br>  |<br>  +--&gt; mem_init --&gt;free_all_bootmem buddy 得到页面控制权<br>  +<br>  +--&gt; proc_root_init,fork_init, ipc,inode<br>  +--&gt; smp_init<br>  +<br>  +--&gt; 创建kernel thread, init (init/main.c-&gt;函数init)<br>                +---&gt;do_basic_setup<br>                       ----&gt;init pci,mtrr,sysctl,mca....<br>                       ----&gt;filesystem_setup<br>                       ----&gt;mount_root (关注...)<br>                       ----&gt;......<br>                +---&gt;  free_initmem<br>                +---&gt;  打开console<br>                +---&gt;execve("/sbin/init",argv_init,envp_init);<br>	        +---&gt;execve("/etc/init",argv_init,envp_init);<br>	        +---&gt;execve("/bin/init",argv_init,envp_init);<br>	        +---&gt;execve("/bin/sh",argv_init,envp_init);<br><br><br>    收工.<br>   <br>&nbsp;<br></pre>
    </td>
  </tr>
  </tbody>
</table></body></html>

⌨️ 快捷键说明

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