操作系统 3.4-段页结合的实际内存管理

段与页结合的初步思路

  1. 虚拟内存的引入

    • 为了结合段和页的优势,操作系统引入了虚拟内存的概念。虚拟内存是一段地址空间,它映射到物理内存上,但对用户程序是透明的。

  2. 段到虚拟内存的映射

    • 用户程序中的段首先映射到虚拟内存的相应区域。这一步骤通过段表(Segment Table)实现,段表记录了每个段的起始地址和长度。

  3. 虚拟内存到物理内存的映射

    • 虚拟内存中的区域再映射到物理内存的页上。这一步骤通过页表(Page Table)实现,页表记录了虚拟页到物理页框的映射关系。

  4. 地址翻译

    • 用户程序发出的地址首先被翻译成虚拟地址,然后再翻译成物理地址。这一过程通常由硬件中的内存管理单元(MMU)自动完成。

  5. 内存访问

    • 一旦地址被翻译成物理地址,CPU就可以访问物理内存中的数据,执行指令或进行数据操作。

用户从应用程序视角看待虚拟内存中的段和页

从用户程序的视角来看,虚拟内存中的段和页提供了一种抽象的内存模型,使得程序能够以一种简化和统一的方式来访问内存。以下是用户程序如何查看和与虚拟内存中的段和页交互的方式:

  1. 逻辑地址空间

    • 用户程序操作的是逻辑地址空间,这是一个由操作系统管理的虚拟地址空间。在这个空间中,程序可以使用段和偏移量(如cs:ip)来访问内存,而不需要关心物理内存的实际布局。

  2. 地址翻译

    • 用户程序发出的地址(如0x00345008)首先被翻译成虚拟地址,然后再由操作系统和硬件(如内存管理单元MMU)翻译成物理地址(如0x7008)。

    • 这个过程对用户程序是透明的,用户程序不需要知道具体的物理地址。

  3. 内存保护和隔离

    • 段和页的结合允许操作系统实现内存保护和隔离。每个进程都有自己的虚拟地址空间,操作系统确保一个进程不能访问另一个进程的内存空间。

  4. 虚拟内存的优势

    • 虚拟内存提供了内存的抽象,使得程序可以假设它拥有一个连续的、大的内存空间,而实际上物理内存可能是分散的、有限的。

    • 这种抽象简化了程序设计,提高了内存使用的灵活性,并允许操作系统更有效地管理内存资源。

重定位

逻辑地址到物理地址的转换过程

  1. 逻辑地址:由段号和偏移量组成,例如 段号+偏移(cs:ip)

  2. 段表:包含段号、基址、长度和保护信息。

    • 段号:标识段的编号。

    • 基址:段在内存中的起始地址。

    • 长度:段的大小。

    • 保护:段的访问权限(如只读R、读写R/W)。

  3. 页号和偏移:逻辑地址中的偏移量被进一步分解为页号和页内偏移。

  4. 页表:包含页框号和保护信息。

    • 页框号:页在内存中的物理位置。

    • 保护:页的访问权限(如只读R、读写R/W)。

  5. 物理地址:由物理页号和偏移量组成。

地址翻译过程

  1. 逻辑地址:由段号和偏移量组成。

  2. 段表查找:根据段号查找段表,获取基址和长度。

  3. 偏移分解:将偏移量分解为页号和页内偏移。

  4. 页表查找:根据页号查找页表,获取页框号。

  5. 物理地址计算:将页框号和页内偏移组合成物理地址。

段页同时存在的场景下,重定位过程是怎样的

在断页同时存在的场景下,用户发出的逻辑地址是CS加上段号和偏移量。

操作系统首先通过段表找到段在虚拟内存中的位置,并生成一个虚拟地址。

然而,这个虚拟地址并不是直接对应的物理内存地址,而是需要经过一次映射,根据虚拟地址计算出页号,再结合页内偏移得到物理地址。

最后,操作系统将这个物理地址发送到地址总线上,从而实现从段和页两个层次上的地址翻译,确保代码能够正确执行和获取数据。

一个实际的段页结合的例子

内存分配

内存管理的核心

  • 内存管理核心就是内存分配:强调了内存分配是内存管理的关键部分。

具体示例

  • 指令_sum: .int 0 定义了一个名为 _sum 的变量,初始值为0。

  • 指令_main: mov [300], 0 将0移动到偏移量为300的地址。

  • 地址计算base + 300(offset) 表示将基地址和偏移量相加得到物理地址。

