📝前言:
这篇文章我们来讲讲Linux------分布式存储管理
🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏
分布式存储管理
- [一, 物理内存管理](#一, 物理内存管理)
- 二,两级页表的地址转换
-
- [1. 页表](#1. 页表)
- [2. 页目录结构](#2. 页目录结构)
- [3. 地址转换过程](#3. 地址转换过程)
- [4. TLB](#4. TLB)
- [三. 缺页异常](#三. 缺页异常)
-
- 其他问题理解
-
- [1. 理解malloc和new](#1. 理解malloc和new)
- [2. 理解写时拷贝](#2. 理解写时拷贝)
一, 物理内存管理
我们之前谈磁盘的时候说数据是按块(4KB)存储的,实际上内存也是会按4KB大小来划分的。(物理内存和磁盘之间,以4KB为单位交换)
4GB 的空间就是4GB/4KB = 1048576
个块,我们把这个块称为:页框 page
(一个页框对应到虚拟地址里称为页)
所有的页框,也需要描述 + 组织 ,在内核中用结构体struct page
cpp
/* include/linux/mm_types.h */
struct page
{
/* 原⼦标志,有些情况下会异步更新 */
unsigned long flags;
union
{
struct
{
struct list_head lru;
struct address_space *mapping;
/* 在映射内的偏移量 */
pgoff_t index;
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;
};
};
};
...
};
union
{
atomic_t _mapcount;
unsigned int page_type;
unsigned int active; /* SLAB */
int units; /* SLOB */
};
...
#if defined(WANT_PAGE_VIRTUAL)
void *virtual;
#endif /* WANT_PAGE_VIRTUAL */
...
}
- flags :用来存放页框的状态。这些状态包括页框是不是脏的,是不是被锁定在内存中等。
flag
的每⼀位单独表⽰⼀种状态,所以它⾄少可以同时表示出32种不同的状态 - _mapcount :表示在页表中有多少项指向该页框,也就是这⼀页框被引用了多少次。当计数值变为-1时,就说明当前内核并没有引用这⼀页框,于是在新的分配中就可以使用它
- virtual :是页框的虚拟地址。通常情况下,它就是页框在虚拟内存中的地址
所有的Page
通过一个全局的 struct page 数组
来管理,每个下标就对应一个Page
二,两级页表的地址转换
1. 页表
实际上,页表中并不是存储每个字节的地址,而页(Page)级别的映射。即:存储每个页框的起始虚拟地址,然后映射到页框的起始物理地址
如果整个物理内存映射到整个虚拟空间上,则如下如:
4GB/4KB = 1048576
,所以页表中只需要存储2^20
个地址。
但是,如果每个页表都这么大,还是太多了,于是就有了利用页目录结构的分级页表。
2. 页目录结构
把拥有2^20
个项的页表再按2^10
为一块划分,分成2^10
个2^10
块,于是第一个2^10
成了页目录,第二个2^10
才对应到真正的页表。

这就是二级页表结构,32位机器就常用这种划分方法
- 前
31- 22
位表示页目录的下标,第21 - 12
位表示页表内的下标 - 最后
11- 0
位(低12
位)则是具体的地址在页框内的偏移量 - 所以我们通过虚拟地址映射到页框的起始物理地址,然后起
始物理地址 + 偏移量
,就可以定位到某个字节的地址
则,在这种结构下:
- 每个进程有自己的页目录(PDE 表,一级表),存储在内存中,并由 CR3 寄存器(x86)指向其物理地址。
- 页表(PTE 表,二级表) 按需分配,只有当进程实际使用某块虚拟地址时,才会分配对应的页表。
- 如果某块 4MB 的虚拟地址范围未使用,对应的页表可以不分配(只需在页目录中标记 P=0),而不是占用完整的 2^20 个 PTE。
3. 地址转换过程
假设我们现在拿到了一个虚拟地址,转换流程:
- 从 CR3 寄存器获取页目录的物理地址(CR3 存储当前进程的页目录基址)。
- 用虚拟地址的
31-22
位作为页目录索引 ,找到对应的 PDE。- 检查 PDE 的 P 位,如果为
0
,触发 缺页异常(Page Fault)。
- 检查 PDE 的 P 位,如果为
- 从 PDE 中取出页表的物理地址。
- 用虚拟地址的
21-12
位作为页表索引 ,找到对应的 PTE。- 检查 PTE 的 P 位,如果为
0
,触发 缺页异常。
- 检查 PTE 的 P 位,如果为
- 从 PTE 中取出物理页框号(PFN)(页框的物理起始地址)。
- 物理地址 = (PFN << 12) | 页内偏移(低 12 位)
以上工作都是由MMU
(内存管理单元)硬件完成
但是,每次MMU都要先进行两次页表查询确定物理地址还是太慢了,于是就有了快表TLB(缓存)
【我们可以通过物理地址来找到对应的虚拟地址,有对应的宏可以帮助我们实现,原理也很简单,只需要针对性的拿地址中的数字,反映的就是下标】
4. TLB
- 当CPU给MMU发了一个新的虚拟地址以后,MMU先去TLB里面找有没有
- 如果没有,就再进行两次页表查询,找到了以后吧地址返回给CPU,并且把这条映射关系给到TLB,让它记录一下
三. 缺页异常
当CPU 给 MMU 的虚拟地址,在 TLB 和页表都没有找到对应的物理页时,会引发缺页异常 Page Fault
,它是⼀个由硬件中断触发的可以由软件逻辑纠正 的错误。
缺页中断会交给 PageFaultHandler 处理,其根据缺页中断的不同类型会进行不同的处理:
Hard Page Fault
(硬缺页错误/主要缺页错误):这时物理内存中没有对应的物理页,需要CPU打开磁盘设备读取到物理内存中,再让MMU建⽴虚拟地址和物理地址的映射。Soft Page Fault
(软缺页错误/次要缺页错误):这时物理内存中是存在对应物理页的,只不过可能是其他进程调入的(比如动态库),发出缺页异常的进程不知道而已,此时MMU只需要建立映射即可。Invalid Page Fault
(无效缺页错误) :如,内存地址越界访问,对空指针解引用等,内核就会报segment fault
错误,中断进程直接挂掉
其他问题理解
1. 理解malloc和new
- 实际上
malloc
和new
开空间并没有开实际的物理空间,而是只分配了进程地址空间。(当虚拟空间分配好以后,OS就认为这是合法的,属于该进程的空间了,用mm_struct
记录) - 当真正要使用的时候,会触发中断错误,CPU去执行中断处理方法,然后真正分配空间,建立映射
- 分配内存本质上是:找到空闲的物理页框,并填充到页表中
- 这是一种内存的延迟申请,可以实现把"暂时没用到"的物理内存先给别人用
所以我们申请内存,实际上只是申请的进程地址空间
2. 理解写时拷贝
- 子进程和父进程指向的其实是同一个页框,权限管理以页框为单位
- 当一个进程写入时,发现权限不对,触发缺页
- 然后,CPU就会去中断向量表执行中断处理方法,对整个页框写时拷贝
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!