内存中的物理内存管理

概述

一般来说,linux内核一般将处理器的虚拟地址空间划分为2部分。底部比较大的部分用于用户进程,顶部则专用于内核。

在IA-32系统上,地址空间在用户进程和内核之间划分的典型比例是3:1。给出4GB的虚拟地址空间,3GB用于用户空间,而1GB用户内核。

4GB是32位系统上可以寻址的最大内存2的32次方为4GB。

在64位计算机上,由于可用的地址空间非常巨大,因此不需要搞点内存模式。

有2中类型的计算机以不同方法管理内存

  1. UMA计算机

一致性内存访问,uniform mmeory access,将可用内存以连续方式组织起来。SMP系统中的每个处理器访问各个内存区都是一样快。

  1. NUMA计算机

非一致内存访问,non-uniform memory access,多处理器计算机。系统的各个CPU都有自己本地内存,可支持特别快速访问。各个处理器通过总线连接起来,以支持对其他CPU的本地内存的访问,比访问本地内存慢些。

(N)UMA模型中的内存组织

内核对一致和非一致内存访问系统使用相同的数据结构,因此针对各种不同形式内存布局,各个算法几乎没有什么差别。在UMA系统上,只使用一个NUMA节点来管理整个系统内存。而内存管理的其他部分则相信他们是在处理一个伪NUMA系统。

首先,内存划分为节点node,用page_date_t表示。每个节点关联到系统的一个处理器。

然后内存进一步细分。比如对可用于(ISA设备的)DMA操作的内存区是有限制。只有前16M适用,还有一个高端内存区域无法映射。在2者之间还有通用的用于“普通”内存区。因此一个节点由3个内存区域组成。

  • ZONE_DMA

    标记适合DMA的区域。该区域的长度依赖于处理器类型。在IA-32计算机上,一般限制是16M。

  • ZONE_DMA32

    标记了使用32位地址可寻址、适合DMA的内存区域。显然,只有在64位系统上,2种DMA内存域才有差别。在32位计算机上,该内存是空的,即长度为 0M。在AMD64系统上,该内存的长度可能从0到4GB。

  • ZONE_NORMAL

    标记了可直接映射到内核段的普通内存区域。这是在所有体系结构上保证都会存在的唯一内存区域,但无法保证该地址范围对应了实际的物理内存。eg:在AMD64系统有2GB内存,那么所在内存都属于ZONE_DMA32范围,而ZONE_NORMAL则为空。

  • ZONE_HIGHMEM

    标记了超出内核段的物理内存。

此外内存标记了一个伪内存ZONE_MOVABLE,防止物理内存碎片的机制中需要使用该内存区域。

各个内存域都关联了一个数组,用来组织属于该内存域的物理内存页(内核称为页帧)。对于每个页帧,都分配了一个struct page 实例以及所需的管理数据。

graph TD
NODE[NODE pg_data_t] -->|ZONE| B(zone)
B --> dma[ZONE_DMA]
B --> normal[ZONE_NORMAL]
normal -->D[page]
normal -->E[page]
normal -->F[page]
B --> high[ZONE_HIGHMEM]

pg_data_t 表示节点node的基本元素

数据架构

1. 节点管理

pg_data_t 基本元素

<mmzone.h>

typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES]; //包含了节点中各内存域的数据结构
struct zonelist node_zonelists[MAX_ZONELISTS];//指定了备用节点以及其内存域的列表
int nr_zones; //节点中不同内存域的数目保存此变量中 //指向 page 实例数组的指针,用于描述结点的所有物理内存页。它包含了结点中所有内存域的页
struct page *node_mem_map; struct bootmem_data *bdata;
unsigned long node_start_pfn;
unsigned long node_present_pages; /* 物理内存页的总数 */
unsigned long node_spanned_pages; /* 物理内存页的总长度,包含洞在内 */
int node_id;
struct pglist_data *pgdat_next;
wait_queue_head_t kswapd_wait;
struct task_struct *kswapd;
int kswapd_max_order;
} pg_data_t;

2. 内存域

内存使用zone来描述内存域

<mmzone.h>

struct zone {
/*通常由页分配器访问的字段 */
unsigned long pages_min, pages_low, pages_high;
unsigned long lowmem_reserve[MAX_NR_ZONES];
struct per_cpu_pageset pageset[NR_CPUS]; /*
* 不同长度的空闲区域
*/
spinlock_t lock;
struct free_area free_area[MAX_ORDER];
ZONE_PADDING(_pad1_) /* 通常由页面收回扫描程序访问的字段 */
spinlock_t lru_lock;
struct list_head active_list;
struct list_head inactive_list;
unsigned long nr_scan_active;
unsigned long nr_scan_inactive;
unsigned long pages_scanned; /* 上一次回收以来扫描过的页 */
unsigned long flags; /* 内存域标志,见下文 */ /* 内存域统计量 */
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
int prev_priority;
ZONE_PADDING(_pad2_) ... ...
} ____cacheline_maxaligned_in_smp;

3. 页帧

