转自:http://blog.csdn.net/myarrow/article/details/8624687

1. Linux物理内存三级架构

对于内存管理,Linux采用了与具体体系架构不相关的设计模型,实现了良好的可伸缩性。它主要由内存节点node、内存区域zone和物理页框page三级架构组成。

    • 内存节点node

内存节点node是计算机系统中对物理内存的一种描述方法,一个总线主设备访问位于同一个节点中的任意内存单元所花的代价相同,而访问任意两个不同节点中的内存单元所花的代价不同。在一致存储结构(Uniform Memory Architecture,简称UMA)计算机系统中只有一个节点,而在非一致性存储结构(NUMA)计算机系统中有多个节点。Linux内核中使用数据结构pg_data_t来表示内存节点node。如常用的ARM架构为UMA架构。

 •  内存区域zone

内存区域位于同一个内存节点之内,由于各种原因它们的用途和使用方法并不一样。如基于IA32体系结构的个人计算机系统中,由于历史原因使得ISA设备只能使用最低16MB来进行DMA传输。又如,由于Linux内核采用

•  物理页框page

2. Linux虚拟内存三级页表

Linux虚拟内存三级管理由以下三级组成:

• PGD: Page Global Directory (页目录)

• PMD: Page Middle Directory (页目录)

• PTE:  Page Table Entry  (页表项)

每一级有以下三个关键描述宏:

• SHIFT

• SIZE

• MASK

如页的对应描述为:

[cpp] view plaincopy
  1. /* PAGE_SHIFT determines the page size  asm/page.h */
  2. #define PAGE_SHIFT      12
  3. #define PAGE_SIZE       (_AC(1,UL) << PAGE_SHIFT)
  4. #define PAGE_MASK       (~(PAGE_SIZE-1))

数据结构定义如下:

[cpp] view plaincopy
  1. /* asm/page.h */
  2. typedef unsigned long pteval_t;
  3. typedef pteval_t pte_t;
  4. typedef unsigned long pmd_t;
  5. typedef unsigned long pgd_t[2];
  6. typedef unsigned long pgprot_t;
  7. #define pte_val(x)      (x)
  8. #define pmd_val(x)      (x)
  9. #define pgd_val(x)  ((x)[0])
  10. #define pgprot_val(x)   (x)
  11. #define __pte(x)        (x)
  12. #define __pmd(x)        (x)
  13. #define __pgprot(x)     (x)

2.1 Page Directory (PGD and PMD)

每个进程有它自己的PGD( Page Global Directory),它是一个物理页,并包含一个pgd_t数组。其定义见<asm/page.h>。 进程的pgd_t数据见 task_struct -> mm_struct -> pgd_t * pgd;

ARM架构的PGD和PMD的定义如下<arch/arm/include/asm/pgtable.h>:

[cpp] view plaincopy
  1. <p>#define PTRS_PER_PTE  512    // PTE中可包含的指针<u32>数 (21-12=9bit)
  2. #define PTRS_PER_PMD  1
  3. #define PTRS_PER_PGD  2048   // PGD中可包含的指针<u32>数 (32-21=11bit)</p><p>#define PTE_HWTABLE_PTRS (PTRS_PER_PTE)
  4. #define PTE_HWTABLE_OFF  (PTE_HWTABLE_PTRS * sizeof(pte_t))
  5. #define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u32))</p><p>/*
  6. * PMD_SHIFT determines the size of the area a second-level page table can map
  7. * PGDIR_SHIFT determines what a third-level page table entry can map
  8. */
  9. #define PMD_SHIFT  21
  10. #define PGDIR_SHIFT  21</p>
  11. <span style="font-size:18px;">     虚拟地址SHIFT宏图:</span>

虚拟地址MASK和SIZE宏图:

2.2 Page Table Entry

PTEs, PMDs和PGDs分别由pte_t, pmd_t 和pgd_t来描述。为了存储保护位,pgprot_t被定义,它拥有相关的flags并经常被存储在page table entry低位(lower bits),其具体的存储方式依赖于CPU架构。

