Linux物理内存管理概述

在内核态申请内存比在用户态申请内存要更为直接,它没有采用用户态那种延迟分配内存技术。内核认为一旦有内核函数申请内存,那么就必须立刻满足该申请内存的请求,并且这个请求一定是正确合理的。相反,对于用户态申请内存的请求,内核总是尽量延后分配物理内存,用户进程总是先获得一个虚拟内存区的使用权,最终通过缺页异常获得一块真正的物理内存。

1.物理内存的内核映射
IA32架构中内核虚拟地址空间只有1GB大小(从3GB到4GB),因此可以直接将1GB大小的物理内存(即常规内存)映射到内核地址空间,但超出1GB大小的物理内存(即高端内存)就不能映射到内核空间。为此,内核采取了下面的方法使得内核可以使用所有的物理内存。
1).高端内存不能全部映射到内核空间,也就是说这些物理内存没有对应的线性地址。不过,内核为每个物理页框都分配了对应的页框描述符,所有的页框描述符都保存在mem_map数组中,因此每个页框描述符的线性地址都是固定存在的。内核此时可以使用alloc_pages()和alloc_page()来分配高端内存,因为这些函数返回页框描述符的线性地址。
2).内核地址空间的后128MB专门用于映射高端内存,否则,没有线性地址的高端内存不能被内核所访问。这些高端内存的内核映射显然是暂时映射的,否则也只能映射128MB的高端内存。当内核需要访问高端内存时就临时在这个区域进行地址映射,使用完毕之后再用来进行其他高端内存的映射。
由于要进行高端内存的内核映射,因此直接能够映射的物理内存大小只有896MB,该值保存在high_memory中。内核地址空间的线性地址区间如下图所示:



从图中可以看出,内核采用了三种机制将高端内存映射到内核空间:永久内核映射,固定映射和vmalloc机制。

2.物理内存管理机制
基于物理内存在内核空间中的映射原理,物理内存的管理方式也有所不同。内核中物理内存的管理机制主要有buddy伙伴算法,slab高速缓存和vmalloc机制。其中buddy伙伴算法和slab高速缓存都在物理内存映射区分配物理内存,而vmalloc机制则在高端内存映射区分配物理内存。
伙伴算法
伙伴算法负责大块连续物理内存的分配和释放,以页框为基本单位。该机制可以避免外部碎片。
per-CPU页框高速缓存
内核经常请求和释放单个页框,该缓存包含预先分配的页框,用于满足本地CPU发出的单一页框请求。
slab缓存
slab缓存负责小块物理内存的分配,并且它也作为高速缓存,主要针对内核中经常分配并释放的对象。
vmalloc机制
vmalloc机制使得内核通过连续的线性地址来访问非连续的物理页框,这样可以最大限度的使用高端物理内存。

3.物理内存的分配
内核发出内存申请的请求时,根据内核函数调用接口将启用不同的内存分配器。
3.1 分区页框分配器
分区页框分配器 (zoned page frame allocator) ,处理对连续页框的内存分配请求。分区页框管理器分为两大部分:前端的管理区分配器和伙伴系统,如下图:



管理区分配器负责搜索一个能满足请求页框块大小的管理区。在每个管理区中,具体的页框分配工作由伙伴系统负责。为了达到更好的系统性能,单个页框的申请工作直接通过per-CPU页框高速缓存完成。
该分配器通过几个函数和宏来请求页框,它们之间的封装关系如下图所示。



这些函数和宏将核心的分配函数__alloc_pages_nodemask()封装,形成满足不同分配需求的分配函数。其中,alloc_pages()系列函数返回物理内存首页框描述符,__get_free_pages()系列函数返回内存的线性地址。
3.2 slab分配器
slab 分配器最初是为了解决物理内存的内部碎片而提出的,它将内核中常用的数据结构看做对象。slab分配器为每一种对象建立高速缓存。内核对该对象的分配和释放均是在这块高速缓存中操作。一种对象的slab分配器结构图如下:



