Linux内存管理

原创 2018年04月15日 22:03:10

内核和用户空间不同,不支持简单便捷的内存分配方式,而且处理内存分配错误也绝非易事。因此在深入之前,非常有必要理解内核是如何管理内存的。

1.1.1 页面

内核把物理页作为内存管理的基本单元。体系结构不同,支持的页大小也不同,大多数32位体系结构支持4KB,而64位一般会支持8KB的页。

系统中每一个物理页有一个 struct page,结构体定义在文件:大多数内核(kernel)的操作只使用ZONE_NORMAL区域,系统内存由很多固定大小的内存块组成的,这样的内存块称作为“页”(PAGE),x86体系结构中,page的大小为4096个字节。

  Page结构与物理页相关,而并非与虚拟页相关。是对页的描述是短暂的,因为会存在交换等原因。该结构描述当前时刻相关物理页中存放的东西。目的在于物理内存本身,而不是包含在其中的数据。

页的数据结构对象都保存在mem_map全局数组中,该数组通常被存放在ZONE_NORMAL的首部,或者就在小内存系统中为装入内核映像而预留的区域之后。从载入内核的低地址内存区域的后面内存区域,也就是ZONE_NORMAL开始的地方的内存的页的数据结构对象,都保存在这个全局数组中。

1.1.1.1  分配页

内核提供了请求内存的底层机制,提供了进行访问的几个接口。以页为单位分配内存,定义于include/linux/gfp.h

static inline struct page *

alloc_pages(gfp_t gfp_mask, unsigned int order)

{

        return alloc_pages_current(gfp_mask, order);

}

此外使用page_address函数将页转换成为逻辑地址。

__get_free_pages函数同alloc_pages,不过返回的是逻辑地址。

如果要获取返回的页的内容全为0,可以使用函数get_zeroed_page函数,该函数同__get_free_pages函数,只是将页填充成了0。

底层分配页如下:


       对应的释放函数有:__free_pages,free_pages,free_page。

1.1.1.2  分配字节单位空间

为了获得以字节为单位的一块物理地址连续的内核内存,内核提供函数kmalloc函数,定义在文件include/linux/slab.h

/**

 * kmalloc - allocate memory

 * @size: how many bytes of memory are required.

 * @flags: the type of memory to allocate.

 *

 * kmalloc is the normal method of allocating memory

 * for objects smaller than page size in the kernel.

 *

 * The @flags argument may be one of:

 *

 * %GFP_USER - Allocate memory on behalf of user.  May sleep.

 *

 * %GFP_KERNEL - Allocate normal kernel ram.  May sleep.

 *

 * %GFP_ATOMIC - Allocation will not sleep.  May use emergency pools.

 *   For example, use this inside interrupt handlers.

 *

 * %GFP_HIGHUSER - Allocate pages from high memory.

 *

 * %GFP_NOIO - Do not do any I/O at all while trying to get memory.

 *

 * %GFP_NOFS - Do not make any fs calls while trying to get memory.

 *

 * %GFP_NOWAIT - Allocation will not sleep.

 *

 * %__GFP_THISNODE - Allocate node-local memory only.

 *

 * %GFP_DMA - Allocation suitable for DMA.

 *   Should only be used for kmalloc() caches. Otherwise, use a

 *   slab created with SLAB_DMA.

 *

 * Also it is possible to set different flags by OR'ing

 * in one or more of the following additional @flags:

 *

 * %__GFP_HIGH - This allocation has high priority and may use emergency pools.

 *

 * %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail

 *   (think twice before using).

 *

 * %__GFP_NORETRY - If memory is not immediately available,

 *   then give up at once.

 *

*

 * %__GFP_NOWARN - If allocation fails, don't issue any warnings.

 *

 * %__GFP_RETRY_MAYFAIL - Try really hard to succeed the allocation but fail

 *   eventually.

 *

 * There are other flags available as well, but these are not intended

 * for general use, and so are not documented here. For a full list of

 * potential flags, always refer to linux/gfp.h.

 */

static __always_inline void *kmalloc(size_t size, gfp_t flags)

{

        if (__builtin_constant_p(size)) {

                if (size > KMALLOC_MAX_CACHE_SIZE)

                        return kmalloc_large(size, flags);

#ifndef CONFIG_SLOB

                if (!(flags & GFP_DMA)) {

                        int index = kmalloc_index(size);

 

                        if (!index)

                                return ZERO_SIZE_PTR;

 

                        return kmem_cache_alloc_trace(kmalloc_caches[index],

                                        flags, size);

                }

#endif

        }

        return __kmalloc(size, flags);

}

如果调用成功,返回指向内存的的指针。

       与kmalloc对应的释放函数是kfree。

       和kmalloc类似还有vmalloc,存在的差异是vmalloc分配的地址是虚拟地址连续的。并不能保证物理地址的连续性。一般是硬件设备需要连续的物理内存。

       当然是用物理连续的内存块可以带来性能增益,因为把物理上不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项,而且不连续的物理地址容易导致TLB抖动。所以,为了获得大块内存时候,会调用vmalloc函数。

       与vmalloc对应的释放函数是vfree。

 

1.1.2 

实际的计算机体系结构有硬件的诸多限制, 这限制了页框可以使用的方式。内核并不能对所有页一视同仁。

例如80x86体系结构的两种硬件约束.

l   ISA总线的直接内存存储DMA处理器有一个限制,只能对RAM的前16MB进行寻址

l   在具有大容量RAM的现代32位计算机中, CPU不能直接访问所有的物理地址, 因为线性地址空间太小, 内核不可能直接映射所有物理内存到线性地址空间