每个pte_t指向一个物理页的地址,并且所有的地址都是页对齐的。因此在32位地址中有PAGE_SHIFT(12)位是空闲的,它可以为PTE的状态位。

PTE的保护和状态位如下图所示:

2.3 如何通过3级页表访问物理内存

为了通过PGD、PMD和PTE访问物理内存,其相关宏在asm/pgtable.h中定义。

• pgd_offset

根据当前虚拟地址和当前进程的mm_struct获取pgd项的宏定义如下:

[cpp] view plaincopy
  1. /* to find an entry in a page-table-directory */
  2. #define pgd_index(addr)     ((addr) >> PGDIR_SHIFT)  //获得在pgd表中的索引
  3. #define pgd_offset(mm, addr)    ((mm)->pgd + pgd_index(addr)) //获得pmd表的起始地址
  4. /* to find an entry in a kernel page-table-directory */
  5. #define pgd_offset_k(addr)  pgd_offset(&init_mm, addr)

• pmd_offset
             根据通过pgd_offset获取的pgd 项和虚拟地址,获取相关的pmd项(即pte表的起始地址)

[cpp] view plaincopy
  1. /* Find an entry in the second-level page table.. */
  2. #define pmd_offset(dir, addr)   ((pmd_t *)(dir))   //即为pgd项的值

• pte_offset

根据通过pmd_offset获取的pmd项和虚拟地址,获取相关的pte项(即物理页的起始地址)

[cpp] view plaincopy
  1. #ifndef CONFIG_HIGHPTE
  2. #define __pte_map(pmd)      pmd_page_vaddr(*(pmd))
  3. #define __pte_unmap(pte)    do { } while (0)
  4. #else
  5. #define __pte_map(pmd)      (pte_t *)kmap_atomic(pmd_page(*(pmd)))
  6. #define __pte_unmap(pte)    kunmap_atomic(pte)
  7. #endif
  8. #define pte_index(addr)     (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
  9. #define pte_offset_kernel(pmd,addr) (pmd_page_vaddr(*(pmd)) + pte_index(addr))
  10. #define pte_offset_map(pmd,addr)    (__pte_map(pmd) + pte_index(addr))
  11. #define pte_unmap(pte)          __pte_unmap(pte)
  12. #define pte_pfn(pte)        (pte_val(pte) >> PAGE_SHIFT)
  13. #define pfn_pte(pfn,prot)   __pte(__pfn_to_phys(pfn) | pgprot_val(prot))
  14. #define pte_page(pte)       pfn_to_page(pte_pfn(pte))
  15. #define mk_pte(page,prot)   pfn_pte(page_to_pfn(page), prot)
  16. #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
  17. #define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)

其示意图如下图所示:

2.4 根据虚拟地址获取物理页的示例代码

根据虚拟地址获取物理页的示例代码详见<mm/memory.c中的函数follow_page>。

