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

 

 

 

 

 

 

目录
打赏
0
0
0
0
78
分享
相关文章
|
4月前
|
linux 手动释放内存
在 Linux 系统中,内存管理通常自动处理,但业务繁忙时缓存占用过多可能导致内存不足,影响性能。此时可在业务闲时手动释放内存。
189 17
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
126 27
|
6月前
|
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
1586 6
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
155 48
|
3月前
|
Linux系统内存使用优化技巧
交换空间(Swap)的优化 禁用 Swap sudo swapoff -a 作用:这个命令会禁用系统中所有的 Swap 空间。swapoff 命令用于关闭 Swap 空间,-a 参数表示关闭 /etc/fstab 文件中配置的所有 Swap 空间。 使用场景:在高性能应用场景下,比如数据库服务器或高性能计算服务器,禁用 Swap 可以减少磁盘 I/O,提高系统性能。
77 3
|
3月前
|
Linux查看内存命令
1. free free命令是最常用的查看内存使用情况的命令。它显示系统的总内存、已使用内存、空闲内存和交换内存的总量。 free -h • -h 选项:以易读的格式(如GB、MB)显示内存大小。 输出示例: total used free shared buff/cache available Mem: 15Gi 4.7Gi 4.1Gi 288Mi 6.6Gi 9.9Gi Swap: 2.0Gi 0B 2.0Gi • to
105 2
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
282 20
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
842 58
如何解决 Linux 系统中内存使用量耗尽的问题?
如何解决 Linux 系统中内存使用量耗尽的问题?
454 48
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等