把握linux内核设计思想(十一):内存管理之页的分配与回收

2023-11-04

【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】 
        内存管理单元(MMU)负责将管理内存,在把虚拟地址转换为物理地址的硬件的时候是按页为单位进行处理,从虚拟内存的角度来看,页就是内存管理中的最小单位。页的大小与体系结构有关,在 x86 结构中一般是4KB(32位)或者8KB(64位)。
通过 getconf 命令可以查看系统的page的大小:
# getconf -a | grep PAGE
PAGESIZE                           4096
PAGE_SIZE                          4096
_AVPHYS_PAGES                      230873
_PHYS_PAGES                        744957 
         内核中的每个物理页用struct  page结构表示,结构定义于文件<include/linux/mm_types.h>:
struct page {
    unsigned long flags;        /*页的状态*/
    atomic_t _count;        /* 页引用计数 */
    union {
        atomic_t _mapcount; /* 已经映射到mms的pte的个数*/
        struct {        /* */
            u16 inuse;
            u16 objects;
        };
    };
    union {
        struct {
        unsigned long private;     
        struct address_space *mapping;
        };
#if USE_SPLIT_PTLOCKS
        spinlock_t ptl;
#endif
        struct kmem_cache *slab;    /* 指向slab层 */
        struct page *first_page;    /* Compound tail pages */
    };
    union {
        pgoff_t index;      /* Our offset within mapping. */
        void *freelist;     /* SLUB: freelist req. slab lock */
    };
    struct list_head lru;       /* 将页关联起来的链表项 */

#if defined(WANT_PAGE_VIRTUAL)
    void *virtual;          /* Kernel virtual address (NULL if
                       not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
#ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
    unsigned long debug_flags;  /* Use atomic bitops on this */
#endif

#ifdef CONFIG_KMEMCHECK
    void *shadow;
#endif
};
       内核使用这一结构来管理系统中所有的页,因为内核需要知道一个该页是否被分配,是被谁拥有的等信息。
       由于ISA总线的DMA处理器有严格的限制,只能对物理内存前16M寻址,内核线性地址空间只有1G,CPU不能直接访问所有的物理内存。这样就导致有一些内存不能永久地映射在内核空间上。所以在linux中,把页分为不同的区,使用区来对具有相似特性的页进行分组。分组如下(以x86-32为例):
ZONE_DMA 小于16M内存页框,这个区包含的页用来执行DMA操作。
ZONE_NORMAL 16M~896M内存页框,个区包含的都是能正常映射的页。
ZONE_HIGHMEM 大于896M内存页框,这个区包"高端内存",其中的页能不永久地映射到内核地址空间。
        linux 把系统的页划分区,形成不同的内存池,这样就可以根据用途进行分配了。
        每个区都用struct zone表示,定义于<include/linux/mmzone.h>中。该结构体较大,详细结构体信息可以查看源码文件。
        linux提供了几个以页为单位分配释放内存的接口,定义于<include/linux/gfp.h>中。分配内存主要有以下方法:
alloc_page(gfp_mask) 只分配一页,返回指向页结构的指针
alloc_pages(gfp_mask, order) 分配 2^order 个页,返回指向第一页页结构的指针
__get_free_page(gfp_mask) 只分配一页,返回指向其逻辑地址的指针
__get_free_pages(gfp_mask, order) 分配 2^order 个页,返回指向第一页逻辑地址的指针
get_zeroed_page(gfp_mask) 只分配一页,让其内容填充为0,返回指向其逻辑地址的指针

alloc_*函数返回的是内存的物理地址,get_* 函数返回内存物理地址映射后的逻辑地址。如果无须直接操作物理页结构体的话,一般使用 get_*函数。