可以看到每种对象的高速缓存是由若干个slab组成,每个slab是由若干个页框组成的。虽然slab分配器可以分配比单个页框更小的内存块,但它所需的所有内存都是通过伙伴算法分配的。
slab高速缓存分专用缓存和通用缓存。专用缓存是对特定的对象,比如为内存描述符创建高速缓存。通用缓存则是针对一般情况,适合分配任意大小的物理内存,其接口即为kmalloc()。
3.3 非连续内存区内存的分配
内核通过vmalloc()来申请非连续的物理内存,若申请成功,该函数返回连续内存区的起始地址,否则,返回NULL。vmalloc()和kmalloc()申请的内存有所不同,kmalloc()所申请内存的线性地址与物理地址都是连续的,而vmalloc()所申请的内存线性地址连续而物理地址则是离散的,两个地址之间通过内核页表进行映射。
vmalloc()的工作方式理解起来很简单:
1).寻找一个新的连续线性地址空间;
2).依次分配一组非连续的页框;
3).为线性地址空间和非连续页框建立映射关系,即修改内核页表;

vmalloc()的内存分配原理与用户态的内存分配相似,都是通过连续的虚拟内存来访问离散的物理内存,并且虚拟地址和物理地址之间是通过页表进行连接的,通过这种方式可以有效的使用物理内存。但是应该注意的是,vmalloc()申请物理内存时是立即分配的,因为内核认为这种内存分配请求是正当而且紧急的;相反,用户态有内存请求时,内核总是尽可能的延后,毕竟用户态跟内核态不在一个特权级





1. 基本概念

1.1 地址

(1)逻辑地址:指由程序产生的与段相关的偏移地址部分。在C语言指针中,读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址。

(2)线性地址:段中的偏移地址(逻辑地址),加上相应段的基地址就生成了一个线性地址。

(3)物理地址: 放在寻址总线上的地址。

(4)虚拟地址:保护模式下段和段内偏移量组成的地址,而逻辑地址就是代码段内偏移量,或称进程的逻辑地址。

1.2 内存

(1)虚拟内存:计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。

(2) 物理内存:实际的内存。物理地址被分成离散的单元,成为页(page)。目前大多数系统的页面大小都为4k。

1.3 地址转换

Linux采用段页式管理机制,有两个部件用于地址转换:分段部件和分页部件。

(1) 分段部件:将逻辑地址转换为线性地址。分段提供了隔绝各个代码、数据和堆栈区域的机制,因此多个程序(任务)可以运行在同一个处理器上而不会互相干扰。

(2) 分页部件:将线性地址转换为物理地址(页表和页目录),若没有启用分页机制,那么线性地址直接就是物理地址。

2. 内存分配

Malloc,kmalloc 和vmalloc区别?

(1) kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存。

(2) kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续。

(3) kmalloc申请的内存比较小,一般小于128 K。它是基于slab(内存池)的,以加快小内存申请效率。

3. 常见问题

(1) 调用malloc函数后,OS会马上分配实际的内存空间吗?

答:不会,只会返回一个虚拟地址,待用户要使用内存时,OS会发出一个缺页中断,此时,内存管理模块才会为程序分配真正的内存。

(2) 段式管理和页式管理的优缺点?

在段式存储管理中,将程序的地址空间划分为若干个段(segment),这样每个进程有一个二维的地址空间,相互独立,互不干扰。程序通过分段划分为多个模块,如代码段、数据段、共享段。这样做的优点是:可以分别编写和编译源程序的一个文件,并且可以针对不同类型的段采取不同的保护,也可以按段为单位来进行共享。段式存储管理的优点是:没有内碎片,外碎片可以通过内存紧缩来消除;便于实现内存共享。

在页式存储管理中,将程序的逻辑地址空间划分为固定大小的页(page),而物理内存划分为同样大小的页框(pageframe)。程序加载时,可将任意一页放人内存中任意一个页框,这些页框不必连续,从而实现了离散分配。这种管理方式的优点是,没有外碎片,且一个程序不必连续存放。这样就便于改变程序占用空间的大小。

