Linux内存管理 | 五、物理内存空间布局及管理

img
我的圈子: 高级工程师聚集地
我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强企业!
创作理念:专注分享高质量嵌入式文章,让大家读有所得!
img

文章目录

    • 1、内存节点node
    • 2、内存区域zone
      • 2.1 各区域的布局
      • 2.2 各区域的管理
    • 3、内存页page

上章,我们介绍了物理内存的访问内存模型和组织内存模型,我们再来回顾一下:

物理内存的访问内存模型分为

  • UMA:一致内存访问
  • NUMA:非一致内存访问

物理内存的组织模型

  • FLATMEM:平坦内存模型
  • DISCONTIGMEM:不连续内存模型
  • SMARSEMEM:稀疏内存模型

Linux内核为了用统一的代码获取最大程度的兼容性,对物理内存的定义方面,引入了:内存结点(node)、内存区域(zone),内存页(page)的概念,下面我们来一一探究。

更多干货可见:高级工程师聚集地,助力大家更上一层楼!

 

1、内存节点node

内存节点的引入,是Linux为了最大程度的提高兼容性,将UMANUMA系统统一起来,对于UMA而言是只有一个节点的系统

下面的代码部分,我们尽可能的只保留暂时用的到的部分,不涉及太多的体系架相关的细节。

Linux内核中,我们使用 typedef struct pglist_data pg_data_t表示一个节点

/*
 * On NUMA machines, each NUMA node would have a pg_data_t to describe
 * it's memory layout. On UMA machines there is a single pglist_data which
 * describes the whole memory.
 *
 * Memory statistics and page replacement data structures are maintained on a
 * per-zone basis.
 */
typedef struct pglist_data {
    ...
    int node_id;
    struct page *node_mem_map;
    unsigned long node_start_pfn;
    unsigned long node_present_pages; /* total number of physical pages */
    unsigned long node_spanned_pages; /* total size of physical page
                         range, including holes */
        
    ...    
} pg_data_t;
  • node_id:每个节点都有自己的ID

  • node_mem_map:当前节点的struct page数组,用来管理这个节点的所有的页

  • node_start_pfn:这个节点的起始页号

  • node_present_pages:这个节点的真正可用的物理内存的页面数

  • node_spanned_pages:这个节点所包含的物理内存的页面数,包括不连续的内存空洞

例如,64M 物理内存隔着一个 4M 的空洞,然后是另外的 64M 物理内存。

这样换算成页面数目就是,16K 个页面隔着 1K 个页面,然后是另外 16K 个页面。

这种情况下,node_spanned_pages 就是 33K 个页面,node_present_pages 就是 32K 个页面。

 

内核使用了一个大小为 MAX_NUMNODES ,类型为 struct pglist_data 的全局数组 node_data[] 来管理所有的 NUMA 节点。

image-20231018065244586

2、内存区域zone

2.1 各区域的布局

每一个节点,都被分成了一个个区域zone,我们看一下zone的定义:

enum zone_type {
#ifdef CONFIG_ZONE_DMA
    ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
    ZONE_DMA32,
#endif
    ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
    ZONE_HIGHMEM,
#endif
    ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
    ZONE_DEVICE,
#endif
    __MAX_NR_ZONES
};

ZONE_DMA:用作DMA的内存。

DMA 是这样一种机制:要把外设的数据读入内存或把内存的数据传送到外设,原来都要通过 CPU 控制完成,但是这会占用 CPU,影响 CPU 处理其他事情,所以有了 DMA 模式。

CPU 只需向 DMA 控制器下达指令,让 DMA 控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,这样就可以解放 CPU

ZONE_DMA32:对于 64 位系统,有两个 DMA 区域。除了上面说的 ZONE_DMA,还有 ZONE_DMA32

ZONE_NORMAL:直接映射区,也就i是之前讲的从物理内存到虚拟内存的内核区域,通过加上一个常量直接映射。

ZONE_HIGHMEM:高端内存区

ZONE_MOVABLE:可移动区域,通过将物理内存划分为可移动分配区域和不可移动分配区域来避免内存碎片。

image-20231017072500328

2.2 各区域的管理

上面我们大致了解了,每个zone的布局情况,下面我们来看看内核是如何对其进行管理的。

接着上面介绍的pglist_data结构体

/*
 * On NUMA machines, each NUMA node would have a pg_data_t to describe
 * it's memory layout. On UMA machines there is a single pglist_data which
 * describes the whole memory.
 *
 * Memory statistics and page replacement data structures are maintained on a
 * per-zone basis.
 */
