Linux内存管理

简介:


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

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。

底层分配页如下:

f159b3ae582550892c6ccbb05fcb6cfd07d95339

       对应的释放函数有:__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上的区如下图:

70c8776bd31b03b4d897565bb74a9921d9316be3

 

一个管理区(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)

 

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

bccbcc982d9b37d893716be4d24c960914dc3604

 

 

 

 

 

 

目录
相关文章
|
14天前
|
Linux
Linux rsyslog占用内存CPU过高解决办法
该文档描述了`rsyslog`占用内存过高的问题及其解决方案。
39 4
|
1月前
|
移动开发 运维 监控
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
|
19天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
3天前
|
消息中间件 存储 Unix
【探索Linux】P.15(进程间通信 —— system V共享内存)
【探索Linux】P.15(进程间通信 —— system V共享内存)
12 0
|
3天前
|
安全 Linux Python
Volatility3内存取证工具安装及入门在Linux下的安装教程
Volatility3内存取证工具安装及入门在Linux下的安装教程
Volatility3内存取证工具安装及入门在Linux下的安装教程
|
3天前
|
安全 Python Linux
Kali Linux下Volatility2.6常见问题疑难杂症-内存取证信息安全管理与评估
Kali Linux下Volatility2.6常见问题疑难杂症-内存取证信息安全管理与评估
Kali Linux下Volatility2.6常见问题疑难杂症-内存取证信息安全管理与评估
|
4天前
|
缓存 运维 算法
深入理解Linux内核的虚拟内存管理
【5月更文挑战第6天】 在现代操作系统中,尤其是类Unix系统如Linux中,虚拟内存管理是一项核心功能,它不仅支持了多任务环境,还提供了内存保护和抽象。本文将深入探讨Linux操作系统的虚拟内存子系统,包括分页机制、虚拟地址空间布局、页面置换算法以及内存分配策略。通过对这些概念的剖析,我们旨在为读者揭示Linux如何有效地管理和优化物理内存资源,并确保系统的稳定运行与高效性能。
消息中间件 Unix Linux
14 1
|
5天前
|
缓存 Linux
linux性能分析之内存分析(free,vmstat,top,ps,pmap等工具使用介绍)
这些工具可以帮助你监视系统的内存使用情况、识别内存泄漏、找到高内存消耗的进程等。根据具体的问题和需求,你可以选择使用其中一个或多个工具来进行内存性能分析。注意,内存分析通常需要综合考虑多个指标和工具的输出,以便更好地理解系统的行为并采取相应的优化措施。
24 6
|
8天前
|
存储 缓存 监控