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.mzph.cn/news/689891.shtml

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

相关文章

关于投资,房地产,AI

各位朋友&#xff0c;新年好&#xff01; 过个年&#xff0c;世界发生了很多大事&#xff01; 投资 先是证监会&#xff0c;证监会年前换帅&#xff0c;吴清接棒&#xff0c;吴清何许人也&#xff1f;江湖人称“券商屠夫”&#xff0c;成功处置了2008年的券商风险&#xff0…

Eclipse - Format Comment

Eclipse - Format & Comment 1. Correct Indentation2. Format3. Toggle Comment4. Add Block Comment5. Remove Block CommentReferences 1. Correct Indentation Ctrl A: 选择全部代码 Ctrl I: 校正缩进 or right-click -> Source -> Correct Indentation 2. F…

QWidget自由绘制曲线

效果图:候补 具体实现(核心代码部分带解释) 项目配置部分: QT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = DrawWidget TEMPLATE = appSOURCES += main.cpp\mainwindow.cpp \drawwidget.cppHEADERS += mainwindow.h \drawwidget.hmain.c…

centos8安装docker docker compose

首先&#xff0c;你需要在 CentOS 8 上安装 Docker。以下是安装步骤&#xff1a; 更新你的系统&#xff1a;首先&#xff0c;你需要确保你的系统是最新的。你可以使用以下命令来更新你的系统&#xff1a; sudo dnf update -y安装 Docker&#xff1a;然后&#xff0c;你可以使用…

【开工大吉】推荐4款开源、美观的WPF UI组件库

WPF介绍 WPF 是一个强大的桌面应用程序框架&#xff0c;用于构建具有丰富用户界面的 Windows 应用。它提供了灵活的布局、数据绑定、样式和模板、动画效果等功能&#xff0c;让开发者可以创建出吸引人且交互性强的应用程序。 HandyControl HandyControl是一套WPF控件库&#xf…

安全技术和防火墙

1.安全技术和防火墙 1.1安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全决…

普中51单片机学习(九)

蜂鸣器 蜂鸣器简介 在单片机应用的设计上&#xff0c;很多方案都会用到蜂鸣器&#xff0c;大部分都是使用蜂鸣器来做提示或报警&#xff0c;比如按键按下、开始工作、工作结束或是故障等等。改变单片机引脚输出波形的频率&#xff0c;就可以调整控制蜂鸣器音调&#xff0c;产…

Python操作Kafka基础教程

01 Python操作Kafka基础教程 创建ZooKeeper容器 docker run -d --name zookeeper -p 2181:2181 -v /etc/localtime:/etc/localtime wurstmeister/zookeeper创建Kafka容器 语法是&#xff1a; docker run -d --name kafka -p 9092:9092 -e KAFKA_BROKER_ID0 -e KAFKA_ZOOKE…

世界顶级名校计算机专业,都在用哪些书当教材?(文末送书)

目录 01《深入理解计算机系统》02《算法导论》03《计算机程序的构造和解释》04《数据库系统概念》05《计算机组成与设计&#xff1a;硬件/软件接口》06《离散数学及其应用》07《组合数学》08《斯坦福算法博弈论二十讲》参与规则 清华、北大、MIT、CMU、斯坦福的学霸们在新学期里…

vue常见问题

文章目录 data为什么是一个函数&#xff0c;而不是一个对象&#xff1f;什么情况下可以使用对象&#xff1f;key的作用&#xff0c;为什么不能用Index&#xff1f;render函数&#xff0c;h函数&#xff0c;和template什么关系&#xff1f;vue 是怎么解析template的? template会…

讨好型人格的职业分析,如何改变讨好型人格

一味讨好他人&#xff0c;忽略自己感受&#xff0c;凡事以人为先&#xff0c;忽视自己需求&#xff0c;这就是讨好型人格。 讨好型人格最典型的表现就是非常注重外界的看法&#xff0c;不管做什么事都会小心翼翼&#xff0c;生怕自己所做的事会引发别人的不满。 如果自己哪方…

MAC电脑系统清理空间免费版软件CleanMyMac X2024

大家好&#xff0c;我是那个总是被苹果电脑“内存已满”提示搞得焦头烂额的专业博主。如果你也像我一样&#xff0c;在使用Mac时经常遭遇卡顿、慢吞吞的情况&#xff0c;那么今天的Mac清理空间妙招分享绝对适合你&#xff01; CleanMyMac X全新版下载如下: https://wm.makedi…

【Redis快速入门】深入解读哨兵模式

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

分糖果问题(java实现)

一、题目 描述 一群孩子做游戏&#xff0c;现在请你根据游戏得分来发糖果&#xff0c;要求如下&#xff1a; 每个孩子不管得分多少&#xff0c;起码分到一个糖果。任意两个相邻的孩子之间&#xff0c;得分较多的孩子必须拿多一些糖果。(若相同则无此限制) 给定一个数组 arr …

odoo16实用功能之作业队列(queue.job)

目录 1、官方文档 2、说明 3、简单的开发手册 1、在 Odoo 代码中定义需要异步处理的方法。 2、在需要调用异步方法的位置&#xff0c;使用 with_delay() 调用该方法。 3、注意事项 1、官方文档 Job Queue .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! This…

layui-tab加载echarts宽度丢失

主要是设置div的样式为&#xff1a;width:100%;height:100%;display: block; <style>.layui-tab-item{width:100%;height:480px;} </style><div class"layui-tab layui-tab-brief" lay-filter"component-tabs-brief"><ul class"…

C语言系列12——多线程与并发编程

目录 写在开头1 多线程的基本概念与应用场景1.1 什么是多线程&#xff1f;1.2 多线程的优势1.3 应用场景详解1.3.1 并行计算1.3.2 高响应性应用程序1.3.3 网络编程1.3.4 实时处理 1.4 多线程编程的挑战 2 POSIX线程库的使用与实际案例2.1 基础概念2.2 创建和管理线程2.3 线程同…

HTML 入门指南

简述 参考&#xff1a;HTML 教程- (HTML5 标准) HTML 语言的介绍、特点 HTML&#xff1a;超级文本标记语言&#xff08;HyperText Markup Language&#xff09; “超文本” 就是指页面内可以包含图片、链接等非文字内容。“标记” 就是使用标签的方法将需要的内容包括起来。…

116 C++ 可变参数函数,initializer_list (初始化列表), 省略号形参

一 可变参数函数 有时候我们传递的参数是不固定的。 这种能接受非固定个数参数的函数就是可变参数函数 怎么实现呢&#xff1f;就要用到 initializer_list 标准库类型 该类型能够使用的前提条件是&#xff1a;所有的实参类型相同。 二&#xff0c;initializer_list(初始化列…

电阻(二):希尔伯特(Hilbert)曲线

1、Hilbert简介 希尔伯特曲线是一种能在 2D平面完美填充正方形的曲线&#xff0c;连续且稳定&#xff08;当细分足够小时&#xff0c;线构成面&#xff09;而又不可导的曲线。只要恰当选择函数&#xff0c;画出一条连续的参数曲线&#xff0c;当参数 t 在 [0、1 ] 区间取值时&a…