typedef struct pglist_data {
    ...
    int node_id;
    struct page *node_mem_map;
    unsigned long node_start_pfn;
    unsigned long node_present_pages; /* total number of physical pages */
    unsigned long node_spanned_pages; /* total size of physical page
                         range, including holes */
    ...
    
    struct zone node_zones[MAX_NR_ZONES];
    struct zonelist node_zonelists[MAX_ZONELISTS];
    int nr_zones;
    
    ...    
} pg_data_t;
  • nr_zones:用于统计 NUMA 节点内包含的物理内存区域个数,不是每个 NUMA 节点都会包含以上介绍的所有物理内存区域,NUMA 节点之间所包含的物理内存区域个数是不一样的

事实上只有第一个 NUMA 节点可以包含所有的物理内存区域,其它的节点并不能包含所有的区域类型,因为有些内存区域比如:ZONE_DMAZONE_DMA32 必须从物理内存的起点开始。这些在物理内存开始的区域可能已经被划分到第一个 NUMA 节点了,后面的物理内存才会被依次划分给接下来的 NUMA 节点。因此后面的 NUMA 节点并不会包含 ZONE_DMAZONE_DMA32 区域。

ZONE_NORMALZONE_HIGHMEMZONE_MOVABLE 是可以出现在所有 NUMA 节点上的。

  • node_zones[MAX_NR_ZONES]node_zones该数组包括了所有的zone物理内存区域
  • node_zonelists[MAX_ZONELISTS]:是 struct zonelist 类型的数组,它包含了备用 NUMA 节点和这些备用节点中的物理内存区域。

 

下面我们看一下struct zone结构体

struct zone {
......
    struct pglist_data	*zone_pgdat;
    struct per_cpu_pageset __percpu *pageset;
 
 
    unsigned long		zone_start_pfn;
 
 
    /*
     * spanned_pages is the total pages spanned by the zone, including
     * holes, which is calculated as:
     * 	spanned_pages = zone_end_pfn - zone_start_pfn;
     *
     * present_pages is physical pages existing within the zone, which
     * is calculated as:
     *	present_pages = spanned_pages - absent_pages(pages in holes);
     *
     * managed_pages is present pages managed by the buddy system, which
     * is calculated as (reserved_pages includes pages allocated by the
     * bootmem allocator):
     *	managed_pages = present_pages - reserved_pages;
     *
     */
    unsigned long		managed_pages;
    unsigned long		spanned_pages;
    unsigned long		present_pages;
 
 
    const char		*name;
......
    /* free areas of different sizes */
    struct free_area	free_area[MAX_ORDER];
 
 
    /* zone flags, see below */
    unsigned long		flags;
 
 
    /* Primarily protects free_area */
    spinlock_t		lock;
......
} ____cacheline_internodealigned_in_
  • zone_start_pfn:表示属于这个zone的第一个页
  • spanned_pages:看注释我们可以知道,spanned_pages = zone_end_pfn - zone_start_pfn,表示该区域的所有物理内存的页面数,包括内存空洞
  • present_pages:看注释我们可以知道,present_pages = spanned_pages - absent_pages(pages in holes),表示该区域真实存在的物理内存页面数,不包括空洞
  • managed_pages:看注释我们可以知道,managed_pages = present_pages - reserved_pages,被伙伴系统管理的所有页面数。
  • per_cpu_pageset:用于区分冷热页,

什么叫冷热页呢?咱们讲 x86 体系结构的时候讲过,为了让 CPU 快速访问段描述符,在 CPU 里面有段描述符缓存。CPU 访问这个缓存的速度比内存快得多。同样对于页面来讲,也是这样的。如果一个页被加载到 CPU 高速缓存里面,这就是一个热页(Hot Page),CPU 读起来速度会快很多,如果没有就是冷页(Cold Page)。由于每个 CPU 都有自己的高速缓存,因而 per_cpu_pageset 也是每个 CPU 一个。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

 

3、内存页page

内存页是物理内存最小单位,有时也叫页帧(page frame)Linux会为系统的物理内存的每一个页都创建了struct page对象,并用全局变量struct page *mem_map来存放所有物理页page对象的指针,页的大小取决于MMU(Memory Management Unit),后者主要用来将虚拟地址空间转换为物理地址空间。

看一下page的结构体