因此,内核把页划分为不同的区。

Linux内核对不同区域的内存需要采用不同的管理方式和映射方式,

管理区分类:

enum zone_type {

#ifdef CONFIG_ZONE_DMA

        /*

         * ZONE_DMA is used when there are devices that are not able

         * to do DMA to all of addressable memory (ZONE_NORMAL). Then we

         * carve out the portion of memory that is needed for these devices.

         * The range is arch specific.

         *

         * Some examples

         *

         * Architecture         Limit

         * ---------------------------

         * parisc, ia64, sparc  <4G

         * s390                 <2G

         * arm                  Various

         * alpha                Unlimited or 0-16MB.

         *

         * i386, x86_64 and multiple other arches

         *                      <16M.

         */

        ZONE_DMA,

#endif

#ifdef CONFIG_ZONE_DMA32

        /*

         * x86_64 needs two ZONE_DMAs because it supports devices that are

         * only able to do DMA to the lower 16M but also 32 bit devices that

         * can only do DMA areas below 4G.

         */

        ZONE_DMA32,

#endif

        /*

         * Normal addressable memory is in ZONE_NORMAL. DMA operations can be

         * performed on pages in ZONE_NORMAL if the DMA devices support

         * transfers to all addressable memory.

         */

        ZONE_NORMAL,

#ifdef CONFIG_HIGHMEM

        /*

         * A memory area that is only addressable by the kernel through

         * mapping portions into its own address space. This is for example

         * used by i386 to allow the kernel to address the memory beyond

         * 900MB. The kernel will set up special mappings (page

         * table entries on i386) for each page that the kernel needs to

         * access.

         */

        ZONE_HIGHMEM,

#endif

        ZONE_MOVABLE,

#ifdef CONFIG_ZONE_DEVICE

        ZONE_DEVICE,

#endif

        __MAX_NR_ZONES

 

};

       例如在x86-32上的区如下图:


 

一个管理区(zone)由struct zone结构体来描述,在linux-2.4.37之前的内核中是用typedef struct zone_struct zone_t数据结构来描述)。

 

1.1.3 节点

CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点。系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点。

内存中每个节点由pg_data_t来描述。在linux中使用page_data_t来体现pglist_data。在分配页面时,Linux采用节点局部分配策略,从最靠近运行中的CPU的节点分配内存。

定义在文件include/linux/mmzone.h中:

n   对于NUMA系统来讲, 整个系统的内存由一个node_data的pg_data_t指针数组来管理

n   对于PC这样的UMA系统,使用struct pglist_data contig_page_data ,作为系统唯一的node管理所有的内存区域。(UMA系统中中只有一个node)

 

节点、管理区和页之前的关系。


版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/notbaron/article/details/79954038

linux内存管理源码分析 - 概述

linux内存管理源码分析 - 概述 本文为原创,转载请注明:http://www.cnblogs.com/tolimit/  http://www.cnblogs.com/tolimit/p...
  • zdy0_2004
  • zdy0_2004
  • 2015-06-05 22:18:20
  • 707

内存管理(Linux内核源码分析)

背景本篇博客试图通过linux内核源码分析linux的内存管理机制,并且对比内核提供的几个分配内存的接口函数。然后聊下slab层的用法以及接口函数。内核分配内存与用户态分配内存内核分配内存与用户态分配...
  • hty46565
  • hty46565
  • 2017-07-12 10:03:53
  • 1038

Linux内存管理浅析(一)

1.Linux内存管理的主要内容 a.      虚拟内存管理 b.      内核空间内存管理 c.      用户空间内存管理 2.虚拟内存和物理内存映射 ZONE_HIGH...
  • u012398362
  • u012398362
  • 2016-11-27 19:40:18
  • 2099

linux内存管理.pdf

  • 2010年04月19日 01:23
  • 279KB
  • 下载

Linux内存管理详解

前一段时间看了《深入理解Linux内核》对其中的内存管理部分花了不少时间,但是还是有很多问题不是很清楚,最近又花了一些时间复习了一下,在这里记录下自己的理解和对Linux中内存管理的一些看法和认识。 ...
  • u010229420
  • u010229420
  • 2016-09-19 21:29:19
  • 938

Linux内存管理(最透彻的一篇)

摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法。力求从外到内、水到渠成地引导网友分析Linux的内存管理与使用。在...
  • hustyangju
  • hustyangju
  • 2015-06-02 15:24:31
  • 5047

linux内存管理大图

转自:
  • mrwangwang
  • mrwangwang
  • 2014-08-20 18:10:50
  • 1025

面试题总结之windows/linux内存管理

前言请说说你对windows/linux内存管理的认识解答内存管理的必要性出现在早期的计算机系统当中,程序是直接运行在物理内存中,每一个程序都能直接访问物理地址。如果这个系统只运行一个程序的话,并且这...
  • u013616945
  • u013616945
  • 2017-08-20 23:09:48
  • 570

linux源代码分析之内存管理

  • 2015年03月08日 10:38
  • 1.16MB
  • 下载

Linux的任督二脉:进程调度和内存管理

比如进程的调度延时是多少?Linux能否硬实时?多核下多线程如何执行?系统的内存究竟耗到哪里去了?我写的应用程序究竟耗了多少内存?什么是内存泄漏,如何判定内存是否真的泄漏?CPU速度、内存大小和系统性...
  • 21cnbao
  • 21cnbao
  • 2017-08-23 14:45:09
  • 25456
收藏助手
不良信息举报
您举报文章:Linux内存管理
举报原因:
原因补充:

(最多只允许输入30个字)