[cpp] view plaincopy
  1. /**
  2. * follow_page - look up a page descriptor from a user-virtual address
  3. * @vma: vm_area_struct mapping @address
  4. * @address: virtual address to look up
  5. * @flags: flags modifying lookup behaviour
  6. *
  7. * @flags can have FOLL_ flags set, defined in <linux/mm.h>
  8. *
  9. * Returns the mapped (struct page *), %NULL if no mapping exists, or
  10. * an error pointer if there is a mapping to something not represented
  11. * by a page descriptor (see also vm_normal_page()).
  12. */
  13. struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
  14. unsigned int flags)
  15. {
  16. pgd_t *pgd;
  17. pud_t *pud;
  18. pmd_t *pmd;
  19. pte_t *ptep, pte;
  20. spinlock_t *ptl;
  21. struct page *page;
  22. struct mm_struct *mm = vma->vm_mm;
  23. page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
  24. if (!IS_ERR(page)) {
  25. BUG_ON(flags & FOLL_GET);
  26. goto out;
  27. }
  28. page = NULL;
  29. pgd = pgd_offset(mm, address);
  30. if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
  31. goto no_page_table;
  32. pud = pud_offset(pgd, address);
  33. if (pud_none(*pud))
  34. goto no_page_table;
  35. if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
  36. BUG_ON(flags & FOLL_GET);
  37. page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE);
  38. goto out;
  39. }
  40. if (unlikely(pud_bad(*pud)))
  41. goto no_page_table;
  42. pmd = pmd_offset(pud, address);
  43. if (pmd_none(*pmd))
  44. goto no_page_table;
  45. if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
  46. BUG_ON(flags & FOLL_GET);
  47. page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE);
  48. goto out;
  49. }
  50. if (pmd_trans_huge(*pmd)) {
  51. if (flags & FOLL_SPLIT) {
  52. split_huge_page_pmd(mm, pmd);
  53. goto split_fallthrough;
  54. }
  55. spin_lock(&mm->page_table_lock);
  56. if (likely(pmd_trans_huge(*pmd))) {
  57. if (unlikely(pmd_trans_splitting(*pmd))) {
  58. spin_unlock(&mm->page_table_lock);
  59. wait_split_huge_page(vma->anon_vma, pmd);
  60. } else {
  61. page = follow_trans_huge_pmd(mm, address,
  62. pmd, flags);
  63. spin_unlock(&mm->page_table_lock);
  64. goto out;
  65. }
  66. } else
  67. spin_unlock(&mm->page_table_lock);
  68. /* fall through */
  69. }
  70. split_fallthrough:
  71. if (unlikely(pmd_bad(*pmd)))
  72. goto no_page_table;
  73. ptep = pte_offset_map_lock(mm, pmd, address, &ptl);
  74. pte = *ptep;
  75. if (!pte_present(pte))
  76. goto no_page;
  77. if ((flags & FOLL_WRITE) && !pte_write(pte))
  78. goto unlock;
  79. page = vm_normal_page(vma, address, pte);
  80. if (unlikely(!page)) {
  81. if ((flags & FOLL_DUMP) ||
  82. !is_zero_pfn(pte_pfn(pte)))
  83. goto bad_page;
  84. page = pte_page(pte);
  85. }
  86. if (flags & FOLL_GET)
  87. get_page(page);
  88. if (flags & FOLL_TOUCH) {
  89. if ((flags & FOLL_WRITE) &&
  90. !pte_dirty(pte) && !PageDirty(page))
  91. set_page_dirty(page);
  92. /*
  93. * pte_mkyoung() would be more correct here, but atomic care
  94. * is needed to avoid losing the dirty bit: it is easier to use
  95. * mark_page_accessed().
  96. */
  97. mark_page_accessed(page);
  98. }
  99. if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) {
  100. /*
  101. * The preliminary mapping check is mainly to avoid the
  102. * pointless overhead of lock_page on the ZERO_PAGE
  103. * which might bounce very badly if there is contention.
  104. *
  105. * If the page is already locked, we don't need to
  106. * handle it now - vmscan will handle it later if and
  107. * when it attempts to reclaim the page.
  108. */
  109. if (page->mapping && trylock_page(page)) {
  110. lru_add_drain();  /* push cached pages to LRU */
  111. /*
  112. * Because we lock page here and migration is
  113. * blocked by the pte's page reference, we need
  114. * only check for file-cache page truncation.
  115. */
  116. if (page->mapping)
  117. mlock_vma_page(page);
  118. unlock_page(page);
  119. }
  120. }
  121. unlock:
  122. pte_unmap_unlock(ptep, ptl);
  123. out:
  124. return page;
  125. bad_page:
  126. pte_unmap_unlock(ptep, ptl);
  127. return ERR_PTR(-EFAULT);
  128. no_page:
  129. pte_unmap_unlock(ptep, ptl);
  130. if (!pte_none(pte))
  131. return page;
  132. no_page_table:
  133. /*
  134. * When core dumping an enormous anonymous area that nobody
  135. * has touched so far, we don't want to allocate unnecessary pages or
  136. * page tables.  Return error instead of NULL to skip handle_mm_fault,
  137. * then get_dump_page() will return NULL to leave a hole in the dump.
  138. * But we can only make this optimization where a hole would surely
  139. * be zero-filled if handle_mm_fault() actually did handle it.
  140. */
  141. if ((flags & FOLL_DUMP) &&
  142. (!vma->vm_ops || !vma->vm_ops->fault))
  143. return ERR_PTR(-EFAULT);
  144. return page;
  145. }

