6.s081 学习实验记录(九)lock parallelism

文章目录

    • 一、Memory allocator
      • 简介
      • 提示
      • 实验代码
      • 实验结果
    • 二、Buffer cache
      • 简介
      • 提示
      • 实验代码
      • 实验结果

该实验将重构某些代码以提高并发度。

首先切换到lock分支:

  • git fetch
  • git checkout lock
  • make clean

一、Memory allocator

简介

user/kalloctest 这个程序会对xv6的内存分配器进行压力测试,该测试中三个进程会扩大缩小其地址空间(使用 kalloc、kfree),而这会导致 kmem.lock 的竞争。该测试会在 acquire 中打印出为了获取锁而循环等待的次数,这可以作为锁竞争的粗略指标。在未进行任何优化前,打印如下图:
在这里插入图片描述
这里竞争严重的问题原因是空闲内存由一个链表来维护,多个核情况下存在并发竞争,需要锁来保护。因此,一个简单的思想是每个核都维护一个空闲链表,每个核的空闲链表拥有自己专门的锁来保证并发安全。需要注意的是,我们要处理一个核的空闲链表已经没有内存了,但另外一个核的空闲链表还存在空闲内存的情况,即需要有内存窃取机制。

提示

  • 你可以使用kernel/param.h中定义的常量NCPU
  • freerange函数返回所有的空闲内存给运行 freerange 的cpu
  • 可以使用 cpuid 来获取当前cpu的id,但是需要关闭中断,可以使用push_off()pop_off()控制中断的开关
  • 可以使用xv6的race detector来辅助定位问题,其可以帮助检查内存是否重复分配,如果存在内存分配竞争,其将会打印如下内容:
    • make clean
    • make KCSAN=1 qemu
    • kalloctest
      在这里插入图片描述
  • 可以将 race detector 打印的内容通过 riscv64-linux-gnu-addr2line -e kernel/kernel 转换成对应的函数名
    在这里插入图片描述

实验代码

本实验主要修改内存申请释放模块,即主要修改kalloc.c文件。

  • 将空闲链表分为每个cpu一个,且每个cpu一个锁
  • 修改 kinit() 初始化每个cpu的空闲链表
  • 修改kalloc()分配内存时通过 cpuid() 获取当前cpu id,从自己的空闲链表中获取内存;kfree()同理
  • kalloc()中需要添加内存盗取逻辑

修改空闲链表每个cpu一个

struct {struct spinlock lock;struct run *freelist;
} kmem[NCPU];

更改初始化逻辑

void
kinit()
{char name[16];for(int i = 0; i < NCPU; i++){snprintf(name, sizeof(name), "kmem_cpu%d", i);initlock(&kmem[i].lock, name);}freerange(end, (void*)PHYSTOP);
}

更改分配逻辑

void *
kalloc(void)
{struct run *r;int cpu = -1;push_off(); //关中断cpu = cpuid();pop_off(); // 开中断acquire(&kmem[cpu].lock);r = kmem[cpu].freelist;if(r){kmem[cpu].freelist = r->next;} else {// 内存窃取struct run* tmp;for (int i = 0; i < NCPU; ++i) {if (i == cpu) continue;acquire(&kmem[i].lock);tmp = kmem[i].freelist;if (tmp == 0) {release(&kmem[i].lock);continue;} else {// 窃取一个页面kmem[cpu].freelist = kmem[i].freelist;kmem[i].freelist = tmp->next;tmp->next = 0;release(&kmem[i].lock);break;}}r = kmem[cpu].freelist;if (r)kmem[cpu].freelist = r->next;}release(&kmem[cpu].lock);if(r)memset((char*)r, 5, PGSIZE); // fill with junkreturn (void*)r;
}

更改回收逻辑

void
kfree(void *pa)
{struct run *r;int cpu = -1;if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");// Fill with junk to catch dangling refs.memset(pa, 1, PGSIZE);r = (struct run*)pa;push_off();cpu = cpuid();pop_off();acquire(&kmem[cpu].lock);r->next = kmem[cpu].freelist;kmem[cpu].freelist = r;release(&kmem[cpu].lock);
}

实验结果

  • kalloctest
    在这里插入图片描述

  • usertests sbrkmuch
    在这里插入图片描述

  • usertests -q
    在这里插入图片描述

二、Buffer cache

简介

当多个进程密集的访问文件系统时,它们可能会争夺 bcache.lock ,该锁保护 kernel/bio.c 中磁盘块缓存。bcachetest 将创建多个重复读取不同文件的进程,以使得产生 bcache.lock 的争用,其输出可能如下(在未完成实验之前):
在这里插入图片描述
bcache.lock 保护了很多临界区,包括:the list of cached block buffers, the reference count (b->refcnt) in each block buffer, and the identities of the cached blocks (b->dev and b->blockno).

我们需要修改锁的粒度,实现所有锁在acquire()中的打印接近于0,即每个锁的获取几乎不需要等待。修改 bget() 和 brelse() 函数,使得 bcache 中不同块的并发查找和释放不太可能发生冲突。

必须保证每个块在bcache中最多一份缓存。