释放页的函数有:
extern void __free_pages( struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
extern void free_hot_page( struct page *page);

当需要以页为单位的连续物理页时,可以使用上面这些分配页的函数,对于常用以字节为单位的分配来说,内核提供来kmalloc()函数。
        kmalloc()函数和用户空间一族函数类似,它可以以字节为单位分配内存,对于大多数内核分配来说,kmalloc函数用得更多。
void *kmalloc(size_t size, gfp_t gfp_mask);
参数中有个 gfp_mask 标志,这个标志是控制分配内存时必须遵守的一些规则。

gfp_mask 标志有3类:

  1. 行为标志 :控制分配内存时,分配器的一些行为,如何分配所需内存。
  2. 区标志   :控制内存分配在那个区(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM 之类)。
  3. 类型标志 :由上面2种标志组合而成的一些常用的场景。

行为标志主要有以下几种:

行为标志

描述

__GFP_WAIT 分配器可以睡眠
__GFP_HIGH 分配器可以访问紧急事件缓冲池
__GFP_IO 分配器可以启动磁盘I/O
__GFP_FS 分配器可以启动文件系统I/O
__GFP_COLD 分配器应该使用高速缓存中快要淘汰出去的页
__GFP_NOWARN 分配器将不打印失败警告
__GFP_REPEAT 分配器在分配失败时重复进行分配,但是这次分配还存在失败的可能
__GFP_NOFALL 分配器将无限的重复进行分配。分配不能失败
__GFP_NORETRY 分配器在分配失败时不会重新分配
__GFP_NO_GROW 由slab层内部使用
__GFP_COMP 添加混合页元数据,在 hugetlb 的代码内部使用
标志主要有以下3种:

区标志

描述

__GFP_DMA 从 ZONE_DMA 分配
__GFP_DMA32 只在 ZONE_DMA32 分配 ,和 ZONE_DMA 类似,该区包含的页也可以进行DMA操作
__GFP_HIGHMEM 从 ZONE_HIGHMEM 或者 ZONE_NORMAL 分配,优先从 ZONE_HIGHMEM 分配,如果 ZONE_HIGHMEM 没有多余的页则从 ZONE_NORMAL 分配

类型标志是编程中最常用的,在使用标志时,应首先看看类型标志中是否有合适的,如果没有,再去自己组合 行为标志和区标志。

类型标志

描述

实际标志

GFP_ATOMIC  这个标志用在中断处理程序,下半部,持有自旋锁以及其他不能睡眠的地方 __GFP_HIGH
GFP_NOWAIT 与 GFP_ATOMIC 类似,不同之处在于,调用不会退给紧急内存池。
这就增加了内存分配失败的可能性
0
GFP_NOIO 这种分配可以阻塞,但不会启动磁盘I/O。
这个标志在不能引发更多磁盘I/O时能阻塞I/O代码,可能会导致递归
__GFP_WAIT
GFP_NOFS 这种分配在必要时可能阻塞,也可能启动磁盘I/O,但不会启动文件系统操作。
这个标志在你不能再启动另一个文件系统的操作时,用在文件系统部分的代码中
(__GFP_WAIT
| __GFP_IO) 
GFP_KERNEL 这是常规的分配方式,可能会阻塞。这个标志在睡眠安全时用在进程上下文代码中。
为了获得调用者所需的内存,内核会尽力而为。这个标志应当为首选标志
(__GFP_WAIT
| __GFP_IO
| __GFP_FS ) 
GFP_USER 这是常规的分配方式,可能会阻塞。用于为用户空间进程分配内存时 (__GFP_WAIT
| __GFP_IO
| __GFP_FS )
GFP_HIGHUSER 从 ZONE_HIGHMEM 进行分配,可能会阻塞。用于为用户空间进程分配内存 (__GFP_WAIT
| __GFP_IO
| __GFP_FS )
|__GFP_HIGHMEM) 
GFP_DMA 从 ZONE_DMA 进行分配。需要获取能供DMA使用的内存的设备驱动程序使用这个标志
通常与以上的某个标志组合在一起使用。
__GFP_DMA

以上各种类型标志的使用场景总结:

场景

相应标志

