linux内核内存slab,伙伴系统,内存碎片,内存耗尽(OOM)杀手,内存资源控制器memcg,KASAN学习笔记

目录

1 基础知识

1.1 页

1.2 页表

1.3 UMA(一致性访问) / NUMA(非一致性访问)

1.4 高端内存和低端内存

1.5 内存模型

2 物理内存的管理

2.1 物理内存的组织:节点和管理区(内存域)简介

2.1.1 简介

2.1.2 备用区域列表 / 区域水线 / 内存域水印

2.1.3 相关函数

2.2 伙伴系统

2.2.1 简介

2.2.2 伙伴系统实现原理

2.2.3 /proc/buddyinfo 中获得伙伴系统的当前信息

3 slab 分配器

3.1 slab简介

3.2 常用函数

3.3 /proc/slabinfo

3.4 /sys/kernel/slab/

3.5 slabtop 命令

3.6 注意点

4 vmalloc()和 kmalloc()以及它们之间的区别

4.1 vmalloc() / vfree()

4.1.1 简介

4.1.2 注意点

4.2 kmalloc() / kfree() / kzalloc()

4.2.1 简介

4.2.2 注意点

4.2.3 kmalloc(size, flags)接口中 flags 参数的说明

4.x vmalloc()和 kmalloc()的区别

5 __get_free_pages() / alloc_pages_node()

5.1 简介

5.2 常用函数

6 分配函数的选择

7 避免内存碎片(反碎片)

7.1 简介

7.2 反碎片的技术

7.3 虚拟可移动区域

7.3.1 简介

7.3.2 使用方法

7.4 根据可移动性分组

7.5 内存碎片整理(memory compaction)

7.5.1 简介

7.5.2 使用方法

8 内存耗尽杀手

8.1 简介

8.2 使用方法

8.3 内存耗尽杀手计算进程的坏蛋分数

9 内存资源控制器 / 控制组(cgroup) / 内存控制组(memcg)

9.1 简介

9.2 控制组版本 1 的内存资源控制器的配置方法

10 内存错误检测工具 KASAN

10.1 简介

10.2 对编译器的要求 和 对内核代码版本的要求

10.3 使用方法

11 在系统引导时获取缓冲区(bootmem分配器)


目录

1 基础知识

1.1 页

1.2 页表

1.3 UMA(一致性访问) / NUMA(非一致性访问)

1.4 高端内存和低端内存

1.5 内存模型

2 物理内存的管理

2.1 物理内存的组织:节点和管理区(内存域)简介

2.1.1 简介

2.1.2 相关函数

2.2 伙伴系统

2.2.1 简介

2.2.2 伙伴系统实现原理

2.2.3 /proc/buddyinfo 中获得伙伴系统的当前信息

3 slab 分配器

3.1 slab简介

3.2 常用函数

3.3 /proc/slabinfo

3.4 /sys/kernel/slab/

3.5 slabtop 命令

3.6 注意点

4 vmalloc()和 kmalloc()以及它们之间的区别

4.1 vmalloc() / vfree()

4.1.1 简介

4.1.2 注意点

4.2 kmalloc() / kfree() / kzalloc()

4.2.1 简介

4.2.2 注意点

4.2.3 kmalloc(size, flags)接口中 flags 参数的说明

4.x vmalloc()和 kmalloc()的区别

5 __get_free_pages() / alloc_pages_node()

5.1 简介

5.2 常用函数

6 分配函数的选择

7 在系统引导时获取缓冲区(bootmem分配器)


1 基础知识

1.1 页

        物理地址被分为离散的单元,称之为页。
        系统内部许多对内存的操作都是基于单个页的。每个页的大小随体系架构不同而不同,但是目前大多数系统都使用每页 4096 个字节。常量 PAGE_SIZE 给出了在任何指定体系架构下的页大小。

        仔细观察内存地址,无论是虚拟的还是物理的,它们都被分为页号和一个页内的偏移量。举个例子,如果使用页大小为 4096 个字节,那么最后的 12 位是偏移量,而剩余的高位则指定了页号。

        如果忽略了地址偏移量,并将除去偏移量的剩余位移到右端,称该结构为页帧数。移动位以在页帧数和地址间进行