内存管理过程

  • 分配段、建段表:为进程分配内存段,并建立段表来管理这些段。

  • 分配页、建页表:为进程分配内存页,并建立页表来管理这些页。

  • 进程带动内存使用的图谱:展示了进程如何使用内存的图谱。

  • 从进程fork中的内存分配开始:说明了进程创建(fork)时的内存分配过程。

载入内存

程序加载过程

  1. 分配段:为程序的不同部分(如代码、数据、栈)分配内存段。

  2. 建立段表:记录每个段的基址、长度和保护属性。

  3. 分配页:将每个段进一步分为页,并为每页分配物理内存。

  4. 建立页表:记录每个页的页框号和保护属性。

  5. 地址转换:将程序中的逻辑地址转换为物理地址,以便访问实际的内存。

示例

  • 逻辑地址 0x300:表示用户数据段中的一个地址。

  • 指令 _main: mov [300], 0:将0移动到偏移量为300的地址。

  • 段表和页表:展示了如何通过段号和页号查找对应的基址和页框号,从而完成地址转换。

分配虚拟内存,建立段表

从幻灯片中提取的代码如下:

int copy_process(int nr, long ebp, ...)
{...copy_mem(nr, p); ...
}
​
int copy_mem(int nr, task_struct *p)
{unsigned long new_data_base;new_data_base = nr * 0x40000000;  // 64M * nrset_base(p->ldt[1], new_data_base);set_base(p->ldt[2], new_data_base);
}
  1. 计算新的数据基址

    • copy_mem函数中,首先计算新的数据基址new_data_base。这个基址是通过将进程编号nr乘以0x40000000(即64MB)来得到的。这意味着每个进程将获得64MB的虚拟内存空间。

  2. 设置段表

    • 使用set_base函数将计算出的新数据基址设置到进程的局部描述符表(LDT)中的相应段。在这个例子中,p->ldt[1]p->ldt[2]分别代表了两个不同的段(例如,代码段和数据段)。

    • set_base函数的作用是更新段描述符中的基址字段,使其指向新的虚拟内存区域。

  3. 进程控制块(PCB)

    • task_struct *p是一个指向进程控制块(PCB)的指针,它包含了进程的所有信息,包括段表。

    • 在进程创建时,操作系统会为新进程分配一个新的PCB,并复制父进程的相关信息,同时为新进程分配独立的虚拟内存空间。

  4. 内存分配

    • 在Linux中,每个进程都有独立的虚拟内存空间。通过fork()系统调用创建的新进程会继承父进程的虚拟内存布局,但是操作系统会确保新进程的虚拟内存空间是独立的,以防止进程间的相互干扰。

  5. 进程切换

    • 在进程切换时,操作系统需要更新CPU的段寄存器,以指向新进程的段表。这样,当新进程开始执行时,它将在自己的虚拟内存空间中运行。

分配内存,建立页表

这张幻灯片展示了在Linux操作系统中,如何为新创建的进程分配内存并建立页表。以下是提取的代码和对分配内存、建立页表过程的总结:

提取的代码

