《深入Linux内核架构》第3章 内存管理(1)

目录

3.1 概述

3.2 NUMA模型的内存组织

3.2.1 概述

3.2.2 三个数据结构

3.2.2.1 node

3.2.2.2 zone

3.2.2.3 page


本专栏文章将有70篇左右,欢迎+关注,订阅后续文章。

本章讲物理内存的管理,而不是虚拟内存地址空间。

3.1 概述

页帧:指物理内存的一页,通常4K大小。

        每页用一个struct page实例表示。

内存管理包含:

        大块内存管理:伙伴系统

        小块内存管理:slab slub slob kmalloc

        物理非连续内存管理:vmalloc

        物理内存页帧管理:struct page

        进程地址空间

PAE机制:(Physical Address Extension,物理地址扩展)

        作用:扩展了32位x86 CPU的物理内存寻址范围,允许访问超过2^32(4GB)的物理内存。

PAE实现原理:

        1. 物理地址扩展:

                地址总线从32位扩展到36位。可寻址64GB物理内存。

        2. 页表结构改变:

                PAE引入了更复杂的页表结构,以支持更大的物理地址范围。

注意:虽然内核可访问更大的物理内存地址,但线性地址仍是32位,所以单进程仍只能访问32位虚拟地址空间。

UMA:Uniform Memory Access

        即一致内存访问。

        含义:所有CPU共享同一内存,所有CPU访问该内存速度一样。

        适用对象:小型系统。

NUMA:Non-Uniform Memory Access

        含义:

                每个CPU有自己的本地内存节点。

                CPU访问本地内存节点比其他CPU的内存节点更快。

    适用对象:大型系统。

个人电脑大部分采用UMA,而服务器通常采用NUMA。

SMT:同时多线程技术(超线程)

        作用:

                在一个CPU核中引入多个线程处理单元,当一个线程在等待某些资源时,可切换到另一个线程执行,提高整体性能。

        缺点:

                1. 资源竞争。因为多个线程共享同一CPU核资源。

                2. CPU缓存抖动。因为多个线程共享cache。

DRAM:动态随机存储器,用作主存。

SRAM:静态随机存储器,用作CPU缓存。

三种内存组织类型:

        1. FLATMEM:

                即平坦内存模型。

                最简单。整个物理内存视为一个连续区域。

                缺点:只支持单个内存。

        2. DISCONTIGMEM:

                 即非连续内存模型。

                整个物理内存被分成多个不连续的区域,区域之间有空洞。每个区域属于一个内存结点。

                场景:用于NUMA系统。

                缺点:NUMA节点粒度太粗,不支持内存热插拔。

        3. SPARSEMEM:

                即稀疏内存模型。

                某些区域连续,而另一些不连续。

                场景:也用于NUMA系统。

                在DISCONTIGMEM基础上优化。

DISCONTIGMEM和SPARSEMEM比较:

        DISCONTIGMEM:更稳定,但不支持内存热拔插。

        SPARSEMEM:不稳定,但支持内存热拔插。

        相同:都支持NUMA。

总结:

        UMA系统:适合FLATMEM。用于个人电脑。

        NUMA系统:适合DISCONTIGMEM,SPARSEMEM。用于服务器。

物理内存页框号(PFN)与struct page互相转换:

        struct page         *pfn_to_page(unsigned long pfn)

        unsigned long         page_to_pfn(struct page *page)

内核中分配内存页时,分配单位为:order。

        分配数量为2的order次方。

3.2 NUMA模型的内存组织

UMA:只有一个内存节点,所有CPU共享。

NUMA:每个CPU都有一个本地内存节点。

       

每个内存节点用一个 pg_data_t 实例表示。

3.2.1 概述

将物理内存分三级结构:

        节点(node)、区域(zone)、页(page)

1. node:

        UMA中只有一个内存node。而NUMA中每个CPU都有一个内存node。

        配置选项:CONFIG_NUMA