转换是一个常用操作;宏 PAGE_SHIFT 将告诉程序员,必须移动多少位才能完成这个转换。

                                                                                                                 《LINUX 设备驱动程序》(第三版)P410

1.2 页表

        把线性地址映射到物理地址的数据结构称为页表(page table)。页表存放在主存中,并在启用分页单元之前必须由内核对页表进行适当的初始化。                                       《深入理解 LINUX 内核》P51


        用来将虚拟地址空间映射到物理地址空间的数据结构称为页表。                        《深入 LINUX 内核架构》P9
        对驱动程序作者来说,在 2.6 版内核中删除了对页表直接操作的需求。                  《LINUX 设备驱动程序》(第三版)P414

1.3 UMA(一致性访问) / NUMA(非一致性访问)

        UMA (uniform memory access)
        NUMA(non-uniform memory access)
        UMA 计算机将可用内存以连接方式组织起来。SMP 系统中每个处理器访问各个内存区都是同样快。
        NUMA 计算机总是多处理器计算机。系统的各个 CPU 都有本地内存,可支持特别快速的访问。各个处理器之间通过总线连接起来,以支持对其他 CPU 的本地内存访问,当然比访问本地内存慢些。
        真正的 NUMA 会设置配置选项 CONFIG_NUMA。

                                                                                                                           《深入 LINUX 内核架构》P108

        非一致内存访问(NUMA)模型,在这种模型中,给定 CPU 对不同内存单元的访问时间可能不一样。《深入理解 LINUX 内核》P51

1.4 高端内存和低端内存

        虚拟地址空间的内核部分必然小于 CPU 理论地址空间的最大长度。如果物理内存比可以映射到内核地址空间中的数量要多,那么内核必须借助于高端内存(highmem)方法来管理“多余的”内存。

                                                                                                                            《深入 LINUX 内核架构》P107

        使用 32 位系统只能在 4GB 的内存中寻址
        在不破坏 32 位应用程序和系统兼容性的情况下,为了能使用更多的内存,处理器制造厂家为他们的产品增加了“地址扩展”特性。其结果是在许多情况下,即使 32 位的处理器都可以在大于 4GB 的物理地址空间寻址。
        然而有多少内存可以直接映射到逻辑地址的限制依然存在。只有内存的低端部分(依赖与硬件和内核的设置,一般为 1 到 2GB)用于逻辑地址;剩余部分(高端内存)是没有的。在访问特定的高端内存页前,内核必须建立明确的虚拟映射,使该页可在内核地址空间中被访问。
        因此,许多内核数据结构必须被放置在低端内存中;而高端内存更趋向于为用户空间进程页所保留。

                                                                                                                             《LINUX 设备驱动程序》(第三版)P411

        存在于内核空间上的逻辑地址内存。几乎所有现在读者遇到的系统,它全部的内存都是低端内存。

        是指那些不存在逻辑地址的内存,这是因为它们处于内核虚拟地址之上。

                                                                                                                            《LINUX 设备驱动程序》(第三版)P412

        64 位地址空间避免了古怪的高端内存域。《深入 LINUX 内核架构》P151

1.5 内存模型

内存模型是从处理器角度看到的物理内存分布情况,内核管理不同内存模型的方式存在差异。内存管理子系统支持3 种内存模型:
    (1) 平坦内存(Flat Memory):内存的物理地址空间是连续的,没有空洞。
    (2) 不连续内存(Discontiguous Memory):内存的物理地址空间存在空洞,这种模型可以高效地处理空洞。
    (3) 稀疏内存(Sparse Memory):内存的物理地址空间存在空洞。如果要支持内存热插拔,只能选择稀疏内存模型。

                                                                                                                             《Linux 内核深度解析》P140

2 物理内存的管理

2.1 物理内存的组织:节点和管理区(内存域)简介

2.1.1 简介

        内存管理子系统使用节点 (node) 、区域 (zone) 和页 (page) 三级结构描述物理内存。
        内存节点使用一个 pglist_data 结构体描述内存布局。内核定义了宏 NODE_DATA(nid) ,它用来获取节点的pglist_data 实例。对于平坦内存模型,只有一个 pglist_data 实例 :contig_page_data 。

                                                                                                                              《Linux 内核深度解析》P141

        Linux2.6 支持非一致性内存访问(NUMA)模型,在这种模型中,给定的 CPU 对不同内存单元的访问时间可能不一样。系统的物理内存被划分为几个节点(node)。在一个单独的节点内,任一给定 CPU 访问页面所需时间都是相同的。
        每个节点中的物理内存又可以分为几个管理区(zone)。
        所有节点的描述符存放在一个单向链表中,它的第一个元素由 pgdat_list 变量指向。

                                                                                                                               《深入理解 LINUX 内核》P298

 