int copy_mem(int nr, task_struct *p)
{unsigned long old_data_base;old_data_base = get_base(current->ldt[2]);copy_page_tables(old_data_base, new_data_base, data_limit);
}
​
int copy_page_tables(unsigned long from, unsigned long to, long size)
{from_dir = (unsigned long *)(((from >> 20) & 0xffc));to_dir = (unsigned long *)(((to >> 20) & 0xffc));size = (unsigned long)((size + 0x3fffff) >> 22);for (; size-- > 0; from_dir++, to_dir++) {from_page_table = (0xfffff000 & *from_dir);to_page_table = get_free_page();// 这里应该还有代码来复制页表项和设置新的页表}
}

分配内存、建立页表的总结

  1. 获取旧数据基址

    • copy_mem函数中,首先获取当前进程的数据段基址(old_data_base,这通常是通过读取当前进程的段表(LDT)来实现的。

  2. 调用复制页表函数

    • 然后调用copy_page_tables函数,传入旧数据基址、新数据基址(new_data_base)和数据段的大小(data_limit),以复制页表

  3. 计算目录基址

    • copy_page_tables函数中,计算源目录(from_dir)和目标目录(to_dir)的基址。这是通过将虚拟地址右移20位(即页目录的索引)并取低12位(页目录项的偏移)来实现的。

  4. 计算页表项数量

    • 计算需要复制的页表项数量(size),这是通过将数据段大小加上0x3FFFFF(即1MB-1,因为页表项是按页大小为单位的),然后右移22位(即页大小为4KB)来实现的。

  5. 复制页表项

    • 在循环中,对于每个页表项,从源目录读取页表项(from_page_table),然后从页框(page frame)中获取一个空闲页(to_page_table = get_free_page())来存储目标页表项。

    • 这里应该还有代码来复制页表项的内容,并设置新的页表项,包括页框号、保护位等。

  6. 建立页表

    • 通过上述步骤,为新进程建立了页表,将虚拟地址映射到物理内存。

  7. 更新进程控制块(PCB)

    • 最后,需要更新新进程的PCB,包括新的页表基址等信息,以便新进程可以正确地访问其虚拟内存。

from_dir,to_dir

from_dir = (unsigned long *)(((from >> 20) & 0xffc));
to_dir = (unsigned long *)(((to >> 20) & 0xffc));
size = (unsigned long)((size + 0x3fffff) >> 22);
​
for (; size-- > 0; from_dir++, to_dir++) {from_page_table = (0xfffff000 & *from_dir);// 这里应该还有代码来复制页表项和设置新的页表
}
  1. 计算页目录地址

    • from_dirto_dir 是指向页目录的指针。它们是通过将虚拟地址右移20位(即页目录的索引)并取低12位(页目录项的偏移)来计算得到的。这是因为在x86架构中,页目录项是按4KB对齐的,所以需要取低12位来得到页目录项的偏移。

  2. 计算页表项数量

    • size 是需要复制的页表项数量。这是通过将数据段大小加上0x3FFFFF(即1MB-1,因为页表项是按页大小为单位的),然后右移22位(即页大小为4KB)来计算得到的。

  3. 复制页表项

    • 在循环中,对于每个页表项,从源目录读取页表项(from_page_table),然后从页框(page frame)中获取一个空闲页(get_free_page())来存储目标页表项。

    • 这里应该还有代码来复制页表项的内容,并设置新的页表项,包括页框号、保护位等。

  4. 页目录指针

    • 页目录指针(CR3)用于指向当前进程的页目录。在进程切换时,需要更新CR3寄存器以指向新进程的页目录。

from_page_table,to_page_table

从幻灯片中提取的代码和相关信息如下:

提取的代码:

for (; size-- > 0; from_dir++, to_dir++) {to_page_table = get_free_page();*to_dir = ((unsigned long)to_page_table) | 7;
}
​
unsigned long get_free_page(void) {register unsigned long _res asm("ax");__asm__("std; repne; scasb\n\t""movl %%edx, %%eax\n\t""D"(mem_map+PAGING_END-1));return _res;
}

总结:

  1. 页表复制过程

    • 在循环中,对于每个页表项,首先通过调用get_free_page()函数为新进程分配一个空闲的页框(物理内存页)。

    • 然后,将新分配的页框地址设置到目标页目录项中(to_dir),并设置页表项的权限(这里通过或操作| 7来设置)。

  2. 页表项权限设置

    • 在x86架构中,页表项通常包含页框号和一些权限位。这里的| 7操作可能是设置页表项的权限位,例如可读写(RW)和存在位(P)。

  3. 页表项结构

    • 幻灯片中展示了页表项的结构,包括页目录号(10 bits)、页号(10 bits)和偏移(12 bits)。这是x86架构中分页机制的基本概念。

  4. 页目录和页表

    • from_dirto_dir 分别指向源进程和目标进程的页目录项。通过复制页目录项,可以实现页表的复制和更新。

  5. 获取空闲页框

    • get_free_page()函数通过汇编语言实现,用于扫描内存映射(mem_map)来找到一个空闲的页框。这个过程涉及到检查内存映射中的每个条目,直到找到一个空闲的页框。

  6. 内存映射

    • mem_map是一个内存映射数组,用于跟踪哪些页框是空闲的,哪些已经被使用。PAGING_END可能是定义了内存映射数组的结束位置。

复制和更新页表

这张幻灯片展示了在操作系统中,如何复制和更新页表项以实现内存管理。以下是提取的代码和对过程的总结:

提取的代码:

for (; nr-- > 0; from_page_table++, to_page_table++) {this_page = *from_page_table;this_page &= ~2; // 只读*to_page_table = this_page;*from_page_table = this_page;this_page -= LOW_MEM;this_page >>= 12;mem_map[this_page]++;
}

总结:

  1. 页表项复制

    • 代码中的循环遍历所有的页表项,从源页表(from_page_table)复制到目标页表(to_page_table)。

  2. 权限设置

    • this_page &= ~2; 这行代码通过位操作清除页表项中的某个权限位(通常是只读位),确保复制的页表项具有适当的权限设置。

  3. 页表项更新

    • *to_page_table = this_page; 将修改后的页表项写入目标页表。

    • *from_page_table = this_page; 也将修改后的页表项写回源页表,这可能是为了确保源进程的页表项也反映了权限的更改。

  4. 页框号计算

    • this_page -= LOW_MEM; 这行代码从页框号中减去一个基址(LOW_MEM),可能是为了将页框号转换为相对于某个特定内存区域的偏移量。

  5. 页框号转换

    • this_page >>= 12; 这行代码将页框号右移12位,这通常是为了将页框号转换为页框数组中的索引。

  6. 内存映射更新

    • mem_map[this_page]++; 这行代码更新内存映射数组,增加对应页框的使用计数。这是为了跟踪每个页框的使用情况,特别是在使用写时复制(copy-on-write)技术时。

使用内存

写时复制(COW)技术

写时复制是一种优化技术,用于减少复制内存的开销,特别是在创建新进程时。在写时复制中,父子进程最初共享相同的物理内存页,只有当进程实际修改内存页时,才会创建该内存页的副本。

幻灯片内容分析

  1. 内存共享与写时复制

    • 只要段表和页表设置正确,父子进程就可以通过MMU(内存管理单元)自动访问相同的物理内存页。

    • 当父子进程中的任何一个尝试修改共享的内存页时,操作系统会触发写时复制机制,为修改内存页的进程创建一个新的物理内存页副本

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Amazon EC2】为何基于浏览器的EC2 Instance Connect 客户端连接不上EC2实例

文章目录 前言📖一、报错先知❌二、问题复现😯三、解决办法🎲四、验证结果👍五、参考链接🔗 前言📖 这篇文章将讲述我在 Amazon EC2 上使用 RHEL9 AMI 时无法连接到 EC2 实例时所遇到的麻烦😖 …

Python学习笔记(二)(字符串)

文章目录 编写简单的程序一、标识符 (Identifiers)及关键字命名规则:命名惯例:关键字 二、变量与赋值 (Variables & Assignment)变量定义:多重赋值:变量交换:(很方便哟) 三、输入与输出 (In…

Hydra Columnar:一个开源的PostgreSQL列式存储引擎

Hydra Columnar 是一个 PostgreSQL 列式存储插件,专为分析型(OLAP)工作负载设计,旨在提升大规模分析查询和批量更新的效率。 Hydra Columnar 以扩展插件的方式提供,主要特点包括: 采用列式存储&#xff0c…

es的告警信息

Elasticsearch(ES)是一个开源的分布式搜索和分析引擎,在运行过程中可能会产生多种告警信息,以提示用户系统中存在的潜在问题或异常情况。以下是一些常见的 ES 告警信息及其含义和处理方法: 集群健康状态告警 信息示例…

健康与好身体笔记

文章目录 保证睡眠饭后百步走,活到九十九补充钙质一副好肠胃肚子咕咕叫 健康和工作的取舍 以前对健康没概念,但是随着年龄增长,健康问题凸显出来。 持续维护该文档,健康是个永恒的话题。 保证睡眠 一是心态要好,沾枕…

vue实现在线进制转换

vue实现在线进制转换 主要功能包括: 1.支持2-36进制之间的转换。 2.支持整数和浮点数的转换。 3.输入验证(虽然可能存在不严格的情况)。 4.错误提示。 5.结果展示,包括大写字母。 6.用户友好的界面,包括下拉菜单、输…

智体知识库:poplang编程语言是什么?

问:poplang语言是什么 Poplang 语言简介 Poplang(OPCode-Oriented Programming Language)是一种面向操作码(Opcode)的轻量级编程语言,主要用于智体(Agent)系统中的自动化任务处理、…

二分查找5:852. 山脉数组的峰顶索引

链接:852. 山脉数组的峰顶索引 - 力扣(LeetCode) 题解: 事实证明,二分查找不局限于有序数组,非有序的数组也同样适用 二分查找主要思想在于二段性,即将数组分为两段。本体就可以将数组分为ar…

下列软件包有未满足的依赖关系: python3-catkin-pkg : 冲突: catkin 但是 0.8.10-

下列软件包有未满足的依赖关系: python3-catkin-pkg : 冲突: catkin 但是 0.8.10- 解决: 1. 确认当前的包状态 首先,运行以下命令来查看当前安装的catkin和python3-catkin-pkg版本,以及它们之间的依赖关系: dpkg -l | grep ca…

深度学习:AI 大模型时代的智能引擎

当 Deepspeek 以逼真到难辨真假的语音合成和视频生成技术横空出世,瞬间引发了全球对 AI 伦理与技术边界的激烈讨论。从伪造名人演讲、制造虚假新闻,到影视行业的特效革新,这项技术以惊人的速度渗透进大众视野。但在 Deepspeek 强大功能的背后…

医学分割新标杆!双路径PGM-UNet:CNN+Mamba实现病灶毫厘级捕捉

一、引言:医学图像分割的挑战与机遇 医学图像分割是辅助疾病诊断和治疗规划的关键技术,但传统方法常受限于复杂病理特征和微小结构。现有深度学习模型(如CNN和Transformer)虽各有优势,但CNN难以建模长距离依赖&…

CV - 目标检测

物体检测 目标检测和图片分类的区别: 图像分类(Image Classification) 目的:图像分类的目的是识别出图像中主要物体的类别。它试图回答“图像是什么?”的问题。 输出:通常输出是一个标签或一组概率值&am…

高并发秒杀系统设计:关键技术解析与典型陷阱规避

电商、在线票务等众多互联网业务场景中,高并发秒杀活动屡见不鲜。这类活动往往在短时间内会涌入海量的用户请求,对系统架构的性能、稳定性和可用性提出了极高的挑战。曾经,高并发秒杀架构设计让许多开发者望而生畏,然而&#xff0…

蓝桥杯--结束

冲刺题单 基础 一、简单模拟(循环数组日期进制) (一)日期模拟 知识点 1.把月份写为数组,二月默认为28天。 2.写一个判断闰年的方法,然后循环年份的时候判断并更新二月的天数 3.对于星期数的计算&#…

13、nRF52xx蓝牙学习(GPIOTE组件方式的任务配置)

下面再来探讨下驱动库如何实现任务的配置,驱动库的实现步骤应该和寄存器方式对应,关 键点就是如何调用驱动库的函数。 本例里同样的对比寄存器方式编写两路的 GPOITE 任务输出,一路配置为输出翻转,一路设 置为输出低电平。和 …

Java的基本语法(1)

一、运算符和表达式 举例说明什么是运算符,什么是表达式: int a 1; int b 2; int c a b; 在这个例子当中,是运算符,并且是算术运算符 ab是表达式,因为是运算符,所以ab是算术表达式 1.1算术运算符 …

C++学习之密码学知识

目录 1.文档介绍 2.知识点概述 3.项目准备 4.序列化介绍 5.项目中基础组件介绍 6.基础模块在项目中作用 7.项目中其他模块介绍 8.加密三要素 9.对称加密和非堆成加密 10.对称和非对称加密特点 11.堆成加密算法des 12.des对称加密算法 13.对称加密算法aes 14.知识点…

安装vllm

ubuntu 22.04, RTX3080, cuda 12.1, cudnn 8.9.7,cuda和cudnn的安装参考:https://blog.csdn.net/m0_52111823/article/details/147154526?spm1001.2014.3001.5501。 查看版本对应关系,下载12.1对应的whl包,https://github.com/vl…

【WPF】自定义控件:ShellEditControl-同列单元格编辑支持文本框、下拉框和弹窗

需要实现表格同一列,单元格可以使用文本框直接输入编辑、下拉框选择和弹窗,文本框只能输入数字,弹窗中的数据是若干位的二进制值。 本文提供了两种实现单元格编辑状态下,不同编辑控件的方法: 1、DataTrigger控制控件的…

Gson、Fastjson 和 Jackson 对比解析

目录 1. Gson (Google) 基本介绍: 核心功能: 特点: 使用场景: 2. Fastjson (Alibaba) 基本介绍: 核心功能: 特点: 使用场景: 3. Jackson 基本介绍: 核心功能…