页式和段式系统有许多相似之处。比如,两者都采用离散分配方式,且都通过地址映射机构来实现地址变换。但概念上两者也有很多区别,主要表现在: [1] 页是信息的物理单位,分页是为了实现离散分配方式,以减少内存的外零头,提高内存的利用率。或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了更好地满足用户的需要。

[2] 页的大小固定且由系统决定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的。段的长度不固定,且决定于用户所编写的程序,通常由编译系统在对源程序进行编译时根据信息的性质来划分。

[3] 页式系统地址空间是一维的,即单一的线性地址空间,程序员只需利用一个标识符,即可表示一个地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址。

(3) Malloc在什么情况下调用mmap?

从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。brk是将数据段(.data)的最高地址指针_edata往高地址推,mmap是在进程的虚拟地址空间中(一般是堆和栈中间)找一块空闲的。这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。

默认情况下,malloc函数分配内存,如果请求内存大于128K(可由M_MMAP_THRESHOLD选项调节),那就不是去推_edata指针了,而是利用mmap系统调用,从堆和栈的中间分配一块虚拟内存。这样子做主要是因为brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的),而mmap分配的内存可以单独释放。

(4) 32位系统,通常情况下,最大虚拟地址和物理地址空间为多少?

不使用PAE情况下,最大虚拟地址和物理地址空间均为4G,若果使用PAE,最大虚拟地址仍为4G,而物理地址空间可变为64G(x86, 32为变36位)。

(5) 怎样实现malloc和free?

Malloc实现可考虑采用buddy算法+slob算法,free类似。

Linux可以支持大量的架构,所以需要用一种与架构无关的方式去描述内存。在linux的内存管理中,我们首先要明确的一个概念就是NUMA(Non-Uniform Memory Access,关于NUMA的介绍可以参考我前面的文章)。很多大型机器都采用NUMA架构,将内存和CPU分为很多组,每一组称为一个节点(node)。节点与节点之间的互相访问,会因为“距离”的不同导致不同的开销。Linux通过struct pglist_data这个结构体来描述节点,对于UMA架构,Linux同样会保留节点的概念,只是整个系统就是一个节点,只需要一个struct pglist_data来描述,它叫作contig_page_data.