内存划分为结点。每个结点关联到系统中的一个处理器,在内核中表示为 pg_data_t 的实例。
各个结点又划分为内存域,是内存的进一步细分。


一个结点由最多 3 个内存域组成。内核引入下列常量来区分它们。
ZONE_DMA:                 标记适合 DMA 的内存域。该区域的长度依赖于处理器类型。
ZONE_DMA32:             标记了使用 32 位地址字可寻址、适合 DMA 的内存域。
ZONE_NORMAL:          标记了可直接映射到内核段的普通内存域。
ZONE_HIGHMEM:        标记了超出内核段的物理内存。

                                                                                                         《深入 LINUX 内核架构》P109

ZONE_MOVABLE:       它是一个伪内存区域,用来防止内存碎片。
ZONE_DEVICE:           为支持持久内存(persistent memory)热插拔增加的内存区域。
                                                                                                         《Linux 内核深度解析》P142

2.1.2 备用区域列表 / 区域水线 / 内存域水印

备用区域列表

如果首选的内存节点和区域不能满足页分配请求,可以从备用的内存区域借用物理页,借用必须遵守以下原则。
<1> 一个内存节点的某个区域类型可以从另一个内存节点的相同区域类型借用物理页,例如节点 0 的普通区域可以从节点 1 的普通区  域借用物理页。
<2> 高区域类型可以从低区域类型借用物理页,例如普通区域可以从 DMA 区域借用物理页。
<3> 低区域类型不能从高区域类型借用物理页,例如 DMA 区域不能从普通区域借用物理页。

                                                                                                                            《Linux内核深度解析》P154

区域水线 / 内存域水印

首选的内存区域在什么情况下从备用区域借用物理页?这个问题要从区域水线开始说起。每个内存区域有 3 个水线。
<1> 高水线:如果内存区域的空闲页数大于高水线,说明该内存区域的内存充足。
<2> 低水线:如果内存区域的空闲页数小于低水线,说明该内存区域的内存轻微不足。
<3> 最低水线:如果内存区域的空闲页数小于最低水线,说明该内存区域的内存严重不足。
最低水线以下的内存称为紧急保留内存。

                                                                                                                            《Linux 内核深度解析》P156

        最低水线以下的内存称为紧急保留内存,在内存严重不足的紧急情况下,给承诺“给我少量紧急保留内存使用,我可以释放更多的内存”的进程使用。
        设置了进程标志位 PF_MEMALLOC 的进程可以使用紧急保留内存,标志位 PF_MEMALLOC 表示“给我少量紧急保留内存使用,我可以释放更多的内存”。内存管理子系统以外的子系统不应该使用这个标志位,典型的例子是页回收内核线程 kswapd ,在回收页的过程中可能需要申请内存。

                                                                                                                              《Linux 内核深度解析》P156

相关算法

《Linux 内核深度解析》P157
《深入理解 LINUX 内核》P303

相关/proc/文件

/proc/sys/vm/min_free_kbytes
/proc/sys/vm/watermark_scale_factor    《Linux 内核深度解析》P156   《深入 LINUX 内核架构》P116

/proc/sys/vm/numa_zonelist_order
/proc/zoneinfo       《Linux 内核深度解析》P155

2.1.3 相关函数

NODE_DATA(nid);       //它用来获取节点的 pglist_data 实例;《Linux 内核深度解析》P141

page_to_nid();             //用来得到物理页所属的内存节点的编号;《Linux 内核深度解析》P143

 

2.2 伙伴系统

2.2.1 简介

        在内核初始化完成后,内存管理的责任由伙伴系统承担。伙伴系统基于一种相对简单然而令人吃惊的强大算法,已经伴随我们几乎 40 年。它结合了优秀内存分配器的两个关键特征:速度和效率

                                                                                                                     《深入 LINUX 内核架构》P159