Linux内存管理--基本概念【转】相关推荐

  1. linux内存管理基本概念

    1. Linux物理内存三级架构 对于内存管理,Linux采用了与具体体系架构不相关的设计模型,实现了良好的可伸缩性.它主要由内存节点node.内存区域zone和物理页框page三级架构组成.     ...

  2. linux内存管理的主要概念是虚拟内存,有关linux内存管理机制的相关内容,linux物理内存和虚拟内存,深入了解Linux内存运行 ......

    在linux中空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然. 这是Linux内存管理的一个优秀特性,区别于Windows的内存管理. 主要特点: 无论物理内存有多大,L ...

  3. Linux内存管理之基本概念介绍(一)

    Linux内存管理之基本概念介绍(一) Linux内存管理之物理内存管理(二) Linux内存管理之内存管理单元(MMU)(三) Linux内存管理之分配掩码(四) Linux内存管理之伙伴系统(五) ...

  4. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

  5. Linux内存管理原理【转】

    转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...

  6. Linux内存管理【转】

    转自:http://www.cnblogs.com/wuchanming/p/4360264.html 转载:http://www.kerneltravel.net/journal/v/mem.htm ...

  7. Linux内存管理原理

    本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...

  8. Linux内存管理之一 分段与分页

    Linux内存管理之一 分段与分页 Posted on 2012-07-19 21:22 sin 阅读(3130) 评论(0)  编辑 收藏 引用 所属分类: Linux编程 现代操作系统的内存管理机 ...

  9. 闲聊Linux内存管理(1)

    今天还有时间,之前一直想抽时间来写写Linux内核原理相关的东西,关注点不在代码,而在于内在的原理和机制,让大家对Linux内核有个总体的感性认识,个人认为这很有必要,把看似复杂.深不可测的内核实现, ...

最新文章

  1. pandas数据清洗(缺失值、异常值和重复值处理)
  2. 聊聊linux查看服务和端口状态命令netstat
  3. 文科生也会搭的微信个人号后台
  4. 控件无法安装,windows已经阻止此软件因为无法验证发行者
  5. RecycleView Layout 详解
  6. Oracle10gr2 开机自启动脚本
  7. c语言 2D-FFT(fft2)及IFFT
  8. 原子变量、锁、内存屏障,写得非常好!
  9. golang 将word转为pdf_如何将word转化为pdf格式
  10. php循环给数组每个值加引号,php数组非数字键名加引号的必要性!
  11. VTK:可视化之TextureMapQuad
  12. delete 和 delete [] 的真正区别
  13. (转) 穿越NAT总结
  14. background属性及其应用
  15. request模块发送json请求
  16. 数字电路与逻辑设计——组合逻辑篇
  17. 个人陈述 计算机专业,研究生个人陈述范例,计算机专业
  18. 卧槽!百度网盘体验版流出,竟然不限速。速度6MB/s!(附下载链接)
  19. python爬虫学习笔记-scrapy框架之start_url
  20. Java实现21点多人游戏(期末作业)

热门文章

  1. 一个简单的适用于Vue的下拉刷新,触底加载组件
  2. 《抓住“三伏天”习武健身的黄金季节》--胡俭雷
  3. Springboot 整合 Mybatis 的完整 Web 案例
  4. 浅谈linux性能调优之六:IO调度算法的选择
  5. web开发中的JAVA字符转码
  6. 二手轻型载货车报价图片_业主坐地提价, 新房抢客, 10月广州二手房成交跌了24%...
  7. mysql 多数据源_SpringBoot+多数据源(MySQL)
  8. Flink 读取 Mysql
  9. Oracle ora-15070,查询字段过多触发了Oracle的BUG?【ORA-01465: 无效的十六进制数字】...
  10. mariadb用户群体mysql_mysql(mariadb)新建用户及用户授权管理