6.S081——虚拟内存部分——xv6源码完全解析系列(4)

news/2024/5/5 23:49:33/文章来源:https://blog.csdn.net/zzy980511/article/details/130107673

0.briefly speaking

点击跳转到上一篇博客

好,现在进入下一个话题,就是物理内存分配器(kernel/kalloc.c)。在简单介绍完内核态的物理内存分配器之后,之后简单带过一下两个头文件riscv.h和memorylayout.h这两个头文件,因为它们都比较特殊,直接阅读可能会失去整体性

1.kernel/memorylayout.h (79 rows) <-----------(简单概括)
2.kernel/vm.c (434 rows)
3.kernel/kalloc.c (82 rows) <-----------(这篇博客要阅读的代码)
4.kernel/exec.c (154 rows)
5.kernel/riscv.h (366 rows) <-----------(简单概括)

3.kernel/kalloc.c

3.1 end[]

和vm.c一样,这里的开头是一个全局变量的引用声明,end标明了自由物理内存的开始位置

// 译:内核之后的第一个地址,由kernel.ld定义
extern char end[]; // first address after kernel.// defined by kernel.ld.

既然注释说到了end标号在kernel.ld中定义,不妨去看看:)

/*......*/
/*以上部分省略*/
.bss : {. = ALIGN(16);*(.sbss .sbss.*) /* do not need to distinguish this from .bss */. = ALIGN(16);*(.bss .bss.*)}PROVIDE(end = .);

在kernel.ld:43处定义了end标号,它定义在所有内核代码和数据段之后,对照一下内核地址空间就可以更加方便地认识到这一点了
在这里插入图片描述

3.2 struct run & kmem

这两个结构体向我们展示了在内核中空闲物理内存以怎样的方式组织起来,其实就是一个简单的链表,这个链表将一个个空闲的页面串在了一起。关于这种组织方式,xv6 book中有更加详细的描述,每个run结构体存储在对应空闲页面中,占用着一个指针的大小,指向它的后继者,而freelist指针则指向整个空闲链表开头,如下图所示,空心圆点表示空指针

在这里插入图片描述

源码如下:

// run结构体就是一个指向自身的指针,用于指向下一个空闲页表开始位置
struct run {struct run *next;
};// 管理物理内存的结构
// 有一把锁lock保证访问时的互斥性
// 以及一个指向
struct {struct spinlock lock;struct run *freelist;
} kmem;

3.3 kfree函数

首先来看kfree函数,这是因为freerange函数和kinit函数调用了它,其实kfree做的事情非常简单,就是使用头插法将页面回收到空闲链表中

// Free the page of physical memory pointed at by pa,
// which normally should have been returned by a
// call to kalloc().  (The exception is when
// initializing the allocator; see kinit above.)
// 译:释放pa指向的页的物理内存,它通常是由调用kalloc返回的
// 特殊情况是初始化分配器时,见上面的kinit函数
void
kfree(void *pa)
{struct run *r;// 如果要释放的内存不是页对齐的,或者不在自由内存范围内,陷入panicif(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");// Fill with junk to catch dangling refs.// 将要回收的这一页填满无用的数据// 这一步主要是为了防止在本页内存释放之后仍有进程尝试访问之,无用数据会导致进程快速崩溃// 这在xv6 book中有所解释memset(pa, 1, PGSIZE);// 将pa强制转型为run类型的指针,准备回收到链表中r = (struct run*)pa;// 头插法,将回收的页作为链表第一项插入到空闲链表中// 注意使用锁机制来保持动作的安全性acquire(&kmem.lock);r->next = kmem.freelist;kmem.freelist = r;release(&kmem.lock);
}

3.4 freerange函数

这个函数用来以页为单位释放[pa_start, pa_end]这个范围内的物理内存。pa_start和pa_end函数不一定要是完全页对齐的,这个函数首先会使用PGROUNDUP宏将页面强制对齐,然后逐页释放到终点页面

思考一个问题,为什么要用向上取整的函数?
——这是一种保守策略,本质还是担心将有用的页面释放,甚至于看for循环中的终止条件:p + PGSIZE <= (char*)pa_end,也是遵循同样的保守策略。因为调用者传入的地址可能是非对齐的,这个地址可能正好处于某个页面的中部,此地址以下的地址空间可能还有有用的数据,如果向下对齐可能会将有用的页面一起释放,从而导致错误。

——另外一个重要的原因是,页(page)是内存管理的最小单位,因此PTE只能指向一个对齐的页,所以释放和申请内存时内存也必须是页对齐的