内核中很多时候要求分配连续页。为快速检测内存中的连续区域,内核采用了一种父老而历经检验的技术:伙伴系统

                                                                                                                     《深入 LINUX 内核架构》P11

Linux 采用著名的伙伴系统(buddy system)算法来解决外碎片问题。《深入理解 LINUX 内核》P312

 

        由一个母实体分成的两个各方面属性一致的两个子实体,这两个子实体就处于伙伴关系。在操作系统分配内存的过程中,一个内存块常常被分成两个大小相等的内存块,这两个大小相等的内存块就处于伙伴关系。它满足 3 个条件 :
    • 两个块具有相同大小记为 2^K
    • 它们的物理地址是连续的
    • 从同一个大块中拆分出来

                                                                                            https://blog.csdn.net/chm880910/article/details/80342183

2.2.2 伙伴系统实现原理

        为了便于页面的维护,将多个页面组成内存块,每个内存块都有 2 的方幂个页,方幂的指数被称为阶 order。order相同的内存块被组织到一个空闲链表中。伙伴系统基于 2 的方幂来申请释放内存页。
        当申请内存页时,伙伴系统首先检查与申请大小相同的内存块链表中,检看是否有空闲页,如果有就将其分配出去,并将其从链表中删除,否则就检查上一级,即大小为申请大小的 2 倍的内存块空闲链表,如果该链表有空闲内存,就将其分配出去,同时将剩余的一部分(即未分配出去的一半)加入到下一级空闲链表中;如果这一级仍没有空闲内存;就检查它的上一级,依次类推,直到分配成功或者彻底失败,在成功时还要按照伙伴系统的要求,将未分配的内存块进行划分并加入到相应的空闲内存块链表
        在释放内存页时,会检查其伙伴是否也是空闲的,如果是就将它和它的伙伴合并为更大的空闲内存块,该检查会递归进行,直到发现伙伴正在被使用或者已经合并成了最大的内存块。

                                                                                               https://blog.csdn.net/chm880910/article/details/80342183

2.2.3 /proc/buddyinfo 中获得伙伴系统的当前信息

参考《深入 LINUX 内核架构》P161

3 slab 分配器

《LINUX 设备驱动程序》(第三版)P217
《Linux 内核设计与实现》P197
《深入理解 LINUX 内核》P323
《深入 LINUX 内核架构》P205

3.1 slab简介

        分配和释放数据结构是所有内核中最普遍的操作之一。为了便于数据的频繁分配和回收,编程人员常常会用到空闲链表。空闲链表包含可供使用的、已经分配好的数据结构块。当代码需要一个新的数据结构实例时,就从空闲链表中抓取一个,而不需要分配内存,再把数据放进区。以后,当不再需要这个数据结构的实例时,就把它放回空闲链表,而不是释放它。从这个意义上说,空闲链表相当于对象高速缓存——快速存储频繁使用的对象类型。

        在内核中,空闲链表面临的主要问题之一是不能全局控制。当可以内存变得紧缺时,内核无法通知每个空闲链表,让其收缩缓存的大小以便释放出一些内存来。实际上内核根本不知道存在空闲链表。为了弥补之一缺陷,也为了使代码更加稳固,linux 内核提供了 slab 层(也就是所谓的 slab 分配器)。slab 分配器扮演了通用数据结构缓存层的角色

                                                                                                                        《Linux 内核设计与实现》P197

Linux 内核的高速缓存管理有时称为“slab 分配器”。      《LINUX 设备驱动程序》(第三版)P217

3.2 常用函数

kmem_cache_create();
《LINUX 设备驱动程序》(第三版)P217
《深入理解 LINUX 内核》P328

kmem_cache_alloc();
《LINUX 设备驱动程序》(第三版)P218
《深入理解 LINUX 内核》P337

kmem_cache_free();
《LINUX 设备驱动程序》(第三版)P218
《深入理解 LINUX 内核》P338

kmem_cache_destroy();

        这个释放操作只有在已将从缓存中分配的所有对象都归还后才能成功。所以,模块应该检查 kmem_cache_destroy 的返回状态;如果失败,则表明模块中发生了内存泄漏(因为有一些对象被漏掉了)。                           《LINUX 设备驱动程序》(第三版)P219

 