完成该实验后,运行下列命令打印如下:
在这里插入图片描述

提示

  • 所有锁的名称必须用bcache开头,并使用initlock() 初始化这些锁
  • 可以采用给每个hash桶一个锁的方式来实现
  • 可以扩大hash桶来减少桶内元素的竞争
  • 阅读 xv6-book 的 section 8.1-8.3,了解xv6的块缓存
  • hash桶的大小可以采用质数(例如13)来减少冲突,hash表的大小可以是固定,不用动态扩容
  • hash表中寻找buffer缓存和当搜寻不到,为该块分配缓存时的操作必须是原子的
  • 删除所有bcache中的buffer list(bcache.head),并且不使用LRU算法,这样 relse() 可以不用获取 bcache 锁bget()中可以选择任意一个 refcnt == 0的块。

实验代码

  • 主要修改 bio.c,该实验由于时间原因,取网上答案且未验证
    重新定义bcache,包含13个hash桶,每个桶一个锁,hash算法使用最简单的blockno % NBUCKET
#define NBUCKET 13
struct {struct spinlock lock;struct buf head[NBUCKET];struct buf hash[NBUCKET][NBUF];struct spinlock hashlock[NBUCKET]; // lock per bucket
} bcache;
void
binit(void)
{struct buf *b;initlock(&bcache.lock, "bcache");for (int i = 0; i < NBUCKET; i++) {initlock(&bcache.hashlock[i], "bcache");// Create linked list of buffersbcache.head[i].prev = &bcache.head[i];bcache.head[i].next = &bcache.head[i];for(b = bcache.hash[i]; b < bcache.hash[i]+NBUF; b++){b->next = bcache.head[i].next;b->prev = &bcache.head[i];initsleeplock(&b->lock, "buffer");bcache.head[i].next->prev = b;bcache.head[i].next = b;}}
}static struct buf*
bget(uint dev, uint blockno)
{struct buf *b;uint hashcode = blockno % NBUCKET;acquire(&bcache.hashlock[hashcode]);// Is the block already cached?for(b = bcache.head[hashcode].next; b != &bcache.head[hashcode]; b = b->next){if(b->dev == dev && b->blockno == blockno){b->refcnt++;release(&bcache.hashlock[hashcode]);acquiresleep(&b->lock);return b;}}// Not cached.// Recycle the least recently used (LRU) unused buffer.for(b = bcache.head[hashcode].prev; b != &bcache.head[hashcode]; b = b->prev){if(b->refcnt == 0) {b->dev = dev;b->blockno = blockno;b->valid = 0;b->refcnt = 1;release(&bcache.hashlock[hashcode]);acquiresleep(&b->lock);return b;}}panic("bget: no buffers");
}void
brelse(struct buf *b)
{if(!holdingsleep(&b->lock))panic("brelse");releasesleep(&b->lock);uint hashcode = b->blockno % NBUCKET;acquire(&bcache.hashlock[hashcode]);b->refcnt--;if (b->refcnt == 0) {// no one is waiting for it.b->next->prev = b->prev;b->prev->next = b->next;b->next = bcache.head[hashcode].next;b->prev = &bcache.head[hashcode];bcache.head[hashcode].next->prev = b;bcache.head[hashcode].next = b;}release(&bcache.hashlock[hashcode]);
}

实验结果

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

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

相关文章

Unity 2D Spine 外发光实现思路

Unity 2D Spine 外发光实现思路 前言 对于3D骨骼&#xff0c;要做外发光可以之间通过向法线方向延申来实现。 但是对于2D骨骼&#xff0c;各顶点的法线没有向3D骨骼那样拥有垂直于面的特性&#xff0c;那我们如何做2D骨骼的外发光效果呢&#xff1f; 理论基础 我们要知道&a…

Spring Boot 笔记 010 创建接口_更新用户头像

1.1.1 usercontroller中添加updateAvatar&#xff0c;校验是否为url PatchMapping("updateAvatar")public Result updateAvatar(RequestParam URL String avatarUrl) {userService.updateAvatar(avatarUrl);return Result.success();} 1.1.2 userservice //更新头像…

2.18通过字符设备驱动分步注册过程实现LED驱动的编写,编写应用程序测试

应用程序&#xff1a; #include<stdlib.h> #include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #include<string.h> #include<sys/ioctl.h> #include"myled.h&quo…

JVM-JVM调优基础(理论)

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;作为笔记进行记录&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 JVM参数 标准参数 …

蓝桥杯:C++排序

排序 排序和排列是算法题目常见的基本算法。几乎每次蓝桥杯软件类大赛都有题目会用到排序或排列。常见的排序算法如下。 第(3)种排序算法不是基于比较的&#xff0c;而是对数值按位划分&#xff0c;按照以空间换取时间的思路来排序。看起来它们的复杂度更好&#xff0c;但实际…

ADC--模拟量转换成数字量

目录 一、ADC硬件组成七大部分&#xff1a; 二、单次转换&#xff0c;连续转换&#xff0c;不连续采样模式&#xff0c;扫描模式区别 1、举例(5种组合情况) 2、模拟看门狗中断的作用&#xff1a; 三、MCU使用ADC步骤 一、ADC硬件组成七大部分&#xff1a; ①输入电压&#…

Java数字孪生智慧工地数据大屏APP项目源码

目录 智慧工地云平台核心功能 1.劳务管理 2.视频监控 3.安全教育 4.进度管理 5.环境监测 6.塔吊监控 7.升降机监控 8.工地广播 9.深基坑高支模 10.AI识别 11.安全质量 智慧工地建设的价值和意义 危大工程管理 智慧工地聚焦施工现场一线生产活动&#xff0c;利用物…

使用Python生成二维码的完整指南

无边落木萧萧下&#xff0c;不如跟着可莉一起游~ 可莉将这篇博客收录在了&#xff1a;《Python》 可莉推荐的优质博主首页&#xff1a;Kevin ’ s blog 本文将介绍如何使用Python中的qrcode库来生成二维码。通过简单的代码示例和详细解释&#xff0c;读者将学习如何在Python中轻…

数据结构-双指针法

介绍 双指针法是一种可以在O&#xff08;n&#xff09;时间复杂度内解决数组、链表、字符串等数据结构相关的问题的方法。核心思想为使用两个指针在不同位置遍历数组或链表&#xff0c;从而实现特定操作。 常见的双指针法有 1.快慢指针&#xff1a;快指针每次移动两步&…

单测的思路

文章目录 单测的分类方法的单测生成工具的对比生成步骤 接口的单测mock步骤部分依赖mock的方式 场景的单测参考 单测的分类 单元测试&#xff08;Unit Testing&#xff09;是一种软件开发中的测试方法&#xff0c;它的主要目的是确保软件中的最小可测试单元&#xff08;通常是…

(07)Hive——窗口函数详解

一、 窗口函数知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowing…

英文论文(sci)解读复现【NO.21】一种基于空间坐标的轻量级目标检测器无人机航空图像的自注意

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

红队笔记Day3-->隧道上线不出网机器

昨天讲了通过代理的形式&#xff08;端口转发&#xff09;实现了上线不出网的机器&#xff0c;那么今天就来讲一下如何通过隧道上线不出网机器 目录 1.网络拓扑 2.开始做隧道&#xff1f;No&#xff01;&#xff01;&#xff01; 3.icmp隧道 4.HTTP隧道 5.SSH隧道 1.什么…

探索未来科技前沿:深度学习的进展与应用

深度学习的进展 摘要&#xff1a;深度学习作为人工智能领域的重要分支&#xff0c;近年来取得了巨大的进展&#xff0c;并在各个领域展现出惊人的应用潜力。本文将介绍深度学习的发展历程、技术原理以及在图像识别、自然语言处理等领域的应用&#xff0c;展望深度学习在未来的…

RunnerGo:UI自动化测试神器!

UI自动化测试已经成为现代软件开发过程中不可或缺的一部分。它能够提供诸多优势&#xff0c;包括提高测试效率、减少人力成本、提升软件质量等。同时&#xff0c;可视化工具为UI自动化测试带来了更多便利和灵活性。RunnerGo近期上线脚本录制器&#xff0c;根据你的测试操作直接…

React 更改程序入口点(index.js文件位置变更)

食用前提示&#xff1a;本文基于已经快速配置好的React环境而作&#xff0c;配置React环境详见拙作&#xff1a;React环境配置-CSDN博客~ 一、了解默认入口点 使用create-react-app快速搭建react环境后&#xff0c;npm start启动程序的默认入口点为/src/index(即src目录下的ind…

【Prometheus】组件介绍-工作流程-部署模式-数据类型-监控

基于Prometheus和K8S构建智能化告警系统 一、Prometheus简介二、Prometheus特点于样本2.1、特点2.2、样本 三、Prometheus组件介绍四、Prometheus工作流程五、Prometheus的几种部署模式5.1、基本高可用模式5.2、基本高可用远程存储5.3、基本高可用远程存储联邦集群 六、Prometh…

ESP32工程中CMake使用及加入第三方SDK库文件

1、ESP32工程结构 本文中使用的是乐鑫官方推出的ESP-IDF v5.1对ESP32S3设备开发&#xff0c;并非是Arduino、Micro-python等第三方工具开发。在ESP-IDF框架中&#xff0c;乐鑫官方已经将CMake 和 Ninja 编译构建工具集成到了ESP-IDF中。 ESP-IDF 即乐鑫物联网开发框架&#xff…

卷积神经网络的基本结构

卷积神经网络的基本结构 与传统的全连接神经网络一样&#xff0c;卷积神经网络依然是一个层级网络&#xff0c;只不过层的功能和形式发生了变化。 典型的CNN结构包括&#xff1a; 数据输入层&#xff08;Input Layer&#xff09;卷积层&#xff08;Convolutional Layer&#x…

第11章 GUI

11.1 Swing概述 Swing是Java语言开发图形化界面的一个工具包。它以抽象窗口工具包&#xff08;AWT&#xff09;为基础&#xff0c;使跨平台应用程序可以使用可插拔的外观风格。Swing拥有丰富的库和组件&#xff0c;使用非常灵活&#xff0c;开发人员只用很少的代码就可以创建出…