进程上下文,可以睡眠 使用 GFP_KERNEL
进程上下文,不可以睡眠 使用 GFP_ATOMIC,在睡眠之前或之后以 GFP_KERNEL 执行内存分配
中断处理程序 使用 GFP_ATOMIC
软中断 使用 GFP_ATOMIC
tasklet 使用 GFP_ATOMIC
需要用于DMA的内存,可以睡眠 使用 (GFP_DMA|GFP_KERNEL)
需要用于DMA的内存,不可以睡眠 使用 (GFP_DMA|GFP_ATOMIC),或者在睡眠之前执行内存分配

kmalloc  所对应的释放内存的方法为:

void kfree(const void *);
vmalloc()也可以按字节来分配内存。
void *vmalloc(unsigned long size)
和kmalloc是一样的作用,不同在于前者分配的内存虚拟地址是连续的,而物理地址则无需连续。 kmalloc()可以保证在物理地址上都是连续的,虚拟地址当然也是连续的。vmalloc()函数只确保页在虚拟机地址空间内是连续的。它通过分配非联系的物理内存块,再“修正”页表,把内存映射到逻辑地址空间的连续区域中,就能做到这点。但很显然这样会降低处理性能,因为内核不得不做“拼接”的工作。所以这也是为什么不得已才使用 vmalloc()的原因 vmalloc()可能睡眠,不能从中断上下文中进行调用,也不能从其他不允许阻塞的情况下进行调用。释放时必须使用vfree()
void vfree(const void *);

        对于内存页面的管理,通常是先在虚存空间中分配一个虚存区间,然后才根据需要为此区间分配相应的物理页面并建立起映射,也就是说,虚存区间的分配在前,而物理页面的分配在后。但由于频繁的请求和释放不同大小的连续页框,必然导致在已分配页框的块内分散了许多小块的空闲页框,由此产生的问题是:即使有足够的空闲页框可以满足请求,但当要分配一个大块的连续页框时,无法满足请求。这就是著名的内存管理问题:外碎片问题。Linux采用著名的伙伴(Buddy)系统算法来解决外碎片问题。

        把所有的空闲页框分组为11个块链表。每个块链表包含大小为1,2,4,8,16,32,64,128,256,5121024个的页框伙伴系统算法原理为:

        假设请求一个256个页框的块,先在256个页框的链表内检查是否有一个空闲的块。如果没有这样的块,算法会查找下一个更大的块,在512个页框的链表中找一个空闲块。如果存在这样的块,内核就把512的页框分成两半,一半用作满足请求,另一半插入256个页框的链表中。如果512个页框的块链表也没有空闲块,就继续找更大的块,1024个页框的块。如果这样的块存在,内核把1024个页框的256个页框用作请求,然后从剩余的768个中拿出512个插入512个页框的链表中,把最后256个插入256个页框的链表中。

页框块的释放过程如下:

如果两个块具有相同的大小:a并且他们的物理地址连续那么这两个块成为伙伴,内核就会试图把大小为a的一对空闲伙伴块合并为一个大小为2a的单独块。该算法还是迭代的,如果合并成功的话,它还会试图合并2a的块。


管理分区数据结构struct zone_struct中,涉及到空闲区数据结构。

struct free_area    free_area[MAX_ORDER];

struct free_area {
 struct list_head    free_list[MIGRATE_TYPES];
 unsigned long        nr_free; 
};

        采用伙伴算法分配内存时,每次至少分配一个页面。但当请求分配的内存大小为几十个字节或几百个字节时应该如何处理?如何在一个页面中分配小的内存区,小内存区的分配所产生的内碎片又如何解决?slab的分配模式可以解决该问题,下一节我们将开始分析slab分配器


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

