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

📄 007_mm_highmem_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_48f3d5crhb:9">      <table align=center cellpadding=0 cellspacing=0 height=5716 width=768>
  <tbody>
  <tr>
    <td height=5716 valign=top width=100%>
      <pre>2005-11-16   <br>HIGHMEM Completed Understanding<br>   mm/highmem.c  include/asm-i386/highmem.h(not mm/highmem.h)<br>   <br>  HighMem 已经不止一次的被提到了.或许本节所述已存于前,还是希望这里有完<br>整的表述.<br>  内核空间从3G开始,如果全部采用"线性映射"(物理地址和逻辑地址只差一个常<br>量 PAGE_OFFSET ),最多管理1G物理内存.现在我使用的电脑就有1G内存(AMD64),<br>我朋友的机器有2G,据说玩游戏巨爽(AMD64300+).显然如果线性映射我的朋友就会<br>浪费1G内存.为了使内核能够访问这些"高端内存",内核使用HighMem.做法是不将<br>内核1G的虚拟地址空间全部映射成物理内存,而是预留一部分给高端内存做临时映<br>射使用.<br>   其实内核不仅仅预留了highmem的地址空间,还给fixmap,vmalloc预留了虚存空<br>间.实际上,系统初始化的时候预留128M虚存,896M用于"直接"映射物理内存.分析<br>arch/i386/mm/init.c的时候已经对此有过详细说明.这里还是将相关部分罗列出<br>来,以便有一个完整的印象.<br>   首先是内核虚存的划分管理图:<br>+------------------------------------------------------------------     <br>|   8K空洞<br>+------------------------------------------------------------------<br>|   FIXADDR_TOP(0xffffe000UL)            (include/asm-i386/fixmap.h)<br>|   fixed map(每项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>|  	   FIX_KMAP_BEGIN,  /* 主要用于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>|   VMALLOC_END	(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>|   VMALLOC_START (((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>    首先是highmem所占用的虚拟内存分成两个部分,一个是FixMap部分为每个cpu<br>都预留了8k的虚存用于kmap_atomic,另外一部分从PKMAP_BASE (0xfe000000UL)开<br>始,预留了4M的虚存.总共能映射1024+2个高端内存页面.<br>    然后看看如何获取页面的虚拟地址:<br>    对于物理地址小于high_memory的页面,采取的是线性映射phys_to_vir有效<br>同样,对于虚拟地址小于virt_to_phys(high_memory)则可以使用宏virt_to_phys.<br> 另外一个获取页面虚拟地址的方式是page-&gt;virtual变量.在初始化page的mem_map<br>数组的时候,free_area_init_core对非ZONE_HIGHMEM的page-&gt;virtual进行了初始<br>化.而ZONE_HIGHMEM则在需要的时候用kmap来映射页面并初始化page-&gt;virtual,或<br>者使用vmalloc. 所以,在任何时候page-&gt;virtual都可代表页面在内核的虚拟地址<br>(只要非0).<br>    其实还有一个 fix_to_virt 用于转换fix map页面.<br>    从虚拟地址向物理地址转换,virt_to_phys适用于虚拟地址小于virt_to_phys<br>(high_memory)的范围,前面刚刚说过.还有一个是从pte中获取页面nr,从nr转换成<br>物理地址. 从pte获取页面物理地址的方式是任何时候都不会出错的.所以在处理缺<br>页的时候,"都是"从pte转换. 因为内核尽量给用户分配highmem.所以virt_to_phys<br>不能工作. 这里的差别应该仔细体会.<br>   <br>  I)include/asm-i386/highmem.h 的几个重要的全局变量和宏定义:<br>  <br>/* ref. setup_arch  line 720*/<br>/* highstart_pfn:高端内存起始页面的page fram num<br> * highend_pfn  :高端内存终止页面的page fram num<br> */<br>extern unsigned long highstart_pfn, highend_pfn;<br><br>/*<br> * kmap_pte: ref kmap_init,为kmap_atomic预留的两个fixmap页面的起始pte<br> * kmap_prot: i386下初始化为PAGE_KERNEL<br> */<br>extern pte_t *kmap_pte;<br>extern pgprot_t kmap_prot;<br>/* <br> * pkmap_page_table: 内核为kmap预留了4M虚存,正好一整页的pte(one page <br> * table) pgd,pmd,page table在内核初始化的时候都分配好了,只差将pte置<br> * 为某个page pfn<br> */<br>extern pte_t *pkmap_page_table;<br><br>    参考一下对arch/i386/mm/init.c的分析,fixmap和pkmap对page dir,pmd,<br>包括page table都分配了页面,只有page table中的pte未设置.<br><br>#define PKMAP_BASE (0xfe000000UL)<br>                    /*<br>	                   * kmap 使用 从PKMAP_BASE (0xfe000000UL)<br>                     * 开始的4M 空间,不幸的是和VMALLOC_START-VMALLOC_END<br>                     * 有重叠 linux2.6 已经修正了这个错误<br>                     * kmap_atomic 使用 FIX_KMAP_BEGIN的一段虚存,见 fixmap.h<br>                     */<br>#ifdef CONFIG_X86_PAE<br>#define LAST_PKMAP 512<br>#else<br>#define LAST_PKMAP 1024 /*4M虚存最多映射1k页面,叫max kmaped pages更合适*/<br>#endif<br>#define LAST_PKMAP_MASK (LAST_PKMAP-1)<br><br>/*将virt转换成其所位于的4M kmap虚存页的序号*/<br>#define PKMAP_NR(virt)  ((virt-PKMAP_BASE) &gt;&gt; PAGE_SHIFT) <br>/*pkmap nr 到虚拟地址*/<br>#define PKMAP_ADDR(nr)  (PKMAP_BASE + ((nr) &lt;&lt; PAGE_SHIFT))<br> <br>       <br>  II) mm/memory.c 中相关的两个全局变量<br>  <br>/*从虚拟地址 high_memory 开始不再是线性映射区<br> *如果全部可以线性映射其值为物理内存顶端之虚拟地址<br> */<br>void * high_memory;  <br>struct page *highmem_start_page; /*高端物理内存的起始 page(struct page)*/<br><br>  III)mm/highmem.c的全局量<br>  /*<br> * Virtual_count is not a pure "count".<br> *  0 means that it is not mapped, and has not been mapped<br> *    since a TLB flush - it is usable.<br> *  1 means that there are no users, but it has been mapped<br> *    since the last TLB flush - so we can't use it.<br> *  n means that there are (n-1) current users of it.<br> */<br>static int pkmap_count[LAST_PKMAP];<br>static unsigned int last_pkmap_nr; /*上一次分配出去的虚存页面的kmapnr*/<br> <br>      数组pkmap_count管理为kmap准备的4M虚存,LAST_PKMAP为1K,正好管理1k个<br>虚存页面.last_pkmap_nr记录上次分配出去的虚存页面之nr.<br><br>   IV)highmem的接口函数<br>highmem.h(include/asm-i386)<br>static inline void *kmap(struct page *page)<br>{<br>	if (in_interrupt())<br>		BUG();<br>	if (page &lt; highmem_start_page) /*如果企图映射非highmem页面,直接返回其*/<br>		return page_address(page); /*线性映射地址*/<br>	return kmap_high(page); /*否则寻找一个未用虚存页,设置pte,完成映射*/<br>}   <br>kmap_high检查page-&gt;virtual如果非0证明已经映射过,否则用map_new_virtual建立<br>真正的映射,如果没有发现可用虚存则等待,kumap的时候会唤醒等待进程.<br>   相关代码很是直白,有问题再讨论吧.<br>   <br>   kmap_atomic使用fixmap中保留的两个虚存页面,提供了可以在irq环境中使用的<br>highmem访问接口.<br>   另外注意__GFP_HIGH不是要分配highmem的内存,__GFP_HIGHMEM才是,看看<br>struct buffer_head * create_bounce(int rw, struct buffer_head * bh_orig)<br>不要把这两个东西搞混了.create_bounce为处于highmem的bh分配一个非highmem的<br>内存页面,并继承bh其他的所有东西.在io完成之前后负责在highmem和这个跳板页<br>面直接复制数据. 或许是dma不能处理himem故需要这个跳板(should be this).<br>  看看什么情况下对HigMemPage进行io:<br>  内核尽量给用户分配highmem页面,在某种情况下需要将数据写到这些页面.比如用<br>户将文件mmap到内存,然后内核为这些页面分配了Highmem Page,现在需要读入数据.<br>直接看block_read_full_page,<br>............<br>			if (!buffer_mapped(bh)) {<br>				memset(kmap(page) + i*blocksize, 0, blocksize);<br>				flush_dcache_page(page);<br>				kunmap(page);<br>				set_bit(BH_Uptodate, &amp;bh-&gt;b_state);<br>				continue;<br>			}<br>...............<br>    可以看到kmap,映射高端内存的操作.证明这个流程需要处理Highmem Page.看<br>submit_bh-&gt;generic_make_request-&gt;q-&gt;make_request_fn这个函数度于ide就是<br>__make_request(见函数blk_init_queue):<br>#if CONFIG_HIGHMEM<br>	bh = create_bounce(rw, bh);<br>#endif<br>     利用了highmem提供的这个跳板.<br>     <br>    所有其他函数这里不再讨论了.<br>   <br>   <br><br>   <br>   <br>    <br></pre>
    </td>
  </tr>
  </tbody>
</table></body></html>

⌨️ 快捷键说明

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