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

简介:
 
 

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 plain  copy
 
  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 plain  copy
 
  1. /* asm/page.h */  
  2. typedef unsigned long pteval_t;  
  3.   
  4. typedef pteval_t pte_t;  
  5. typedef unsigned long pmd_t;  
  6. typedef unsigned long pgd_t[2];  
  7. typedef unsigned long pgprot_t;  
  8.   
  9. #define pte_val(x)      (x)  
  10. #define pmd_val(x)      (x)  
  11. #define pgd_val(x)  ((x)[0])  
  12. #define pgprot_val(x)   (x)  
  13.   
  14. #define __pte(x)        (x)  
  15. #define __pmd(x)        (x)  
  16. #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 plain  copy
 
  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 plain  copy
 
  1. /* to find an entry in a page-table-directory */  
  2. #define pgd_index(addr)     ((addr) >> PGDIR_SHIFT)  //获得在pgd表中的索引  
  3.   
  4. #define pgd_offset(mm, addr)    ((mm)->pgd + pgd_index(addr)) //获得pmd表的起始地址  
  5.   
  6. /* to find an entry in a kernel page-table-directory */  
  7. #define pgd_offset_k(addr)  pgd_offset(&init_mm, addr)  

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

[cpp]  view plain  copy
 
  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 plain  copy
 
  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.   
  9. #define pte_index(addr)     (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))  
  10.   
  11. #define pte_offset_kernel(pmd,addr) (pmd_page_vaddr(*(pmd)) + pte_index(addr))  
  12.   
  13. #define pte_offset_map(pmd,addr)    (__pte_map(pmd) + pte_index(addr))  
  14. #define pte_unmap(pte)          __pte_unmap(pte)  
  15.   
  16. #define pte_pfn(pte)        (pte_val(pte) >> PAGE_SHIFT)  
  17. #define pfn_pte(pfn,prot)   __pte(__pfn_to_phys(pfn) | pgprot_val(prot))  
  18.   
  19. #define pte_page(pte)       pfn_to_page(pte_pfn(pte))  
  20. #define mk_pte(page,prot)   pfn_pte(page_to_pfn(page), prot)  
  21.   
  22. #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)  
  23. #define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)  

        其示意图如下图所示:

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

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

 

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






















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/5660210.html,如需转载请自行联系原作者

相关文章
|
2月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
177 6
|
10天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
16天前
|
存储 缓存 监控
|
28天前
|
Java 程序员 编译器
内存分配的基本概念
【10月更文挑战第10天】
31 4
|
1月前
|
存储 缓存 监控
Linux中内存和性能问题
【10月更文挑战第5天】
38 4
|
1月前
|
算法 Linux
Linux中内存问题
【10月更文挑战第6天】
41 2
|
13天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
22 0
|
1月前
|
存储 缓存 固态存储
|
1月前
|
Ubuntu Java Linux
Linux操作系统——概念扫盲I
Linux操作系统——概念扫盲I
42 4
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。