把握linux内核设计思想(十一):内存管理之页的分配与回收 的相关文章

  • Linux内核同步之RCU机制基础

    Why RCU 1 中断与抢占 当一个进程被时钟中断打断后 kernel运行tick中断处理程序 一般是top half 中断处理程序运行结束后 有两种情况 之前的进程获得CPU继续运行 另一个进程获得了CPU开始运行 而之前的进程则被抢占
  • 一文读懂 PyTorch 显存管理机制

    点击上方 视学算法 选择加 星标 或 置顶 重磅干货 第一时间送达 作者丨米阿罗 知乎 已授权 来源丨https zhuanlan zhihu com p 486360176 编辑丨极市平台 首发于踢翻炼丹炉 https www zhihu
  • /etc/sysctl.conf 调优 & 优化Linux内核参数

    from http apps hi baidu com share detail 15652067 http keyknight blog 163 com blog static 3663784020104152407759 http bl
  • 第十四章 netlink机制--基于Linux3.10

    Netlink基于网络的消息机制 能够让用户和内核空间进行通信 12 3节提到的ifconfig是使用ioctl方法和内核通信的 而ip命令则是使用netlink和内核通信的 该机制初衷是为网络服务的 但是现在起应用范围已经大大扩展 14
  • C++从入门到放弃之:C++ 左值引用与右值引用详解

    C 从入门到放弃 C 引用 1 左值引用 2 万能引用 常引用 3 右值引用 4 引用型函数返回值 5 引用和指针 6 函数传参传递指针和引用的区别 总结 C 引用 1 左值引用 定义 引用即别名 某个变量的别名 对引用的操作就等同于对变量
  • java实现:《操作系统实验三》模拟内存管理

    固定分区分配 固定分区分配是最简单的一种多道程序存储管理方式 它将用户内存空间划分为若干个固定大小的区域 每个分区只装入一道作业 当有空闲分区时 便可以再从外存的后背作业队列中 选择适当大小的作业装入该分区 如此循环 优缺点 分区大小相等
  • UBOOT命令总结(转)

    UBOOT命令总结 转 很好的UBOOT命令总结 我在起步时就是看的这篇东西 熟悉了以后就用 看自带帮助就行 Printenv 打印环境变量 Uboot gt printenvbaudrate 115200ipaddr 192 168 1
  • make -C $(LINUX_KERNEL_PATH) M=$(PWD) modules中的M选项

    新的内核模块编程中的make命令里有个M选项 如下 make C lib modules shell uname r build M PWD modules M PWD 意思是返回到当前目录继续读入 执行当前的Makefile 请参考 从
  • 一文搞懂Linux内核页框回收(Page Frame Reclamation)

    页替换策略 Page Replacement Policy 每当讨论页替换策略 提及最多的就是基于LRU Least Recently Used 的算法 但严格来说这是不对的因为这些lists并不是严格按照LRU的顺序来维护的 在Linux
  • Fedora12 Linux启动过程

    1 加载BIOS BIOS Basic Input Output System 是固化到计算机内主板上一个ROM芯片上的程序 保存CPU相关信息 设备启动顺序信息 硬盘信息 内存信息等等 打开计算机电源第一项工作即加载BIOS 进行POST
  • kvm内存管理

    qemu kvm 进程很像一个普通的linux程序 它通过通常的malloc和mmap调用来申请内存 如果一个客户系统想使用1G物理内存 qemu kvm将会做一个malloc 1 lt lt 30 调用 在主机上申请1G的虚拟地址 然而
  • 分段和分页内存管理

    两者描述 打个比方 比如说你去听课 带了一个纸质笔记本做笔记 笔记本有100张纸 课程有语文 数学 英语三门 对于这个笔记本的使用 为了便于以后复习方便 你可以有两种选择 第一种是 你从本子的第一张纸开始用 并且事先在本子上做划分 第2张到
  • 进程间通信之共享内存分析

    零拷贝技术 https strikefreedom top linux io and zero copy 一 内存映射和共享内存的区别 1 1 内存映射之mmap函数 将一个文件或者其它对象映射到进程的地址空间 实现文件磁盘地址和进程虚拟地
  • Linux内核文件系统知识大总结

    1 文件系统特点 文件系统要有严格的组织形式 使得文件能够以块为单位进行存储 文件系统中也要有索引区 用来方便查找一个文件分成的多个块都存放在了什么位置 如果文件系统中有的文件是热点文件 近期经常被读取和写入 文件系统应该有缓存层 文件应该
  • diagnose-tools 编译报错

    在 Ubuntu 20 04 4 LTS 环境中 编译diagnose tools 执行make deps时报错 checking whether gcc m32 makes executables we can run no config
  • 第九章 tcp拥塞控制--基于Linux3.10

    下载地址 http download csdn net detail shichaog 8620701 Linux提供丰富的拥塞控制算法 这些算法包括Vegas Reno HSCTP High Speed TCP Westwood BIC
  • iOS开发之内存管理

    iOS开发之内存管理 一 垃圾回收机制 二 内存管理的概念 三 OC内存管理注意事项 四 MRC相关语法 一 垃圾回收机制 与Java语言相同Objective c 2 0之后 也提供了垃圾回收机制 OC是支持垃圾回收机制的 Garbage
  • 慢慢欣赏linux pud_offset解析

    typedef struct pudval t pud pud t gt typedef u64 pudval t dir表示L0页表索引的指针 指向PUD页表的基地址 define pud offset dir addr pud t va
  • dracut 基本介绍

    dracut 基本介绍 dracut 维基 https dracut wiki kernel org index php Main Page http www 360doc com content 13 0428 09 12139495 2
  • linux内核学习(7)粗略走走kbuild Makefile编译流程

    今天看Makefile文件 我头大了 此Makefile非彼Makefile 里面多了很多内置命令 比如origin patsubst等等啦 这些都没听说过 更可恶的是 连网上都没有 可见 这是一件多么伤人的事情 分析这样的 真是让人折寿啊