void
freerange(void *pa_start, void *pa_end)
{char *p;// 向上对齐,防止释放有用的页面p = (char*)PGROUNDUP((uint64)pa_start);// 逐页释放到终点页面,注意终止条件p + PGSIZE <= pa_end// 这本质上也加入了保护措施,防止释放有用页for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)kfree(p);
}

3.5 kinit

kinit函数是在内核启动时对物理内存分配器进行初始化的函数,只有经过kinit之后内存管理器才有内存可以分配。

void
kinit()
{initlock(&kmem.lock, "kmem");// 释放从end到PHYSTOP之间的所有物理内存// 回收进空闲链表freelist中freerange(end, (void*)PHYSTOP);
}

另外,一个操作系统应该是从硬件信息中直接获悉系统的内存,但是xv6直接假定内存只有128MB。所以PHYSTOP的定义如下,这包含所有内核代码和数据以及可用的RAM大小:

#define KERNBASE 0x80000000L
#define PHYSTOP (KERNBASE + 128*1024*1024)

3.6 kalloc函数

kalloc函数专门负责分配一页未用的物理内存并返回,主要操作就是从空闲链表的头部摘下一个节点并返回

// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{struct run *r;// 加锁保证操作安全acquire(&kmem.lock);// 取下链表头部的第一个节点,即第一个空闲页r = kmem.freelist;if(r)kmem.freelist = r->next;release(&kmem.lock);// 如果r不为空,表示成功分配到了内存// 将其填满随机数据后返回if(r)memset((char*)r, 5, PGSIZE); // fill with junkreturn (void*)r;
}

4.kernel/memorylayout.h & kernel/riscv.h

这两个文件不打算在此展开,因为它们都是一些细碎的宏定义和嵌入式汇编语句,其中memorylayout.h头文件中定义了和地址空间布局相关的一些宏,riscv.h则定义了很多嵌入式汇编语句。这些我们在后面阅读代码时再适时地切入可能效果会更好。

那么有关Xv6的虚拟内存部分代码,到这里算是告一段落了,下一次我们该研究陷阱机制了:)

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

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

相关文章

2.5d风格的游戏模式如何制作

文章目录一、 介绍二、 绘制瓦片地图三、 添加场景物体&#xff0c;添加碰撞器四、 创建玩家五、 创建玩家动画六、 玩家脚本七、 2d转换成2.5d八、 “Q”键向左转动视角、“E”键向右转动视角九、 下载工程文件一、 介绍 制作一个类似饥荒风格的2.5d游戏模板。 2.5D游戏是指以…

表id自增的方法

数据库主键id自增的方法&#xff0c;列举了几种如下 一、数据库自增&#xff08;部分数据库支持&#xff09; 创建表的时候设置id自增即可&#xff0c;或者后期修改表id自增 # mysql 语法 create table your_table_name(id bigint(20) not null auto_increment primary key …

Markdown 语法大全

Markdown是一种轻量级标记语言&#xff0c;常用于撰写博客、文档、论文等。它可以让你使用易读易写的纯文本格式来编写文档&#xff0c;然后通过转换成有效的HTML文档进行发布。以下是Markdown常用的语法&#xff1a; 这里写目录标题标题列表引用一级引用嵌套引用粗体和斜体删除…

Java集合——Set接口学习总结

一、HashSet实现类 1.常用方法 增加&#xff1a;add(E e)删除&#xff1a;remove(Object o)、clear()修改&#xff1a;查看&#xff1a;iterator()判断&#xff1a;contains(Object o)、isEmpty()常用遍历方式&#xff1a;Set<String> set new HashSet<String>()…

Spark 对hadoopnamenode-log文件进行数据清洗并存入mysql数据库

一.查找需要清洗的文件 1.1查看hadoopnamenode-log文件位置 1.2 开启Hadoop集群和Hive元数据、Hive远程连接 具体如何开启可以看我之前的文章&#xff1a;(10条消息) SparkSQL-liunx系统Spark连接Hive_难以言喻wyy的博客-CSDN博客 1.3 将这个文件传入到hdfs中&#xff1a; hd…

windows系统管理_windows server 2016 用户管理

用户账户的概述 **计算机用户账户&#xff1a;**由将用户定义到某一系统的所有信息组成的记录,账户为用户或计算机提供安 全凭证&#xff0c;包括用户名和用户登陆所需要的密码&#xff0c;以及用户使用以便用户和计算机能够登录到网络并 访问域资源的权利和权限。不同的身份拥…

【Obsidian】基础使用手册(包括如何将Obsidian页面设置为中文)

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于工具类软件的笔记 &#x1f236;本篇是Obsidian的基础使用 Obsidian的基础使用将页面设置为中文常用的默认快捷键常用的格式标题代码块表格字体样式列表任务列表官方下载地址&am…