2. zone:

        根据用途和属性不同,每个内存node可分为多个zone。如:

        enum zone_type {

                ZONE_DMA,

                ZONE_DMA32,

                ZONE_NORMAL,           正常可寻址区域

                ZONE_HIGHMEM,         高端内存区,64位系统不需要。

                ZONE_MOVABLE,         伪区域,用于内存热插拔/迁移,防内存碎片

                __MAX_NR_ZONES

        };

ZONE_DMA:

        用于DMA设备访问物理内存前16MB。

        老式ISA DMA设备会使用。

ZONE_DMA32:

        用于支持32位系统中DMA设备访问内存16MB到4GB范围。

        现代DMA设备使用。

ZONE_HIGHMEM:

        高端内存区。

        问题:32位系统中虚拟内核空间只有1GB(范围3 - 4GB),无法映射访问全部4G的物理内存。

        解决方法:使用128M虚拟内核空间,动态映射到物理内存的高端内存,用完解除映射。

ZONE_MOVABLE:

        伪区域,用于内存热插拔,内存迁移,内存碎片整理。

3. page:

        将物理内存分为一个个页帧,即page,通常一个page大小为4KB,每个zone包含一组page。

        page是虚拟内存映射到物理内存的最小单位。

小结:

        每个node包含多个zone。

        每个zone包含多个page。

如下图:

系统调用

long migrate_pages(int pid, unsigned long maxnode,

                unsigned long *old_nodes,

                unsigned long *new_nodes);

        作用:

                将指定进程的页面从old内存节点迁移到new内存节点。

        举例:

                将进程的页面迁移到本地CPU内存节点上。

        优点:

                减少NUMA中内存访问延迟。

3.2.2 三个数据结构

本节讲node,zone,page三者数据结构。

3.2.2.1 node

node数据结构:pg_data_t。

每个NUMA节点都有一个pg_data_t实例。

系统所有node保存在pg_data_t node_data[nid];数组中。

typedef struct pglist_data {
        struct zone         node_zones[MAX_NR_ZONES];

                //该节点所有的zone。

       

        struct zonelist         node_zonelists[MAX_ZONELISTS];

                //用于快速查找特定zone,便于内存分配和回收操作。

        

        int         nr_zones;

                //该节点中zone数量。

       

        struct page         *node_mem_map;

                //该节点所有页。

     

        struct bootmem_data         *bdata;

                //bootmem分配器,用于内核初始化时分配内存。

     

        unsigned long         node_start_pfn;

                //该节点起始页帧号,所有节点统一编号。

                //UMA只有一个节点,node_start_pfn为0。

       

        unsigned long         node_present_pages;

                //该节点中物理页总数 。

       

        unsigned long         node_spanned_pages;

                //该节点中物理页总数,包含空洞。

        int         node_id;

                //全局节点ID

        struct pglist_data         *pgdat_next;

                //下一个节点

        struct task_struct         *kswapd;

                //该节点的页回收线程,当内存压力高时唤醒线程,执行内存回收。

       

        int         kswapd_max_order;

                //kswapd线程一次可回收的最大页数,防止回收过多。

     

        wait_queue_head_t         kswapd_wait;

                //其他线程在该等待队列上睡眠,等待kswapd线程完成内存回收。

     

        wait_queue_head_t         pfmemalloc_wait;

                //当一个内核线程需分配内存以完成紧急任务(如中断处理),但内存不足,此时把线程挂起并加入到pfmemalloc_wait等待队列。

                //当内存足够时,唤醒等待队列中线程,以继续分配内存。

   

        struct task_struct         *kcompactd;

                //内存规整线程。内存碎片化达到一定程度时被唤醒,用于碎片整理。

       

        wait_queue_head_t         kcompactd_wait;

                //其他线程在该等待队列上睡眠,等待kcompactd线程完成内存规整。

       

        int         kcompactd_max_order;

                //kcompactd线程一次可规整允许最大页数。避免移动过多内存。

} pg_data_t;

成员介绍:

struct zonelist node_zonelists[MAX_ZONELISTS];

        MAX_ZONELISTS= 2

        [0]: 表示Zonelist with fallback

                备用zone列表,按优先级先后包含所有节点的zonelist。

                内存分配时,如果高优先级zone无法满足分配请求,会遍历下一个低优先级zone。

        [1]: 表示No fallback

                只包含本地节点的zone。

                使用GFP_THISNODE标志分配内存时会遍历该node_zonelists中zone。

如何从指定内存结点中分配内存?

        void *kmalloc_node(size_t size, gfp_t flags, int node)

        void *vmalloc_node(size_t size, int node);

        struct page *alloc_pages_node(int nid, gfp_t gfp_mask,

                unsigned int order)

节点状态
enum node_states {
        N_POSSIBLE,

        N_ONLINE,

        N_NORMAL_MEMORY,         该节点包含normal zone

        N_HIGH_MEMORY,                 该节点包含高端内存zone

        N_CPU,

        NR_NODE_STATES

};

N_POSSIBLE:

        系统启动时,所有NUMA节点都会被标记为 N_POSSIBLE

        表示节点已准备好进入下一个状态。

N_ONLINE:

        表示节点已完全初始化并可供进程使用。

N_CPU:

        节点是CPU的本地内存结点。

节点状态函数:

        void node_set_state(int node, enum node_states state);

        void node_clear_state(int node, enum node_states state);

3.2.2.2 zone

        下节讲

3.2.2.3 page

        下下节讲

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

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

相关文章

DC/DC电源模块直流升压变换器电压控制输出5V12V24V转0-50V80V110V150V180V200V250V300V500V800V1000V

特点 效率高达 75%以上1*2英寸标准封装单电压输出可直接焊在PCB 上工作温度: -40℃~75℃阻燃封装,满足UL94-V0 要求温度特性好电压控制输出,输出电压随控制电压线性变化 应用 GRB 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为:4.5~9V、…

记录Python的pandas库详解

如何生成一个pd import pandas as pd df pd.DataFrame([[1,2,3],[4,5,6]],index[A,B],columns[C1,C2,C3])df ---------------------------------------------------------------------------C1 C2 C3 A 1 2 3 B 4 5 6df.T -------------------------------------------------…

字节8年经验之谈 —— 接口测试框架接入性能测试实践分享!

1. 前言 现如今接口测试在软件质量行业中的地位,已经越来越重要,相对于上层的UI自动化测试和下层的单元测试,接口测试的“低”投入、“高”回报,也成了绝大多数质量保障实践的首选。 在开展接口测试时,往往很多时候都…

python使用redis存储时序数据

import redisdef ts_demo():"""时序数据存储RedisTimeSeries测试"""# 连接到Redisr redis.Redis(hostlocalhost, password"xxxx", port63790, db0)r1 r.ts()# print(r1.get("ts_key"))# print(r.exists(ts_key))# # 清空键…

PyQt5 快速入门

PyQt5 简介和开发环境搭建 简介 PyQt是一个GUI小部件工具包。 它是Qt的Python接口, Qt是最强大,最受欢迎的跨平台GUI库之一。 PyQt由RiverBank Computing Ltd.开发。最新版本的PyQt可从其官方网站下载 - riverbankcomputing.com PyQt API是一组包含大…

【leetcode】双指针算法技巧——滑动窗口

标题:【leetcode】双指针算法技巧——滑动窗口 水墨不写bug 正文开始: 滑动窗口介绍 滑动窗口是一种常用的算法技巧,用于解决一些涉及 连续子数组或子串 的问题。它的基本思想是 维护一个窗口,通过 在窗口内移动 来寻找满…

SD-WAN为什么能让网络运维更简单

在数字化浪潮席卷的今天,企业面临着网络规模的不断扩大、分支机构的广泛分布以及云服务需求的日益增长。企业目前的网络不足以达到这些要求,因而出现网络性能下降、运维复杂度剧增、成本攀升的问题。而SD-WAN(软件定义广域网)作为…

SASE:打造数据安全保障新模式