kmem_cache_shrink();

kmem_cache_shrink()函数通过调用 slab_destroy()撤销高速缓存中所有的 slab。《深入理解 LINUX 内核》P329

3.3 /proc/slabinfo

《深入 LINUX 内核架构》P208
《深入理解 LINUX 内核》P329

3.4 /sys/kernel/slab/

Documentation/ABI/testing/sysfs-kernel-slab

3.5 slabtop 命令

《Linux性能优化》P54

3.6 注意点

        对嵌入式系统来说,slab 分配器代码量和复杂性都太高。

        为了处理此类情形,在内核2.6开发期间,增加了slab分配器的两个替代品:slob和slub。

《深入 LINUX 内核架构》P206

4 vmalloc()和 kmalloc()以及它们之间的区别

4.1 vmalloc() / vfree()

4.1.1 简介

vmalloc 是一个接口函数,内核代码使用它来分配在虚拟内存中连续但在物理内存中不一定连续的内存。《深入 LINUX 内核架构》P196

4.1.2 注意点

在大多数情况下不鼓励使用 vmalloc。通过 vmalloc 获得的内存使用起来效率不高。    《LINUX 设备驱动程序》(第三版)P225

 

vmalloc 的开销要比__get_free_pages 大,因为它不但要获取内存,还要建立页表。

vmalloc 函数的一个小缺点是它不能在原子上下文中使用

                                                                                                        《LINUX 设备驱动程序》(第三版)P226

 

        vmalloc()仅在不得已时才会使用——典型的就是为了获得大块内存时,例如,当模块被动态插入到内核中时,就把模块装载到由 vmalloc()分配的内存上。              《Linux 内核设计与实现》P196

 

4.2 kmalloc() / kfree() / kzalloc()

4.2.1 简介

        kmalloc()接口建立在 slab 层之上,使用了一组通用高速缓存。   《Linux 内核设计与实现》P197

        每次调用 kmalloc 时,内核找到最适合的缓存,并从中分配一个对象满足请求(如果没有刚好适合的缓存,则分配稍大的对象,但不会分配更小的对象)。           《深入 LINUX 内核架构》P209

        用 kzalloc 申请内存的时候,效果等同于先是用 kmalloc()申请空间,然后用 memset()来初始化,所有申请的元素都被初始化为 0。对应的释放函数也是 kfree 函数。                     https://www.cnblogs.com/linhaostudy/p/7477370.html

4.2.2 注意点

        对 kmalloc 能够分配的内存大小,存在一个上限。这个限制随着体系架构的不同以及内存配置选项的不同而变化。如果我们希望代码具有完整的可移植性,则不应该分配大于 128KB 的内存。但是,如果希望得到多于几千字节的内存,则最好使用除了 kmalloc 之外的内存获取方法。                                                     《LINUX 设备驱动程序》(第三版)P217

 

4.2.3 kmalloc(size, flags)接口中 flags 参数的说明

《LINUX 设备驱动程序》(第三版)P214
《深入 LINUX 内核架构》P174

 

4.x vmalloc()和 kmalloc()的区别

1. kmalloc 和 vmalloc 都是分配的内核的内存。
2. kmalloc 保证分配的内存在物理上是连续的,vmalloc 保证虚拟地址空间上连续,在物理地址空间上可能不连续。
4. kmalloc 能分配的大小有限,vmalloc 能分配的相对较大。
5. 内存只有在要被 DMA 访问的时候才需要物理上连续。
6. vmalloc 比 kmalloc 要慢。

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

5 __get_free_pages() / alloc_pages_node()

5.1 简介

        仔细观察内存地址,无论是虚拟的还是物理的,它们都被分为页号和一个页内的偏移量。举个例子,如果使用页大小为 4096 个字节,那么最后的 12 位是偏移量,而剩余的高位则指定了页号。
        当系统的 PAGE_SIZE 为 4096 时,__get_free_page()、__get_free_pages()和 get_zeroed_page()获取到的 16 进制页地址的低 3
位是 0。如:
                0xffff 8800 86b7 c000
                0xffff 8800 994b c000

5.2 常用函数

struct page * alloc_pages_node(int nid, unsigned int flags, unsigned int order);     《LINUX 设备驱动程序》(第三版)P221

 