struct pglist_data结构描述如下,现在只需了解其中的关键项即可

 

  1. typedef struct pglist_data {  
  2.     struct zone node_zones[MAX_NR_ZONES];          /*节点中的管理区*/  
  3.     struct zonelist node_zonelists[MAX_ZONELISTS]; /*list中zone的顺序代表了分配内存的顺序,前者分配内存失败将会到后者的区域中分配内存*/  
  4.     int nr_zones;                    /*节点中管理区的数目,不一定为3个,有的节点中可能不存在ZONE_DMA*/  
  5. #ifdef CONFIG_FLAT_NODE_MEM_MAP          /* means !SPARSEMEM */  
  6.     struct page *node_mem_map;  
  7. #ifdef CONFIG_CGROUP_MEM_RES_CTLR  
  8.     struct page_cgroup *node_page_cgroup;  
  9. #endif  
  10. #endif  
  11.     struct bootmem_data *bdata;  
  12. #ifdef CONFIG_MEMORY_HOTPLUG  
  13.     /* 
  14.      * Must be held any time you expect node_start_pfn, node_present_pages 
  15.      * or node_spanned_pages stay constant.  Holding this will also 
  16.      * guarantee that any pfn_valid() stays that way. 
  17.      * 
  18.      * Nests above zone->lock and zone->size_seqlock. 
  19.      */  
  20.     spinlock_t node_size_lock;  
  21. #endif  
  22.     unsigned long node_start_pfn;     /*该节点的起始页框编号*/  
  23.     unsigned long node_present_pages; /* total number of physical pages */  
  24.     unsigned long node_spanned_pages; /* total size of physical page 
  25.                          range, including holes */  
  26.     int node_id;                      /*节点标识符,代表当前节点是系统中的第几个节点*/  
  27.     wait_queue_head_t kswapd_wait;    /*页换出进程使用的等待队列*/  
  28.     struct task_struct *kswapd;       /*指向页换出进程的进程描述符*/  
  29.     int kswapd_max_order;             /*kswapd将要创建的空闲块的大小取对数的值*/  




       每个节点的内存会被分为几个块,我们称之为管理区(zone),对于一个管理区,我们使用struct zone结构体来描述。管理区的类型可以分为ZONE_NORMAL,ZONE_DMA,ZONE_HIGHMEM三种。这三个管理区在物理内存上的布局为

ZONE_DMA:  0~16MB

ZONE_NORMAL:  16MB~896MB

ZONE_HIGHMEM:896MB~end   

 

struct zone结构描述如下:

  1. struct zone {  
  2.     /* Fields commonly accessed by the page allocator */  
  3.   
  4.     /* zone watermarks, access with *_wmark_pages(zone) macros */  
  5.     unsigned long watermark[NR_WMARK];/*该管理区的三个水平线值,min,low,high*/  
  6.   
  7.     /* 
  8.      * When free pages are below this point, additional steps are taken 
  9.      * when reading the number of free pages to avoid per-cpu counter 
  10.      * drift allowing watermarks to be breached 
  11.      */  
  12.     unsigned long percpu_drift_mark;  
  13.   
  14.     /* 
  15.      * We don't know if the memory that we're going to allocate will be freeable 
  16.      * or/and it will be released eventually, so to avoid totally wasting several 
  17.      * GB of ram we must reserve some of the lower zone memory (otherwise we risk 
  18.      * to run OOM on the lower zones despite there's tons of freeable ram 
  19.      * on the higher zones). This array is recalculated at runtime if the 
  20.      * sysctl_lowmem_reserve_ratio sysctl changes. 
  21.      */  
  22.     unsigned long       lowmem_reserve[MAX_NR_ZONES];  /*每个管理区必须保留的页框数*/  
  23.   
  24. #ifdef CONFIG_NUMA           /*如果定义了NUMA*/  
  25.     int node;           /*该管理区所属节点的节点号*/  
  26.     /* 
  27.      * zone reclaim becomes active if more unmapped pages exist. 
  28.      */  
  29.     unsigned long       min_unmapped_pages;  /*当可回收的页面数大于该变量时,管理区将回收页面*/  
  30.     unsigned long       min_slab_pages;      /*同上,只不过该标准用于slab回收页面中*/  
  31.     struct per_cpu_pageset  *pageset[NR_CPUS];   /*每个CPU使用的页面缓存*/  
  32. #else  
  33.     struct per_cpu_pageset  pageset[NR_CPUS];  
  34. #endif  
  35.     /* 
  36.      * free areas of different sizes 
  37.      */  
  38.     spinlock_t      lock;       /*保护该管理区的自旋锁*/  
  39. #ifdef CONFIG_MEMORY_HOTPLUG  
  40.     /* see spanned/present_pages for more description */  
  41.     seqlock_t       span_seqlock;  
  42. #endif  
  43.     struct free_area    free_area[MAX_ORDER];/*标识出管理区中的空闲页框块*/  
  44.   
  45. #ifndef CONFIG_SPARSEMEM  
  46.     /* 
  47.      * Flags for a pageblock_nr_pages block. See pageblock-flags.h. 
  48.      * In SPARSEMEM, this map is stored in struct mem_section 
  49.      */  
  50.     unsigned long       *pageblock_flags;  
  51. #endif /* CONFIG_SPARSEMEM */  
  52.   
  53.   
  54.     ZONE_PADDING(_pad1_)  
  55.   
  56.     /* Fields commonly accessed by the page reclaim scanner */  
  57.     spinlock_t      lru_lock;     
  58.     struct zone_lru {  
  59.         struct list_head list;  
  60.     } lru[NR_LRU_LISTS];  
  61.       
  62.     struct zone_reclaim_stat reclaim_stat; /*页面回收的状态*/  
  63.   
  64.     /*管理区回收页框时使用的计数器,记录到上一次回收,一共扫过的页框数*/  
  65.     unsigned long       pages_scanned;     /* since last reclaim */  
  66.     unsigned long       flags;         /* zone flags, see below */  
  67.   
  68.     /* Zone statistics */  
  69.     atomic_long_t       vm_stat[NR_VM_ZONE_STAT_ITEMS];  
  70.   
  71.     /* 
  72.      * prev_priority holds the scanning priority for this zone.  It is 
  73.      * defined as the scanning priority at which we achieved our reclaim 
  74.      * target at the previous try_to_free_pages() or balance_pgdat() 
  75.      * invokation. 
  76.      * 
  77.      * We use prev_priority as a measure of how much stress page reclaim is 
  78.      * under - it drives the swappiness decision: whether to unmap mapped 
  79.      * pages. 
  80.      * 
  81.      * Access to both this field is quite racy even on uniprocessor.  But 
  82.      * it is expected to average out OK. 
  83.      */  
  84.     int prev_priority;  
  85.   
  86.     /* 
  87.      * The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on 
  88.      * this zone's LRU.  Maintained by the pageout code. 
  89.      */  
  90.     unsigned int inactive_ratio;  
  91.   
  92.   
  93.     ZONE_PADDING(_pad2_)  
  94.     /* Rarely used or read-mostly fields */  
  95.   
  96.     /* 
  97.      * wait_table       -- the array holding the hash table 
  98.      * wait_table_hash_nr_entries   -- the size of the hash table array 
  99.      * wait_table_bits  -- wait_table_size == (1 << wait_table_bits) 
  100.      * 
  101.      * The purpose of all these is to keep track of the people 
  102.      * waiting for a page to become available and make them 
  103.      * runnable again when possible. The trouble is that this 
  104.      * consumes a lot of space, especially when so few things 
  105.      * wait on pages at a given time. So instead of using 
  106.      * per-page waitqueues, we use a waitqueue hash table. 
  107.      * 
  108.      * The bucket discipline is to sleep on the same queue when 
  109.      * colliding and wake all in that wait queue when removing. 
  110.      * When something wakes, it must check to be sure its page is 
  111.      * truly available, a la thundering herd. The cost of a 
  112.      * collision is great, but given the expected load of the 
  113.      * table, they should be so rare as to be outweighed by the 
  114.      * benefits from the saved space. 
  115.      * 
  116.      * __wait_on_page_locked() and unlock_page() in mm/filemap.c, are the 
  117.      * primary users of these fields, and in mm/page_alloc.c 
  118.      * free_area_init_core() performs the initialization of them. 
  119.      */  
  120.     wait_queue_head_t   * wait_table;   /*进程等待队列的散列表,这些进程正在等待管理区中的某页*/  
  121.     unsigned long       wait_table_hash_nr_entries;   /*散列表数组的大小*/  
  122.     unsigned long       wait_table_bits;              /*散列表数组的大小对2取log的结果*/  
  123.   
  124.     /* 
  125.      * Discontig memory support fields. 
  126.      */  
  127.     struct pglist_data  *zone_pgdat;              /*管理区所属节点*/  
  128.     /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */  
  129.     unsigned long       zone_start_pfn; /*管理区的起始页框号*/  
  130.   
  131.     /* 
  132.      * zone_start_pfn, spanned_pages and present_pages are all 
  133.      * protected by span_seqlock.  It is a seqlock because it has 
  134.      * to be read outside of zone->lock, and it is done in the main 
  135.      * allocator path.  But, it is written quite infrequently. 
  136.      * 
  137.      * The lock is declared along with zone->lock because it is 
  138.      * frequently read in proximity to zone->lock.  It's good to 
  139.      * give them a chance of being in the same cacheline. 
  140.      */  
  141.     unsigned long       spanned_pages;  /*管理区的大小,包括洞*/  
  142.     unsigned long       present_pages;  /*管理区的大小,不包括洞*/  
  143.   
  144.     /* 
  145.      * rarely used fields: 
  146.      */  
  147.     const char      *name; /*指向管理区的名称,为"DMA","NORMAL"或"HighMem"*/  
  148. }  

 

物理内存中的每个页都会有一个与之关联的struct page结构来对其进行描述和跟踪,其结构描述如下

  1. struct page {  
  2.     unsigned long flags;        /* Atomic flags, some possibly 
  3.                      * updated asynchronously */  
  4.     atomic_t _count;        /* Usage count, see below. */  
  5.     union {  
  6.         atomic_t _mapcount; /* Count of ptes mapped in mms, 
  7.                      * to show when page is mapped 
  8.                      * & limit reverse map searches. 
  9.                      */  
  10.         struct {        /* SLUB */  
  11.             u16 inuse;  
  12.             u16 objects;  
  13.         };  
  14.     };  
  15.     union {  
  16.         struct {  
  17.         unsigned long private;      /* Mapping-private opaque data: 
  18.                          * usually used for buffer_heads 
  19.                          * if PagePrivate set; used for 
  20.                          * swp_entry_t if PageSwapCache; 
  21.                          * indicates order in the buddy 
  22.                          * system if PG_buddy is set. 
  23.                          */  
  24.         struct address_space *mapping;  /* If low bit clear, points to 
  25.                          * inode address_space, or NULL. 
  26.                          * If page mapped as anonymous 
  27.                          * memory, low bit is set, and 
  28.                          * it points to anon_vma object: 
  29.                          * see PAGE_MAPPING_ANON below. 
  30.                          */  
  31.         };  
  32. #if USE_SPLIT_PTLOCKS  
  33.         spinlock_t ptl;  
  34. #endif  
  35.         struct kmem_cache *slab;    /* SLUB: Pointer to slab */  
  36.         struct page *first_page;    /* Compound tail pages */  
  37.     };  
  38.     union {  
  39.         pgoff_t index;      /* Our offset within mapping. */  
  40.         void *freelist;     /* SLUB: freelist req. slab lock */  
  41.     };  
  42.     struct list_head lru;       /* Pageout list, eg. active_list 
  43.                      * protected by zone->lru_lock ! 
  44.                      */  
  45.     /* 
  46.      * On machines where all RAM is mapped into kernel address space, 
  47.      * we can simply calculate the virtual address. On machines with 
  48.      * highmem some memory is mapped into kernel virtual memory 
  49.      * dynamically, so we need a place to store that address. 
  50.      * Note that this field could be 16 bits on x86 ... ;) 
  51.      * 
  52.      * Architectures with slow multiplication can define 
  53.      * WANT_PAGE_VIRTUAL in asm/page.h 
  54.      */  
  55. #if defined(WANT_PAGE_VIRTUAL)  
  56.     void *virtual;          /* Kernel virtual address (NULL if 
  57.                        not kmapped, ie. highmem) */  
  58. #endif /* WANT_PAGE_VIRTUAL */  
  59. #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS  
  60.     unsigned long debug_flags;  /* Use atomic bitops on this */  
  61. #endif  
  62.   
  63. #ifdef CONFIG_KMEMCHECK  
  64.     /* 
  65.      * kmemcheck wants to track the status of each byte in a page; this 
  66.      * is a pointer to such a status block. NULL if not tracked. 
  67.      */  
  68.     void *shadow;  
  69. #endif  
  70. };  

 

Node,Zone和Page的关系可以用下图来描述

 

至此,已经对内存管理中的这三个关键数据结构有了一个感性的认识,在后面将会结合具体的代码来逐步深入,选择的代码版本为2.6.32.59~
 


4. 参考资料

Linux Memory Management Notes:

http://vmlinz.is-programmer.com/posts/26540.html

内存段页式管理笔记:

http://chenxu.yo2.cn/articles/linux_memory.html

虚拟地址、线性地址和物理地址的转换:

http://dogking.chinaunix.com/space.php?uid=23208702&do=blog&id=163527

kmalloc、vmalloc、malloc的区别:

http://blog.csdn.net/macrossdzh/article/details/5958368

Linux中的物理和虚拟存储空间布局:

http://book.chinaunix.net/showart.php?id=3266

[百度分享]频繁分配释放内存导致的性能问题的分析

http://topic.csdn.net/u/20100325/16/0b86c0ed-5b8d-4eec-a757-c782ae9a3a35.html

5. 后记

最近面试常被问到内存管理的一些基础知识,特整理此文,以作为这阶段面试的小总结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值