Linux内存管理--基本概念【转】

转自: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. }
时间: 2024-08-19 03:05:44

Linux内存管理--基本概念【转】的相关文章

Linux内存管理基本概念

1. 基本概念 1.1 地址 (1)逻辑地址:指由程序产生的与段相关的偏移地址部分.在C语言指针中,读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址.(2)线性地址:段中的偏移地址(逻辑地址),加上相应段的基地址就生成了一个线性地址.(3)物理地址: 放在寻址总线上的地址.(4)虚拟地址:保护模式下段和段内偏移量组成的地址,而逻辑地址就是代码段内偏移量,或称进程的逻辑地址. 1.2 内存 (1) 虚拟内存:计算机系统内存管理的一种技术.它使得应用程序认为它

Linux内存管理机制

一.首先大概了解一下计算机CPU.Cache.内存.硬盘之间的关系及区别. 1.  CPU也称为中央处理器(CPU,Central Processing Unit)是一块超大规模的集成电路, 是一台计算机的运算核心(Core)和控制核心( Control Unit).它的功能主要是解释计算机指令以及处理计算机软件中的数据.中央处理器主要由三核心部件组成,运算器.控制器和总线(BUS),运算器又主要由算术逻辑单元(ALU)和寄存器(RS)组成. 2.Cache即高速缓冲存储器,是位于CPU与主内存

Linux内存管理 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4491368.html Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理之间的关系,希望大家最终能驾驭Linux内存管理. 前言 内存管理一向是所有操作系统书

linux内存管理---物理地址、线性地址、虚拟地址、逻辑地址之间的转换

linux内存管理---虚拟地址.逻辑地址.线性地址.物理地址的区别(一) 这篇文章中介绍了四个名词的概念,下面针对四个地址的转换进行分析 CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步(如下图): 首先,将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址, 其次,再利用其页式内存管理单元,转换为最终物理地址. 这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程.之所以这样冗余

Linux内存管理1

1.前言 关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识的整理. 本文将主要以X86架构来介绍Linux内存管理的相关知识. 2. 内存寻址 内存寻址是操作系统设计的硬件基础之一 操作系统是横跨软件和硬件的桥梁 操作系统设计者必须在硬件相关代码和硬件无关代码之间划分清晰的界限,以便操作系统很容易的移植到不同的平台 内存寻址的不同时期 (1)石器时代---8位寻址:4004是4位寻址,8080是8位寻址,由一个主累加器(寄存器A)和6个次累加器(寄存器B,C,D,E,H和L),没有段

Python学习第六天----Linux内存管理、进程管理、RPM包安装管理及源码安装软件

Linux内存管理.进程管理.RPM包安装管理及源码安装软件 一.交换分区     交换分区其实就相当于Windows系统下的虚拟内存的概念,当物理内存不够用的时候,由操作系统将硬盘的一块区域划分出来作为内存使用.具体使用方法如下:      [[email protected] ~]# fdisk -l 磁盘 /dev/sdb:16.1 GB, 16106127360 字节,31457280 个扇区 Units = 扇区 of 1 * 512 = 512 bytes 扇区大小(逻辑/物理):5

linux内存管理---虚拟地址、逻辑地址、线性地址、物理地址的区别(一)

分析linux内存管理机制,离不了上述几个概念,在介绍上述几个概念之前,先从<深入理解linux内核>这本书中摘抄几段关于上述名词的解释: 一.<深入理解linux内核>的解释 逻辑地址(Logical Address) 包含在机器语言指令中用来指定一个操作数或一条指令的地址(有点深奥).这种寻址方式在80x86著名的分段结构中表现得尤为具体,它促使windows程序员把程序分成若干段.每个逻辑地址都由一个段和偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离. 线性地址(

Linux内存管理介绍

linux内存管理概述 内存管理的目标: 提供一种方法,在各种目的各个用户之间实现内存共享,应该实现以下两个功能: 1.最小化管理内存的时间,内存申请和释放响应时间短 2.最优化用于一般应用的可用内存,内存管理(算法)所占用的内存少,浪费的内存少(内存碎片少) 下图为内存分配器的关系: 1.kmalloc用于分配一块以字节数为单位的内存,所分配的内存物理地址是连续的 void *kmalloc(size_t size, gfp_t flags); size > SLUB_MAX_SIZE(2*P

关于linux内存管理

 Linux的内存管理主要分为两部分:物理地址到虚拟地址的映射,内核内存分配管理(主要基于slab). 物理地址到虚拟地址之间的映射 1.概念 物理地址(physical address) 用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相相应.--这个概念应该是这几个概念中最好理解的一个,可是值得一提的是,尽管能够直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,可是其实,这仅仅是一个硬件提供给软件的抽像,