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

news2025/1/23 4:56:00

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;
  
  // 如果要释放的内存不是页对齐的,或者不在自由内存范围内,陷入panic
  if(((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 junk
  return (void*)r;
}

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

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

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

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

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

相关文章

.Net Core从零学习搭建权限管理系统 - 课程简介

课程简介目录 &#x1f680;前言一、课程背景二、课程目的三、系统功能四、系统技术架构五、课程特点六、课程适合人员七、课程规划的章节八、最后 &#x1f680;前言 本文是《.Net Core从零学习搭建权限管理系统》教程专栏的导航站&#xff08;点击链接&#xff0c;跳转到专栏…

(1条消息) CodeForces 1278 B.A and B(Math)

题目如下&#xff1a; 题解 or 思路 首先我们需要知道&#xff1a; 对于&#xff1a; s u m 1 2 3 4 . . . n sum 1 2 3 4 ... n sum1234...n s u m a b , ( a ∈ [ 0 , s u m ] ) sum a b, (a \in [0, sum]) sumab,(a∈[0,sum]) 这个在此就不再证明 于是我…

vcpkg添加自定义包安装

文章目录 前言新建overlay-ports编写baseline.json编写openssl.json编写配置修改vcpkg.json修改portfile.cmake 挂载安装后话 前言 vcpkg收集了很多C的包&#xff0c;可总是会有没收录进去的&#xff0c;以openssl 3.0.0版本举例&#xff0c;这个版本vcpkg没有收录进去&#x…

String的不可变特性

1 问题 如何理解“String是不可变的&#xff0c;但是可以变”&#xff1f; 2 方法 &#xff08;1&#xff09;String的不可变特性体现在内容和长度 首先在idea中点开查看String这个类是如何定义的 可以看到这样一行代码&#xff1a;private final char value[]; 正是因为这个数…

基于Simulink单载波链路射频波束成形仿真

一、前言 此示例展示了如何在 Simulink中对 IEEE 802.11ad单载波链路进行建模&#xff0c;其中包括具有射频波束成形功能的相控阵天线。 二、介绍 此模型模拟具有射频波束成形的 802.11ad 单载波 &#xff08;SC&#xff09;链路。多个数据包通过自由空间传输&#xff0c;然后射…

图片转为pdf怎么弄?简单几个步骤轻松转换

在日常工作和生活中&#xff0c;我们常常需要将图片转换为PDF格式的文档&#xff0c;以满足资料存档和共享的需要。虽然转换过程看起来有些麻烦&#xff0c;但只要选择正确的工具和方法&#xff0c;就能轻松完成。 下面&#xff0c;小编将为大家介绍两种常见的将图片转换为PDF…

生成对抗网络(GAN) 理论概念、改进模型与练习题

生成对抗网络&#xff08;GAN&#xff09; 生成对抗网络的基本概念模型提出模型类型模型功能模型目标模型结构模型训练模型实质模型缺点模型生成器的构造模型损失函数 生成对抗网络的改进模型DCGANWGANWGAN-GPACGAN 生成对抗网络的题型 生成对抗网络的基本概念 模型提出 2014…

Grad-CAM的详细介绍和Pytorch代码实现

Grad-CAM (Gradient-weighted Class Activation Mapping) 是一种可视化深度神经网络中哪些部分对于预测结果贡献最大的技术。它能够定位到特定的图像区域&#xff0c;从而使得神经网络的决策过程更加可解释和可视化。 Grad-CAM 的基本思想是&#xff0c;在神经网络中&#xff…

体验编写Vue框架项目实例的详细步骤2(包括git仓库使用,element-ui的使用和eslint校验关闭)

1.在src目录下新建pages文件夹用来放页面。新建文件Index.vue&#xff0c;首页 在Index.vue中搭建vue基本结构。 在element官网Element - The worlds most popular Vue UI framework中选择想要的组件。 我选择是Container布局容器。选择好样式点击显示代码复制相关代码至Ind…

【安全运维】小微企业的安全运维工具用哪款好?

即使是小微企业&#xff0c;也同样面临着安全运维的困扰&#xff0c;同样面临着数据泄露、资产难管理的问题&#xff0c;因此选择一款合适的安全运维工具是非常必要的。那你知道小微企业的安全运维工具用哪款好&#xff1f; 小微企业的安全运维工具用哪款好&#xff1f; 【回…

全景视角下的世界探索——三维全景地图

引言&#xff1a;随着数字技术和虚拟现实技术的发展&#xff0c;三维全景地图已成为一种新型地图展示方式&#xff0c;深受人们的关注和喜爱。三维全景地图以其真实逼真、互动性强、展示效果好等特点&#xff0c;正在越来越多的领域得到应用。 三维全景地图的特点 1.真实逼真 …

Elasticsearch(黑马)

初识elasticsearch ​​. 安装elasticsearch 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net 1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的…

【云原生概念和技术】1.2 云原生技术概括(下)

如果想了解或者学习云原生的友友们&#xff0c;欢迎订阅哦&#xff5e;&#x1f917;&#xff0c;目前一周三更&#xff0c;努力码字中&#x1f9d1;‍&#x1f4bb;…目前第一章是一些介绍和概念性的知识&#xff0c;可以先在脑海里有一个知识的轮廓&#xff0c;从第二章开始就…

Talk预告 | ICLR‘23 北京大学楼家宁:针对鲁棒聚类问题的接近最优核心集

本期为TechBeat人工智能社区第485期线上Talk&#xff01; 北京时间3月29日(周三)20:00&#xff0c;北京大学信息科学技术学院——楼家宁的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “针对鲁棒聚类问题的接近最优核心集”&#xff0c;届时将针…

nodejs+vue在线课程管理系统

随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本在线课程管理系统有管理员&#xff0c;教师&#xff0c;学生。管理员功能有个人中心&#xff0c;学生管理&#xff0c;教师管理&#xff0c;在线课程管理&#xff0c;课件信息管理&#x…

maybits就是持久型框架

MyBatis 是一款优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息&#xff0c;将接口和 Java 的 POJOs(Plain Ordinary Java Ob…

使用HiBurn烧录鸿蒙.bin文件到Hi3861开发板

使用HiBurn烧录鸿蒙.bin文件到Hi3861开发板 鸿蒙官方文档的“Hi3861开发板第一个示例程序”中描述了——如何使用DevEco Device Tool工具烧录二进制文件到Hi3861开发板&#xff1b; 本文将介绍如何使用HiBurn工具烧录鸿蒙的.bin文件到Hi3861开发板。 获取HiBurn工具 通过鸿蒙…

简单的Shell脚本实现自动化构建部署-适合前后端分离的小网站

1. 背景 大家在生活中经常会自己写一点小代码。然后部署在公有云的服务器上。但是一般像阿里&#xff0c;腾讯等服务商&#xff0c;提供的机器内存并不是很大。如果想装入一个jenkins之类的服务&#xff0c;会比较占用CPU和内存的资源。但是人手的部署又是比较麻烦的。所以我这…

相较于传统fNIRS,时域矩量fNIRS在多大程度上提高了对大脑活动的估计?

导读 意义&#xff1a;电子技术的进步使通道更多的时域功能近红外光谱(TD-fNIRS)得到发展。由于高阶时域矩的深度选择性&#xff0c;时域矩量分析已被提出用于提高对大脑的敏感度分析。研究者提出了一种综合时域(TD)矩量数据和辅助生理测量(如短分离通道)的一般线性模型(GLM)&…

OpenMV初体验

Openmv初体验 OpenMV IDE OPENMV4-STM32H743 import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.RGB565) # 图像彩色/灰白 sensor.set_framesize(sensor.QVGA) # 图像大小 sensor.skip_frames(time 2000) # 几秒后开始或跳过几帧 cnt 0 #sensor.set_…