【音视频第11天】GCC论文阅读(2)

A Google Congestion Control Algorithm for Real-Time Communication draft-alvestrand-rmcat-congestion-03论文理解 看中文的GCC算法一脸懵。看一看英文版的&#xff0c;找一找感觉。 目录Abstract1. Introduction1.1 Mathematical notation conventions2. System model3.Fe…

获取淘宝商品分类详情API,抓取淘宝全品类目API接口分享(代码展示、参数说明)

商品分类技巧 淘宝店铺分类怎么设置&#xff1f;我们登录卖家账号的时候&#xff0c;我们看到自己的商品&#xff0c;会想要给商品进行分类&#xff0c;一个好的分类可以帮助提高商品的曝光率。那么在给商品分类前&#xff0c;如果您毫无头绪&#xff0c;以下几点可以给您带来…

车载网络 - Autosar网络管理 - 网络管理简介

一、什么是CAN网络管理及它的作用 现在的车辆是由大量的ECU节点组成的&#xff0c;为了能使各ECU能够正确并及时地进行CAN通信&#xff0c;需要有一套机制来统一协调总线上各节点的休眠唤醒&#xff0c;这套机制就是CAN网络管理&#xff08;NM&#xff09;。 网络管理的目的是保…

【算法题解】24. 模拟机器人行走

这是一道 中等难度 的题 https://leetcode.cn/problems/walking-robot-simulation/description/ 题目 机器人在一个无限大小的 XY 网格平面上行走&#xff0c;从点 (0, 0) 处开始出发&#xff0c;面向北方。该机器人可以接收以下三种类型的命令 commands &#xff1a; -2 &am…

WPF mvvm框架Stylet使用教程-基础用法

Stylet框架基础用法 安装Nuget包 在“管理Nuget程序包”中搜索Stylet&#xff0c;查看Stylet包支持的net版本&#xff0c;然后选择第二个Stylet.Start包进行安装&#xff0c;该包会自动安装stylet并且生成基本的配置 注意事项&#xff1a;安装时要把需要安装的程序设为启动项…

PyCharm2021安装教程

PyCharm是一种Python IDE&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外&#xff0c;该IDE提供了一些高级功能&#xff0c;以用于支持Djang…

IntersectionObserver与无限滚动加载

学习链接 IntersectionObserver MDN Api IntersectionObserver API详解 Intersection observer 的概念和用法 过去&#xff0c;要检测一个元素是否可见或者两个元素是否相交并不容易&#xff0c;比如实现图片懒加载、内容无限滚动等功能时&#xff0c;都需要通过​getBound…

[Date structure]时间/空间复杂度

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现&#xff0c;有时候有C/C代码。 ⭐如果觉得文章写的…

linux文件类型和根目录结构

目录 一、Linux文件类型 二、Linux系统的目录结构 1. FHS 2. 路径以及工作目录 &#xff08;1&#xff09;路径 &#xff08;2&#xff09;工作目录 一、Linux文件类型 使用ls -l命令查看到的第一个字符文件类型说明-普通文件类似于Windows的记事本d目录文件类似于Windo…

[NOIP2000 提高组] 进制转换

[NOIP2000 提高组] 进制转换 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置为指数,以 10为底数的幂之和的形式。例如 123 可表示为 10^22*10^13*10^0 这样的形式。 与之相似的&#xff0c;对二进制数来说&#xff0c;也可表示成…

WordPress添加阿里云OSS对象云储存配置教程

背景&#xff1a;随着页面文章增多&#xff0c;内置图片存储拖连网站响应速度&#xff0c;这里对我来说主要是想提升速度 目的&#xff1a;使用第三方云存储作为图片外存储(图床)&#xff0c;这样处理可以为服务器节省很多磁盘空间&#xff0c;在网站搬家的时候减少文件迁移的工…

2023TYUT移动应用软件开发程序设计和填空

目录 程序设计 程序设计1&#xff1a;根据要求设计UI,补充相应布局文件&#xff0c;即.xml文件 程序设计2&#xff1a;根据要求,补充Activity.java文件 程序填空 说明&#xff1a; 程序设计 程序设计1&#xff1a;根据要求设计UI,补充相应布局文件&#xff0c;即.xml文件…

安装Nginx——docker安装

使用docker安装Nginx 1.开启docker systemctl start docker docker search nginx[rootlocalhost ~]# systemctl start docker //开启docker [rootlocalhost ~]# docker search nginx //搜素镜像 2. docker pull nginxdocker imagesdocker run -…