随机推荐

  • CLion2021.2配置qt环境

    文章参考文档 使用CLion开发Qt Qt5 9 1 CLion2020 3 Clion中Cmake构建出现复制文件错误的解决办法 Clion 创建 Qt UI Class 踩坑 文章目录 1 前言 2 QT安装与环境变量的配置 3 工具链
  • Spring系列之@EnableAsync & @Async 实现方法异步调用

    1 本文内容 详解 EnableAsync Async 主要分下面几个点进行介绍 作用 用法 获取异步执行结果 自定义异步执行的线程池 自定义异常处理 线程隔离 源码 原理 2 作用 spring容器中实现bean方法的异步调用 比如有个l
  • 代码随想录算法训练营19期第44天

    完全背包 视频讲解 带你学透完全背包问题 和 01背包有什么差别 遍历顺序上有什么讲究 哔哩哔哩 bilibili https programmercarl com E8 83 8C E5 8C 85 E9 97 AE E9 A2 98 E
  • 数据工具sqoop用法之mysql与hive数据导入导出

    一 pom org apache hive hive jdbc 1 1 0 org apache hadoop hadoop common 2 6 0 org mybatis mybatis 3 4 6 mysql mysql connec
  • 别再无聊地显示隐藏了,Vue 中使用过渡动画让你的网页更有活力

    点击上方 前端Q 关注公众号 回复加群 加入前端Q技术交流群 Vue 是一款流行的前端框架 支持过渡动画的实现是其中的一项重要特性 在 Vue 中 使用过渡动画可以为用户提供更加友好的用户体验 下面我将为大家介绍一下子如何在 Vue 中实现
  • Python报错解决:local variable ‘xxx‘ referenced before assignment

    local variable xxx referenced before assignment 指的是 xxx 局部变量没有被声明 一般有如下两种情况 第一种是变量没有赋值 def test print a 这里a没有赋值 应该改成如下形式
  • 漂亮大气的企业文化宣传ppt模板

    和PPT打交道多年 从什么都不会的小白 到如今PPT制作小达人 关于有哪些好用的PPT模板资源 我还是很清楚的 学做ppt的时间成本确实是挺高的 因为这是一个需要长期磨练的技术活 故而 如若我们时间赶 任务重的话 可不必亲力亲为将自己磨练成
  • 如何使用Java操作TensorFlow

    简介 机器学习在全球范围内越来越受欢迎和使用 它已经彻底改变了某些应用程序的构建方式 并且可能会继续成为我们日常生活中一个巨大的 并且正在增加的 部分 没有什么包装且机器学习并不简单 它对许多人来说似乎非常复杂并常常令人生畏 像谷歌这样的公
  • Dubbo 与 Spring Cloud 完美结合

    概述 可能说起来Dubbo 很多人都不陌生 这毕竟是一款从2012年就开始开源的Java RPC框架 中间由于各种各样的原因停止更新4年半的时间 中间只发过一个小版本修了一个小bug 甚至大家都以为这个项目已经死掉了 竟然又在2017年9月
  • 【入门基础】写给小白看的入门级 Java 基本语法

    众所周知 Java 是一门面向对象的编程语言 它最牛逼的地方就在于它是跨平台的 你可以在 Windows 操作系统上编写 Java 源代码 然后在 Linux 操作系统上执行编译后的字节码 而无需对源代码做任何的修改 01 数据类型 Jav
  • 高并发系统 - 接口幂等技术方案,高可用系统架构与技术选型

    幂等概念来自于数学 在计算机科学中 幂等表示一次后 或多次请求某一资源 应该有同样的影响效果 在业务表现上一般是同样的数据效果 下面就常用的业务场景 来聊聊幂等的技术方案 数据层 索引与事务 根据业务需要 给表添加唯一索性或组合索引 防止产
  • python中import requests是什么意思_pycharm 中 import requests 报错

    一 使用Pycharm来抓取网页的时候 要导入requests模块 但是在pycharm中 import requests 报错 原因 python中还没有安装requests库 解决办法 1 先找到自己python安装目录下的pip 2
  • 【项目实战】与单元测试有关的Maven插件介绍 - maven-surefire-report-plugin插件说明

    一 maven surefire report plugin插件介绍 maven surefire report plugin插件是Maven的一个插件 它用于生成基于JUnit和TestNG的单元测试报告 该插件通过解析测试结果文件 生成
  • 论文阅读之Enhancing Transformer with Sememe Knowledge(2020)

    文章目录 论文阅读 Transformmer SE Transformer SP 实验结果 总结 参考 论文阅读 文章建议结合两种简单的方法将义原知识整合 1 基于语言学假设 我们将聚合义原嵌入添加到每个词嵌入中以增强其语义表示 2 我们使
  • 使用ffmpeg编码h264,降低CPU使用率的几个方法

    ffmpeg做h264编码 使用了libx264 想要降低cpu使用率 就需要牺牲图像质量 由于已经使用 baseline 就无需考虑B帧了 目前能想到的 主要是需要调控以下一些参数 1 qp值 此值范围为0 51 值越小 量化步长越小 量
  • Seaborn.load_dataset()加载数据集失败最佳解决方法

    load dataset 是 Seaborn 库中提供的一个函数 用于加载一些原始数据集 这些数据集包含了许多经典的数据集 比如鸢尾花数据集 小费数据集等 这些数据集在数据可视化和机器学习中非常常见 使用 load dataset 函数可以
  • 删除相同字符

    删除两个字符串数组中相同的字符 Description In this exercise you will get two strings A and B in each test group and the length of every
  • Golang中进行Url编码,再也不怕奇奇怪怪的参数格式了

    1 摘要 go项目在传参调用时 避免不了要在参数中有几个特殊字符 本文可告诉大家 Golang中对字符串进行URL编码的方法 将包含不可打印 保留或非ascii字符的字符串转换为可以在internet上传输的普遍接受的格式 避免大家入坑 2
  • sd卡、U盘作为启动盘后容量变小处理方法

    最近在sd卡上安装Ubuntu 发现一个问题 就是作为启动盘后 容量变小了很多 无论如何格式化和分区都不行 在Windows上 我的电脑右击选择管理 然后选择磁盘管理 发现sd卡那部分缺失的容量变绿 也就是可用空间 但无论如何都无法使用这部
  • 把握linux内核设计思想(十一):内存管理之页的分配与回收

    版权声明 尊重原创 转载请保留出处 blog csdn net shallnet 文章仅供学习交流 请勿用于商业用途 内存管理单元 MMU 负责将管理内存 在把虚拟地址转换为物理地址的硬件的时候是按页为单位进行处理 从虚拟内存的角度来看 页