struct page {
    unsigned long flags;		/* Atomic flags, some possibly
                     * updated asynchronously */
union {
        struct {	/* Page cache and anonymous pages */
            /**
             * @lru: Pageout list, eg. active_list protected by
             * zone_lru_lock.  Sometimes used as a generic list
             * by the page owner.
             */
            struct list_head lru;
            /* See page-flags.h for PAGE_MAPPING_FLAGS */
            struct address_space *mapping;
            pgoff_t index;		/* Our offset within mapping. */
            /**
             * @private: Mapping-private opaque data.
             * Usually used for buffer_heads if PagePrivate.
             * Used for swp_entry_t if PageSwapCache.
             * Indicates order in the buddy system if PageBuddy.
             */
            unsigned long private;
        };
    
        struct {	/* slab, slob and slub */
                    union {
                        struct list_head slab_list;	/* uses lru */
                        struct {	/* Partial pages */
                            struct page *next;
        #ifdef CONFIG_64BIT
                            int pages;	/* Nr of pages left */
                            int pobjects;	/* Approximate count */
        #else
                            short int pages;
                            short int pobjects;
        #endif
                        };
            };
            
        struct kmem_cache *slab_cache; /* not slob */
                    /* Double-word boundary */
                    void *freelist;		/* first free object */
                    union {
                        void *s_mem;	/* slab: first object */
                        unsigned long counters;		/* SLUB */
                        struct {			/* SLUB */
                            unsigned inuse:16;
                            unsigned objects:15;
                            unsigned frozen:1;
                        };
                    };
                };
        .....
}

我们能够看到struct page有很多union组成,union 结构是在 C 语言中被用于同一块内存根据情况保存不同类型数据的一种方式。这里之所以用了 union,是因为一个物理页面使用模式有多种。