页帧代表系统内存的最小单位,对内存中的每个页都会创建struct page 的一个实例。内核程序需要注意保持该结构尽可能小,系统内存同样会分解为大量的页。比如IA-32系统标准页程度为4KB,在主内存为384M时,大约共有100 000页。 内存管理的许多部分都是用页。

<mm_types.h>

struct page {
... ...
union {
atomic_t _mapcount; /* 内存管理子系统中映射的页表项计数,
* 用于表示页是否已经映射,还用于限制逆向映射搜索。
*/
unsigned int inuse; /* 用于SLUB分配器:对象的数目 */
};
... ...
}

page 的定义

<mm.h>
struct page {
unsigned long flags; /* 原子标志,有些情况下会异步更新 */
atomic_t _count; /* 使用计数,见下文。 */
union {
atomic_t _mapcount; /* 内存管理子系统中映射的页表项计数,
* 用于表示页是否已经映射,还用于限制逆向映射搜索。
*/
unsigned int inuse; /* 用于SLUB分配器:对象的数目 */
};
union {
struct {
unsigned long private; /* 由映射私有,不透明数据:
* 如果设置了PagePrivate,通常用于buffer_heads;
* 如果设置了PageSwapCache,则用于swp_entry_t;
* 如果设置了PG_buddy,则用于表示伙伴系统中的阶。
*/
struct address_space *mapping; /* 如果最低位为0,则指向inode
* address_space,或为NULL。
* 如果页映射为匿名内存,最低位置位,
* 而且该指针指向anon_vma对象:
* 参见下文的PAGE_MAPPING_ANON。
*/
};
...
struct kmem_cache *slab; /* 用于SLUB分配器:指向slab的指针 */
struct page *first_page; /* 用于复合页的尾页,指向首页 */
};
union {
pgoff_t index; /* 在映射内的偏移量 */
void *freelist; /* SLUB: freelist req. slab lock */
};
struct list_head lru; /* 换出页列表,例如由zone->lru_lock保护的active_list!
*/ #if defined(WANT_PAGE_VIRTUAL)
void *virtual; /* 内核虚拟地址(如果没有映射则为NULL,即高端内存) */
#endif /* WANT_PAGE_VIRTUAL */
};

上面的代码看起来很多,要了解具体的步骤还是参考相应的书籍来看

页表

层次化的页表用于支持大地址空间快速、高效的管理。

页表用于建立用户进程的虚拟地址空间和系统物理内存(内存、页帧)之间的关联。页表用于向每个进程提供一致的虚拟地址空间。应用程序看到的地址空间是一个连续的内存区。该表也将虚拟地址映射到了物理内存,因此支持共享内存实现。

初始化内存管理

内存初始化,在许多CPU上,必须显示设置适用于Linux内核的内存模型。例如在IA32系统上必须先切换到保护模式,然后内核才能检测可用内存和寄存器。在初始化过程中,还必须建立内存管理的数据结构,一起许多其他事务。

start_kernel的代码流程图:

graph TD
a[start_kernel] --> b[setup_arch]
b-->c[setup_pr_cpu_areas]
c-->d[build_all_zonelists]
d-->e[mem_init]
e-->f[setup_per_cpu_pageset]
  • setup_arch 是一个特定于体系结构的设置函数,其中一项任务是负责初始化自举分配器。
  • 在SMP系统上, setup_per_cpu_areas 初始化源代码中(使用 per_cpu 宏)定义的静态 per-cpu

    变量,这种变量对系统中的每个CPU都有一个独立的副本。此类变量保存在内核二进制映像的

    一个独立的段中。 setup_per_cpu_areas 的目的是为系统的各个CPU分别创建一份这些数据

    的副本。

    在非SMP系统上该函数是一个空操作。
  • build_all_zonelists 建立结点和内存域的数据结构(见下文)。
  • mem_init 是另一个特定于体系结构的函数,用于停用bootmem分配器并迁移到实际的内存管

    理函数,稍后讨论。
  • kmem_cache_init 初始化内核内部用于小块内存区的分配器。
  • setup_per_cpu_pageset 从上文提到的 struct zone ,为 pageset 数组的第一个数组元素分配

    内存。分配第一个数组元素,换句话说,就是意味着为第一个系统处理器分配。系统的所有

    内存域都会考虑进来。

物理内存的管理

内核初始化完成后,内存管理的责任就由伙伴系统承担了。

伙伴系统的结构

系统内存中的每个物理内存页(页帧),都对应一个struct page实例。 每个内存域都关联了一个struct zone的实例。

<mmzone.h>

struct zone {
... ...
/*
* 不同长度的空闲区域
*/
struct free_area free_area[MAX_ORDER];
... ...
};

free_area 是一个辅助数据结构

<mmzone.h>

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

nr_free 自定了当前内存区中空间页块的数目(对0阶内存逐页计算,对1阶内存区计算页对的数目,对2阶内存区计算页对的数据,依次类推)。

free_list 用于连接空闲的链表。

free_area[] 数组中各个元素的所有也解释为阶,用于指定对于链表中的连续内存区包含多少个帧。 第0个链表包含的内存为单页2的零次方为1,第1个链表管理的内存区为2页(2的一次方),依次类推。