struct page * alloc_pages(gfp_t gfp_mask, unsigned int order);

struct page * alloc_page(gfp_t gfp_mask);

void __free_pages(struct page *page, unsigned int order);

void __free_page(struct page *page);

 

void * page_address(struct page *page);

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);

unsigned long __get_free_page(gfp_t gfp_mask);

unsigned long get_zeroed_page(unsigned int gfp_mask);

void free_pages(unsigned long addr, unsigned long order);

void free_page(unsigned long addr);

                                                        《LINUX设备驱动程序》(第三版)P224        《Linux内核设计与实现》P190

 

6 分配函数的选择

《Linux 内核设计与实现》P209

7 避免内存碎片(反碎片)

7.1 简介

        伙伴系统确实工作得非常好。但是在 Linux 内存管理方面,有一个长期存在的问题:在系统启动并长期运行后,物理内存会产生很多碎片。                                       《深入 LINUX 内核架构》P161

        通过伙伴系统可以在某种程度上减少内存碎片,但无法完全消除。   《深入 LINUX 内核架构》P12

        内核的方式是反碎片(anti-fragmentation),即试图从最开始尽可能防止碎片。《深入 LINUX 内核架构》P161

        内存碎片分为内部碎片和外部碎片,内部碎片指内存页里面的碎片,外部碎片指空闲的内存页分散,很难找到一组物理地址连续的空闲内存页。        《Linux 内核深度解析》P288

7.2 反碎片的技术

<1> 2.6.23 版本引入了虚拟可移动区域。
<2> 2.6.23 版本引入了成块回收(lumpy reclaim,有的书中翻译为集中回收),3.5 版本废除,被内存碎片整理技术取代。成块回收不是一个完整的解决方案,它只是缓解了碎片问题。
<3> 2.6.24 版本引入了根据可移动性分组的技术,把物理页分为不可移动页、可移动页和可回收页 3 种类型。
<4> 2.6.35 版本引入了内存碎片整理技术。

                                                                                《Linux 内核深度解析》P288

7.3 虚拟可移动区域

7.3.1 简介

        可移动区域(ZONE_MOVABLE)是一个伪内存区域,基本思想很简单:把物理内存分为两个区域,一个区域用于分配不可移动的页,另一个区域用于分配可移动的页,防止不可移动页向可移动区域引入碎片。    《Linux 内核深度解析》P289

7.3.2 使用方法

《Linux 内核深度解析》P289

7.4 根据可移动性分组

为了预防内存碎片,内核根据可移动性把物理页分为 3 种类型。
<1> 不可移动页:位置必须固定,不能移动,直接映射到内核虚拟地址空间的页属于这一类。
<2> 可移动页:使用页表映射的属于这一类,可移动到其它位置,然后修改页表映射。
<3> 可回收页:不能移动,但可以回收,需要数据的时候可以重新从数据源获取。后备存储设备支持的页属于这一类。

                                                                                            《Linux 内核深度解析》P158


<1> 不可移动页:在内存中有固定的位置,不能移动到其它地方。核心内核分配的大多数内存属于该类别。
<2> 可回收页:不能直接移动,但可以删除,其内存可以从某些源重新生成。例如,映射自文件的数据数据该类别。kswapd 守护进程会根据可回收页访问的频繁程度,周期性的释放此类内存。
<3> 可移动页可以随意移动。属于用户空间的应用程序属于该类别。它们是通过页表映射的。

                                                                                              《深入 LINUX 内核架构》P162
迁移类型:
MIGRATE_UNMOVABLE,
MIGRATE_RECLAIMABLE,
MIGRATE_MOVABLE,
MIGRATE_PCPTYPES,
......

7.5 内存碎片整理(memory compaction)

7.5.1 简介

        基本思想是:从内存区域的底部扫描已分配的可移动页,从内存区域的顶部扫描空闲页,把底部的可移动页移动到顶部的空闲页,在底部形成连续的空闲页。    《Linux 内核深度解析》P291

7.5.2 使用方法

打开内核配置:CONFIG_COMPACTION
向/proc/sys/vm/compact_memory 文件写入任何整数值(数值没有意义),触发内存碎片整理。