  • 第一种模式:直接用一整页,这一整页的物理内存直接与虚拟地址空间建立映射关系,我们把这种称为匿名页(Anonymous Page)。或者用于关联一个文件,然后再和虚拟地址空间建立映射关系,这样的文件,我们称为内存映射文件(Memory-mapped File),这种分配页级别的,Linux采用一种被称为伙伴系统(Buddy System)的技术。
  • 第二种模式:仅需要分配小的内存块。有时候,我们不需要一下子分配这么多的内存,例如分配一个 task_struct 结构,只需要分配小块的内存,去存储这个进程描述结构的对象。为了满足对这种小内存块的需要,Linux 系统采用了一种被称为slab allocator的技术

上面说的两种,都是页的分配方式,也就是物理内存的分配方式,下一章,我们继续深入分析物理内存的这两种分配方式。

img
欢迎关注 公号&星球【嵌入式艺术】,董哥原创!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/142828.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 性能调优之硬件资源监控

写在前面 考试整理相关笔记博文内容涉及 Linux 硬件资源监控常见的命名介绍,涉及硬件基本信息查看查看硬件错误信息查看虚拟环境和云环境资源理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生&#x…

如何通过把setTimeout异步转为同步

一.封装定时器函数 function delayed(time){return new Promise((resolve,reject)>{setTimeout( () > {resolve(time)}, time);}) }二调用的时候通过async await 修饰 async function demo() {console.log(new Date().getMinutes(): new Date().getSeconds())await del…

Transformers 中原生支持的量化方案概述

本文旨在对 transformers 支持的各种量化方案及其优缺点作一个清晰的概述,以助于读者进行方案选择。 目前,量化模型有两个主要的用途: 在较小的设备上进行大模型推理对量化模型进行适配器微调 到目前为止,transformers 已经集成并 原生 支持了…

VScode不打开浏览器实时预览html

下载Microsoft官方的Live Preview就行了 点击预览按钮即可预览

深圳联强优创手持PDA身份证阅读器 身份证核验手持机

身份证手持机外观比较小巧,方便携带,支持条码识别、人脸识别、NFC卡刷卡、内置二代证加密模块,可离线采集和识别二代身份证,进行身份识别。信息读取更便捷、安全高效。采用IP65高防护等级,1.5M防摔,可以适应…

RFID汽车制造工业系统解决方案

随着物联网技术的不断发展,汽车行业的信息化水平也在不断提高,随着近几年国产汽车的带动,汽车配件配套市场也已形成了一定的规模,初步形成比较完整成熟的零部件配套体系。 RFID汽车制造工业系统解决方案 与其他行业对比&#xff0…

人工智能与发电玻璃:未来能源技术的融合

人工智能与发电玻璃:未来能源技术的融合 摘要:本文探讨人工智能与发电玻璃这两项技术的结合,共同推动能源领域的创新。本文将介绍发电玻璃工作原理及应用、人工智能在发电玻璃的应用领域以及共同为可持续能源发展做出贡献。 一、引言 随着科…

Android自定义控件:一款多特效的智能loadingView

先上效果图(如果感兴趣请看后面讲解): 1、登录效果展示 2、关注效果展示 1、【画圆角矩形】 画图首先是onDraw方法(我会把圆代码写上,一步一步剖析): 首先在view中定义个属性:priv…

虹科示波器 | 汽车免拆检修 | 2010款奥迪A5车怠速时发动机偶尔自动熄火

一、故障现象 一辆2010款奥迪A5车,搭载CDN发动机,累计行驶里程约为16.3万km。车主进厂反映,发动机怠速偶尔出现抖动,紧接着自动熄火;重新起动,发动机又能正常工作;故障频率较低,有时…

Elastcsearch入门案例之 —— 搜索聚合

前言 在前面的Mall项目脚手架整合中涉及到的Elasticsearch的内容仅仅只是在表面给出了一个在SpringBoot中的使用示例,但其实对于Elasticsearch的一些基础概念和底层的原理并没有过多的涉及,这种学习方式是浮躁的,所以这篇文章荔枝会对其中欠缺…

NSSCTF题库——web

[SWPUCTF 2021 新生赛]gift_F12 f12后ctrlf找到flag [SWPUCTF 2021 新生赛]jicao——json_decode() 加密后的格式 $json {"a":"php","b":"mysql","c":3}; json必须双引号传输 构造:GET里json{"x"…

蓝桥杯 冒泡排序

冒泡排序的思想 冒泡排序的思想是每次将最大的一下一下移动到最右边,然后将最右边这个确定下来。 再来确定第二大的,再确定第三大的… 对于数组a[n],具体来说,每次确定操作就是从左往右扫描,如果a[i]>a[i1],我们将…

Linux-AWK(应用最广泛的文本处理程序)

目录 一、awk基础 二、awk工作原理 三、OFS输出分隔符 四、awk的格式化输出 五、awk模式pattern 一、awk基础 使用案例: 1.准备工作 请在Linux中执行以下指令 cat -n /etc/passwd > ./passwd 练习: 1.从文件 passwd 中提取并打印出第五行的内…

基于混合蛙跳算法优化概率神经网络PNN的分类预测 - 附代码

基于混合蛙跳算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于混合蛙跳算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于混合蛙跳优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…

《深入浅出进阶篇》洛谷P4147 玉蟾宫——悬线法dp

上链接:P4147 玉蟾宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4147 上题干: 有一个NxM的矩阵,每个格子里写着R或者F。R代表障碍格子,F代表无障碍格子请找出其中的一个子矩阵&#xff0c…

金蝶云星空设置单据体行高

文章目录 金蝶云星空设置单据体行高表单插件Python脚本 金蝶云星空设置单据体行高 表单插件 新建类继承AbstractBillPlugIn,重写OnInitialize方法进行设置 public override void OnInitialize(InitializeEventArgs e){base.OnInitialize(e);this.View.GetControl&…

卡尔曼滤波器第 1 部分 - 简介

一、说明 这是卡尔曼滤波器系列的第一部分。但这并不是另一本定义繁重的读物,它会给你带来一堆行话和方程式!在本文中,我们首先关注需要解决方案的问题(当然是卡尔曼滤波器),然后直观地了解卡尔曼滤波器。只…

CDN加速技术:节点部署的专业指南

随着互联网的迅猛发展,网站的访问量也在不断增加。为了提供更快、更稳定的用户体验,许多网站都采用了剑盾上云CDN(内容分发网络)技术。在CDN加速中,节点的合理部署是关键一环,决定了加速效果的优劣。本文将…

美国通胀预期高企,现货黄金价格继续承压下滑

上周五现货黄金持续振荡下滑,金价失守1940美元关口,最低至1933.17美元/盎司,最终收跌1.09%,报1936.51美元/盎司,创10月17日以来新低;今日(周一)截止汉声集团分析师发稿前&#xff0c…

python趣味编程-使用 Tkinter 的数字到单词转换器

数字到单词转换器应用程序是用Python编程语言编写的。该项目包含演示数字到单词转换的编码脚本。在 Python 中使用 Tkinter 的数字到单词转换器应用程序是一个应用程序,其主要目的是将您输入的数字转换为单词。数字到单词转换器应用程序提供学习资源,帮助您更好地了解Python编…