slab分配器

内核也经常分配内存。但是用上面伙伴系统分配内存太大了。如果需要一个10个字符的字符串分配空间,分配一个4KB或者更多空间的页面,浪费空间。解决方案就是将页拆分为更小的单位,可以容纳大量的小对象。

slab不仅能作为小内存的分配器,也可以作为一种缓存使用,主要是针对经常分配并释放的对象。通过建立slab缓存,内核就能储备一些对象,供后续使用。

Linux内存:物理内存管理概述的更多相关文章

  1. Linux内存描述之概述--Linux内存管理(一)

    1 前景回顾 1.1 UMA和NUMA两种模型 共享存储型多处理机有两种模型 均匀存储器存取(Uniform-Memory-Access,简称UMA)模型 将可用内存以连续方式组织起来, 非均匀存储器 ...

  2. linux 内存地址空间管理 mm_struct

    http://blog.csdn.net/yusiguyuan/article/details/39520933 Linux对于内存的管理涉及到非常多的方面,这篇文章首先从对进程虚拟地址空间的管理说起 ...

  3. Linux软件安装管理概述

    介绍如何在Linux字符界面下安装软件 课程大纲: 一.软件包管理简介 二.rpm命令管理 三.yum在线管理 四.源码包管理 五.脚本安装包

  4. LInux中的物理内存管理

    2017-02-23 一.伙伴系统 LInux下用伙伴系统管理物理内存页,伙伴系统得益于其良好的算法,一定程度上可以避免外部碎片为何这么说?先回顾下Linux下虚拟地址空间的分布. 在X86架构下,系 ...

  5. Linux内存管理的基本框架⭐⭐

    Linux内核的映射机制设计成三层,在页面目录和页面表中间增设了一层“中间目录”.在代码中,页面目录称为PGD,中间目录称为PMD,而页面表称为PT.PT中的表项称为PTE,PTE是“Page Tab ...

  6. [转帖]Linux分页机制之概述--Linux内存管理(六)

    Linux分页机制之概述--Linux内存管理(六) 2016年09月01日 19:46:08 JeanCheng 阅读数:5491 标签: linuxkernel内存管理分页架构更多 个人分类: ┈ ...

  7. 启动期间的内存管理之初始化过程概述----Linux内存管理(九)

    在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检 ...

  8. Linux内存管理 (1)物理内存初始化

    专题:Linux内存管理专题 关键词:用户内核空间划分.Node/Zone/Page.memblock.PGD/PUD/PMD/PTE.lowmem/highmem.ZONE_DMA/ZONE_NOR ...

  9. 非常好的博客!!!linux内存管理概述【转】

    转自:http://blog.csdn.net/bullbat/article/details/7166140 inux内存管理建立在基本的分页机制基础上,在linux内核中RAM的某些部分将会永久的 ...

随机推荐

  1. Codeforces 999

    A /*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std ...

  2. P1879 [USACO06NOV]玉米田Corn Fields[轮廓线DP]

    状压暴力显然可做.但是数据出的再大一点就要稳T了.理论$O(n4^m)$,只不过实际跑不满. 考虑用轮廓线DP,设$f(i,j,S)$为处理到$(i,j)$时候(这格还不确定)的轮廓线为$S$的情况( ...

  3. fedora29 安装mongodb 4.0,6问题记录

    如果运行mongod命令时提示 无加载共享库libcrypto.so.10,那就到页面下载http://www.rpmfind.net/linux/rpm2html/search.php?query= ...

  4. MHA配置

    1,背景 MHA的目的在于维持MySQL Replication中Master库的高可用性,其最大特点是可以修复多个Slave之间的差异日志,最终使所有Slave保持数据一致,然后从中选择一个充当新的 ...

  5. Open Cascade 转化为OpenSceneGraph中的Mesh

    #include <osgDB/ReadFile> #include <osgViewer/Viewer> #include <osgGA/StateSetManipul ...

  6. JavaWeb_(SSH论坛)_四、页面显示

    基于SSH框架的小型论坛项目 一.项目入门 传送门 二.框架整合 传送门 三.用户模块 传送门 四.页面显示 传送门 五.帖子模块 传送门 六.点赞模块 传送门 七.辅助模块 传送门 帖子表与回复表 ...

  7. 如何将项目托管到Github上

    将本地项目放到GitHub上托管并展示 传送门 利用Github Pages展示自己的项目 传送门 git Please tell me who you are解决方法 传送门 git config ...

  8. JS框架_(AJAX)检测ip和地区

    百度云盘 传送门  密码:l94p 实现效果: <!DOCTYPE html> <html> <head> <meta charset="utf-8 ...

  9. centOS7安装docker遇到 [Errno 14] curl#35 - "TCP connection reset by peer问题解决

    ---------------------------------------------------------------------------------------------------- ...

  10. SQL中模糊查询的模式匹配

    SQL模糊查询的语法为: “Select column FROM table Where column LIKE 'pattern'”. SQL提供了四种匹配模式: 1. % 表示任意0个或多个字符. ...