文件/proc/sys/vm/compact_unevictable_allowed 用来设置是否允许内存碎片整理移动不可回收的页,设置为 1 表示允许。
文件/proc/sys/vm/extfrag_threshold 用来设置外部碎片的阀值,取值范围 0~1000,默认 500。

                                                                                              《Linux 内核深度解析》P292

8 内存耗尽杀手

8.1 简介

        当内存严重不足的时候,页分配器在多次尝试直接页回收失败后,就会调用内存耗尽杀手(OOM killer,OOM 是“Out of Memory”的缩写),选择进程杀死,释放内存。                     《Linux 内核深度解析》P338

8.2 使用方法

内存耗尽杀手没有配置宏,可配置的参数如下:
<1> /proc/sys/vm/oom_kill_allocating_task ,是否允许杀死正在申请分配内存并触发内存耗尽的进程。
<2> /proc/sys/vm/oom_dump_tasks ,是否允许内存耗尽杀手杀死进程的时候打印所有用户进程的内存使用信息。
<3> /proc/sys/vm/panic_on_oom,是否允许在内存耗尽的时候内核恐慌,重启系统。

                                                                        《Linux 内核深度解析》P338

8.3 内存耗尽杀手计算进程的坏蛋分数

《Linux 内核深度解析》P338

9 内存资源控制器 / 控制组(cgroup) / 内存控制组(memcg)

9.1 简介

        控制组(cgroup)的内存资源控制器用来控制一组进程的内存使用量,启用内存资源控制器的指控组简称内存控制组(memcg)。控制组把各种资源控制器称为子系统,内存资源控制器也称为内存子系统。    《Linux 内核深度解析》P340

        控制组版本 1 和控制组版本 2 的内存资源控制器是互斥的。如果使用了控制组版本 1 的内存资源控制器,就不能使用控制组版本 2 的内存资源控制器;同样,如果使用了控制组版本 2 的内存资源控制器,就不能使用控制组版本 1 的内存资源控制器。

                                                                                                                《Linux 内核深度解析》P341

9.2 控制组版本 1 的内存资源控制器的配置方法

<1> 在目录”/sys/fs/cgroup”下挂载 tmpfs 文件系统。
mount -t tmpfs none /sys/fs/cgroup
<2> 在目录”/sys/fs/cgroup”下创建目录”memory”
mkdir /sys/fs/cgroup/memory
<3> 在目录”/sys/fs/cgroup/memory/”下挂载 cgroup 文件系统,把内存资源控制器关联到控制组层级树。
mount -t cgroup -o memory none /sys/fs/cgroup/memory/
<4> 创建新的控制组
mkdir /sys/fs/cgroup/memory/memcg0

<5> 设置内存使用的限制。例如把控制组的内存使用限制设置为 4MB
echo 4M > /sys/fs/cgroup/memory/memcg0/memory.limit_in_bytes
<6> 把线程加入控制组
echo /sys/fs/cgroup/memory/memcg0/tasks
<7> 页可以把线程组加入控制组,指定线程组中任意一个线程的标识符,就会把线程组的所有线程加入任务组。
echo /sys/fs/cgroup/memory/memcg0/cgroup.procs

                                                         《Linux 内核深度解析》P342

10 内存错误检测工具 KASAN

10.1 简介

        内核地址消毒剂(Kernel Address SANitizer,KASAN),是一个动态的内存错误检查工具,为发现“释放后使用”和“越界访问”这两类缺陷提供了快速和综合的解决方案。     《Linux 内核深度解析》P401

10.2 对编译器的要求 和 对内核代码版本的要求

        KASAN 使用编译时插桩检查每个内存访问,要求 GCC 编译器的版本至少是 4.9.2,检查栈和全局变量的越界访问需要 GCC 编译器的版本至少是 5.0


内核支持 KASAN 的进展如下
<1> 4.0 版本引入 KASAN,仅 x86_64 架构支持,只有 SLUB 分配器支持 KASAN。
<2> 4.4 版本的 AMD64 架构支持 KASAN。
<3> 4.6 版本的 SLAB 分配器支持 KASAN。

                                                              《Linux 内核深度解析》P401

10.3 使用方法

《Linux 内核深度解析》P401

11 在系统引导时获取缓冲区(bootmem分配器)

《LINUX 设备驱动程序》(第三版)P230

 

 

 

 

 

你可能感兴趣的:(linux,内存管理,slab)