当前位置: 首页 > news >正文

mit6s081 lab8 locks

mit6s081 lab8 locks

一、Memory Allocator(内存分配器)

📘 题目描述

为每个 CPU 实现独立的空闲列表,当 CPU 的空闲列表为空时,可以从其他 CPU 的空闲列表窃取空闲内存

所有的锁都以 "kmem" 开头。你应该为每个锁调用 initlock() 并传入一个以 "kmem" 开头的名称。

通过运行以下命令进行验证:

  • kalloctest:查看锁争用是否显著减少;
  • usertests sbrkmuch:验证是否仍然可以分配所有内存。

输出结果中,尽管具体数值可能不同,但 kmem 锁的总争用次数应显著减少


💡 实现思路

  • 所有空闲内存最初都分配给 CPU0;
  • 当 CPU1 需要内存时,可以窃取 CPU0 的空闲块;
  • 使用完成后,释放的内存会挂回到 CPU1 的空闲列表;
  • 这样 CPU1 下次再分配时就可以直接从自己的空闲列表中获取。

🧱 数据结构定义

为每个 CPU 分配一个独立的空闲链表及其锁:

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

🔧 修改 kinit

只有一个 CPU(通常是 CPU0)会调用该函数。
该函数负责初始化每个 CPU 的锁,并调用 freerange() 将物理内存放入空闲链表中。