在企业纷纷拥抱数字业务的过程中,由于边缘计算、云服务、混合网络的逐渐兴起,使得本就漏洞百出的传统网络安全架构更加岌岌可危,而且远远无法满足企业数字业务的需要。 伴随企业全球化发展,企业的数据中心不再是用户与设备访问需…

socket编程——tcp

在我这篇博客:网络——socket编程中介绍了关于socket编程的一些必要的知识,以及介绍了使用套接字在udp协议下如何通信,这篇博客中,我将会介绍如何使用套接字以及tcp协议进行网络通信。 1. 前置准备 在进行编写代码之前&#xff…

Docker部署Prometheus+AlertManager实现邮件告警

文章目录 一、环境准备1、硬件准备(虚拟机)2、关闭防火墙,selinux3、所有主机安装docker 二、配置Prometheus1、docker启动Prometheus 三、添加监控节点1、docker启动node-exporter 四、Prometheus配置node-exporter1、修改prometheus.yml配置…

Docker构建Golang项目常见问题

Docker构建Golang项目常见问题 1 dockerfile报错:failed to read expected number of bytes: unexpected EOF2 go mod tidy: go.mod file indicates go 1.21, but maximum supported version is 1.17 1 dockerfile报错:failed to read expected number o…

鸿蒙语言TypeScript学习第18天:【泛型】

1、TypeScript 泛型 泛型(Generics)是一种编程语言特性,允许在定义函数、类、接口等时使用占位符来表示类型,而不是具体的类型。 泛型是一种在编写可重用、灵活且类型安全的代码时非常有用的功能。 使用泛型的主要目的是为了处…

【Linux】详解如何利用共享内存实现进程间通信

一、共享内存(Shared Memory)的认识 共享内存(Shared Memory)是多进程间共享的一部分物理内存。它允许多个进程访问同一块内存空间,从而在不同进程之间共享和传递数据。这种方式常常用于加速进程间的通信,因…

JS打包工具 Vite

Vite是 JS 新一代的打包的工具,它所解决的问题,是前端打包慢的问题,随着前端应用复杂度越来越大,项目文件越来越多,通常项目中都是使用 Webpack 进行打包,Webpack是个静态的打包工具,每次改动都…

9.Jetson AGX Orin protobuf验证

Jetson AGX Orin protobuf验证 前提已经安装好grpc 1:进入目录grpc/examples/cpp/helloworld下编译 make如果出现错误,protoc: Command not found。 进入/usr/local/bin与/usr/local/lib 均没发现protoc与libprotobuf。原来/grpc/third_party/protob…

C语言【指针】

1. 基本语法 1.1 指针变量的定义和使用(重点) 指针是一种数据类型,指针变量指向谁 就把谁的地址赋值给指针变量 1.2 通过指针间接修改变量的值 指针变量指向谁 就把谁的地址赋值给指针变量 可以通过 *指针变量 间接修改变量的值 1.3 const修饰的指针变量 语法…

C语言 【函数】

1.函数概述 函数是一种可重用的代码块&#xff0c;用于执行特定任务或完成特定功能 函数作用&#xff1a;对具备相同逻辑的代码进行封装&#xff0c;提高代码的编写效率&#xff0c;实现对代码的重用 2. 函数的使用 2.1 无参无返回值 #include <stdio.h>// 函数名…

【AAAI2024】点云的自适应邻域提取

论文标题&#xff1a;Point Deformable Network with Enhanced Normal Embedding for Point Cloud Analysis 论文地址&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/28497 两个创新点&#xff1a;可变邻域法向量提取 一、由固定邻居变为可变的邻域 二、最小二…

让一个元素在网页上跟随网页窗口大小变化始终保持上下左右居中

废话少说&#xff0c;直接上代码&#xff0c;懂的都懂&#xff1a; <!DOCTYPE html> <html style"font-size: 100px;"> <head><meta http-equiv"Content-Type" content"text/html;charsetUTF-8"><style type"te…

搭建第一个Web服务器(在eclipse或idea上部署Tomcat服务器)

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…