void
kinit()
{char lockname[NCPU];for (int i = 0; i < NCPU; ++i) {snprintf(lockname, sizeof(lockname), "kmem_%d", i); // 为每个 CPU 的锁命名initlock(&kmem[i].lock, lockname); // 初始化锁}freerange(end, (void*)PHYSTOP); // 将用户空间所有内存加入空闲链表
}

🔩 修改 kfree

获取 CPU ID 时必须关闭中断,保证获取的 ID 正确。

void
kfree(void *pa)
{struct run *r;if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");// 用垃圾数据填充,检测悬空引用memset(pa, 1, PGSIZE);r = (struct run*)pa;// 关中断push_off();int id = cpuid(); // 当前 CPU idacquire(&kmem[id].lock);r->next = kmem[id].freelist;kmem[id].freelist = r;release(&kmem[id].lock);pop_off(); // 开中断
}

⚙️ 修改 kalloc

当当前 CPU 的空闲列表为空时,尝试从其他 CPU 的空闲列表中窃取内存块

void *
kalloc(void)
{struct run *r;push_off();int id = cpuid();acquire(&kmem[id].lock);r = kmem[id].freelist;if(r)kmem[id].freelist = r->next;else {int antid; // 其他 CPU idfor (antid = 0; antid < NCPU; ++antid) {if (antid == id) continue;acquire(&kmem[antid].lock);r = kmem[antid].freelist;if (r) {kmem[antid].freelist = r->next;release(&kmem[antid].lock);break;}release(&kmem[antid].lock);}}release(&kmem[id].lock);pop_off();if (r)memset((char*)r, 5, PGSIZE); // 填充垃圾数据return (void*)r;
}

✅ 测试截图

在这里插入图片描述


二、Buffer Cache(缓冲区缓存)

📘 题目描述

修改块缓存,使得运行 bcachetest 时,bcache 中所有锁的 acquire 循环迭代次数接近于 0。

理想情况是所有锁的总争用计数为 0(小于 500 也可接受)。
修改 bget()brelse(),让多个进程能够并发查找和释放缓存块,减少锁竞争。

同时必须保持:

每个磁盘块最多只能缓存一个副本。


💡 优化思路

  • 尽可能减少共享:能独享就独享,比如每 CPU 独立空闲链表。
  • 必须共享时:减少临界区的锁粒度,缩短加锁时间。

⚙️ xv6 原设计

  • 使用双向链表存储所有缓存块;
  • 查找时需遍历整个链表;
  • 若缓存中已有对应 block,则直接返回;
  • 否则选择最近最久未使用且 refcnt == 0 的 buf 进行替换;
  • 整个过程都要持有 bcache.lock,导致严重锁竞争。

🚀 新的改进方案

  • 建立 哈希表(blockno → buf)
  • 每个桶(bucket)配一把独立的自旋锁;
  • 查找 / 插入时仅锁定对应桶;
  • 仅当桶中无空闲 buf 时,再从其他桶“偷取”;
  • 大幅减少全局锁竞争。

🧱 结构体修改

struct buf {int valid;      // 是否从磁盘读取过数据int disk;       // 是否为磁盘所有uint dev;uint blockno;struct sleeplock lock;uint refcnt;uint lastuse;   // *新增*:记录最近一次使用时间(用于 LRU)struct buf *next;uchar data[BSIZE];
};

🔧 全局结构定义与初始化

#define NBUFMAP_BUCKET 13
#define BUFMAP_HASH(dev, blockno) ((((dev)<<27)|(blockno))%NBUFMAP_BUCKET)struct {struct buf buf[NBUF];struct spinlock eviction_lock;// 哈希表:从 dev+blockno 到 buf 的映射struct buf bufmap[NBUFMAP_BUCKET];struct spinlock bufmap_locks[NBUFMAP_BUCKET];
} bcache;void
binit(void)
{// 初始化哈希桶for(int i = 0; i < NBUFMAP_BUCKET; i++) {initlock(&bcache.bufmap_locks[i], "bcache_bufmap");bcache.bufmap[i].next = 0;}// 初始化缓存块for(int i = 0; i < NBUF; i++){struct buf *b = &bcache.buf[i];initsleeplock(&b->lock, "buffer");b->lastuse = 0;b->refcnt = 0;b->next = bcache.bufmap[0].next;bcache.bufmap[0].next = b;}initlock(&bcache.eviction_lock, "bcache_eviction");
}

🔍 修改 bget

static struct buf*
bget(uint dev, uint blockno)
{struct buf *b;uint key = BUFMAP_HASH(dev, blockno);acquire(&bcache.bufmap_locks[key]);// 查找是否已缓存for (b = bcache.bufmap[key].next; b; b = b->next) {if (b->dev == dev && b->blockno == blockno) {b->refcnt++;release(&bcache.bufmap_locks[key]);acquiresleep(&b->lock);return b;}}// 若未命中缓存release(&bcache.bufmap_locks[key]);acquire(&bcache.eviction_lock);// 再次确认是否已有缓存(防止重复创建)for (b = bcache.bufmap[key].next; b; b = b->next) {if (b->dev == dev && b->blockno == blockno) {acquire(&bcache.bufmap_locks[key]);b->refcnt++;release(&bcache.bufmap_locks[key]);release(&bcache.eviction_lock);acquiresleep(&b->lock);return b;}}// 选择 LRU 块进行替换struct buf *before_least = 0;uint holding_bucket = -1;for (int i = 0; i < NBUFMAP_BUCKET; i++) {acquire(&bcache.bufmap_locks[i]);int newfound = 0;for (b = &bcache.bufmap[i]; b->next; b = b->next) {if (b->next->refcnt == 0 && (!before_least || b->next->lastuse < before_least->next->lastuse)) {before_least = b;newfound = 1;}}if (!newfound)release(&bcache.bufmap_locks[i]);else {if (holding_bucket != -1)release(&bcache.bufmap_locks[holding_bucket]);holding_bucket = i;}}if (!before_least)panic("bget: no buffers");b = before_least->next;// 若需要移动到目标桶if (holding_bucket != key) {before_least->next = b->next;release(&bcache.bufmap_locks[holding_bucket]);acquire(&bcache.bufmap_locks[key]);b->next = bcache.bufmap[key].next;bcache.bufmap[key].next = b;}b->dev = dev;b->blockno = blockno;b->refcnt = 1;b->valid = 0;release(&bcache.bufmap_locks[key]);release(&bcache.eviction_lock);acquiresleep(&b->lock);return b;
}

🔁 修改 brelsebpinbunpin

void
brelse(struct buf *b)
{if (!holdingsleep(&b->lock))panic("brelse");releasesleep(&b->lock);uint key = BUFMAP_HASH(b->dev, b->blockno);acquire(&bcache.bufmap_locks[key]);b->refcnt--;if (b->refcnt == 0)b->lastuse = ticks;release(&bcache.bufmap_locks[key]);
}void
bpin(struct buf *b) {uint key = BUFMAP_HASH(b->dev, b->blockno);acquire(&bcache.bufmap_locks[key]);b->refcnt++;release(&bcache.bufmap_locks[key]);
}void
bunpin(struct buf *b) {uint key = BUFMAP_HASH(b->dev, b->blockno);acquire(&bcache.bufmap_locks[key]);b->refcnt--;release(&bcache.bufmap_locks[key]);
}

🧩 总结

对象是否在内存中是否可每 CPU 独立是否需要全局同步优化方式
空闲内存(kalloc)✅ 是✅ 可以每 CPU 独立空闲链表
磁盘缓存(bcache)✅ 是❌ 不行✅ 必须哈希分桶 + 每桶独立锁

结论:

  • 虽然磁盘缓存也使用内存,但它存储的是全局共享的文件系统块数据
  • 因此必须保证数据一致性,不能按 CPU 独立分配;
  • 为提高并发性能,应将全局锁细化为哈希桶锁;
    | ------------ | ----------------- | ---------------- | --------------------- |
    | 空闲内存(kalloc) | ✅ 是 | ✅ 可以 | 否 | 每 CPU 独立空闲链表 |
    | 磁盘缓存(bcache) | ✅ 是 | ❌ 不行 | ✅ 必须 | 哈希分桶 + 每桶独立锁 |

结论:

  • 虽然磁盘缓存也使用内存,但它存储的是全局共享的文件系统块数据
  • 因此必须保证数据一致性,不能按 CPU 独立分配;
  • 为提高并发性能,应将全局锁细化为哈希桶锁;
  • 从而在全局共享一致性的前提下实现高并行度的访问。
http://www.dtcms.com/a/482762.html

相关文章:

  • 建站培训企业管理考研
  • MySQL中的数据类型占用空间和范围
  • Docker部署jenkins集成全自动打包部署
  • 台州自助建站公司做好的网站怎么发布
  • 重磅更新:Claude Code 现在支持插件啦
  • 23种设计模式——访问者模式 (Visitor Pattern)
  • 卧龙区微网站开发网络管理系统怎么打开
  • java用注解优雅校验入参
  • 平台网站建设收费制作网页素材图片
  • 基于OpenCV的实时疲劳检测系统实现
  • HTML5基础——4、表格
  • Docker 通信核心:docker.sock 完全指南
  • Flutter---Button
  • 网站建设需要敲代码吗信息流推广实施策划书
  • 广元网站建设电子商务网站建设主要内容
  • 4.4-中间件之gRPC
  • 网络与通信安全课程复习汇总1——课程导入
  • Qt模型控件:QTreeViewQTreeWidget
  • 河南省城乡建设厅官网廊坊快速优化排名
  • 网站建设应该考虑哪些问题企信宝
  • 青海做网站最好的公司互联网推广引流
  • 一天一个设计模式——装饰器模式
  • 婚恋交友 APP 核心功能分析:从匹配逻辑到用户体验的全链路设计
  • 用一个 prompt 搭建带 React 界面的 Java 桌面应用
  • 宁波建网站价格wordpress注明网站
  • wordpress添加网站地图黑龙江新闻头条最新消息
  • 机械臂装配自动化推动紧固件设计革新
  • JSAR 入门教程:从零开始开发空间天气小摆件
  • 【Pytorch】什么是梯度
  • 核间通信机制