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

MIT6.s081_Lab8

MIT6.s081 Lab8:locks

这个lab主要就是实现“一个锁变成多个锁”,对于一个相同的数据类型,分成多份,然后分别加锁。这个真的是做的最快的实现,所有测试一把过。

代码

1. Memory allocator

内核中的页表都是由一个链表维护的,每次需要申请或者释放内存的时候,我们需要链表的锁,防止出现对数据的重复操作之类的,引起一些意料之外的错误,因此分开链表,每个CPU维护一个能减少获取锁的次数,也是这个实验的关键。在写博客的过程中,发现了一点中断不合理的地方,代码已经进行修改。

  1. 增加页表“维护者”,设为CPU的个数。

    struct {struct spinlock lock;struct run *freelist;
    } kmem[NCPU];
    
  2. 对每个链表初始化,这个锁的名称命名可能有点奇怪。

    void
    kinit()
    {int i;char kmem_name[6] = {'k', 'm', 'e', 'm', '0', '\0'};for(i = 0; i < NCPU; i++) {kmem_name[4] = (char)(i + (int)'0');initlock(&kmem[i].lock, kmem_name);}freerange(end, (void*)PHYSTOP);
    }
    
  3. 修改kfree,每次释放的锁由CPU的 id 决定,这里需要关闭中断,以防出现下面这种情况。

    在进行分配的时候,本来是在cpu0上执行,中断发生,结果分配到CPU1上了,这样会出现CPU1操作CPU0的链表,这是不合理的。

    kfree(void *pa)
    {struct run *r;push_off();int cpu_id = cpuid();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;acquire(&kmem[cpu_id].lock);r->next = kmem[cpu_id].freelist;kmem[cpu_id].freelist = r;release(&kmem[cpu_id].lock);pop_off();
    }
    
  4. 修改kalloc。同样的,要防止中断的发生;然后可能稍微复杂一点的地方在于‘窃取’页面,如果CPU自己没有空闲的,要去别的CPU看看,跳过自己,然后如果有,就偷一个‘头’,在分配给自己。

    kalloc(void)
    {struct run *r;push_off();int cpu_id = cpuid();acquire(&kmem[cpu_id].lock);r = kmem[cpu_id].freelist;if(r)kmem.freelist = r->next;release(&kmem.lock);kmem[cpu_id].freelist = r->next;else {for(int i = 0; i < NCPU; i++) {if(r)break;if(i == cpu_id)continue;acquire(&kmem[i].lock);struct run *oc;oc = kmem[i].freelist;if(oc) {kmem[i].freelist = oc->next;oc->next = r;r = oc;}release(&kmem[i].lock);}}release(&kmem[cpu_id].lock);pop_off();if(r)memset((char*)r, 5, PGSIZE); // fill with junkreturn (void*)r;
    }
    

2. Buffer cache

虽然是hard,但是仔细分析之后,做起来也不是很难,思路总体上来看和上一个差不多,还是分锁,上一个根据CPU分,这个用质数个HASH桶,也就是质数个锁。

  1. 修改bcache,使用桶,同时减少buf的个数,这里有上限(使用30个的话,initlock会出问题),因此要减少buf的数量,其实也可以限制为最大30个,怎么方便怎么来吧。

    #define BUCKETS      13
    #define NEWNBUF      (MAXOPBLOCKS)
    //上面两个我放在param.h中的
    struct {struct spinlock lock;struct buf buf[NEWNBUF];struct buf head;
    } bcache[BUCKETS];
    
  2. 修改初始化,修改了结构,肯定要修改初始化,代码如下。只能说太像了

    void
    binit(void)
    {int i;char bcache_name[8] = {'b', 'c', 'a', 'c', 'h', 'e', '0', '\0'};char buffer_name[8] = {'b', 'u', 'f', 'f', 'e', 'r', '0', '\0'};for(i = 0; i < BUCKETS; i++) {bcache_name[6] = (char)(i + '0');initlock(&bcache[i].lock, bcache_name);for(int j = 0; j < NEWNBUF; j++) {buffer_name[6] = (char)(j + '0');initsleeplock(&(bcache[i].buf+j)->lock, buffer_name);}}
    }
    
  3. buf中增加一个时间戳,方便找到最久未使用的,以减少锁的使用。

    struct buf {
    ……uint timestamp;
    };
    
  4. 修改brelse,因为加了时间戳,这里更新一下时间戳即可,不需要遍历链表放到最后了,减少锁的使用。

    void
    brelse(struct buf *b)
    {if(!holdingsleep(&b->lock))panic("brelse");releasesleep(&b->lock);b->refcnt--;b->timestamp = ticks;
    }
    
  5. 因为使用了桶,每个blockno要寻找自己对应的桶,方便对锁进行分散,用blockno % BUCKETS来获取需要使用用哪个锁,这里先修改bpinbunpin,比较简单

    void
    bpin(struct buf *b) {int buckets_no = b->blockno % BUCKETS;acquire(&bcache[buckets_no].lock);b->refcnt++;release(&bcache[buckets_no].lock);
    }void
    bunpin(struct buf *b) {int buckets_no = b->blockno % BUCKETS;acquire(&bcache[buckets_no].lock);b->refcnt--;release(&bcache[buckets_no].lock);
    }
    
  6. 修改bget,最难的一部分,差别最大就是,当没有buf存放找的blockno时,要找一个最久没使用的给他,但是也是从当前的桶里找,也是容易的,如果很久没有使用,并且引用为0,就可以将这个buf给这个blockno了。

    bget(uint dev, uint blockno)
    {struct buf *b;int i;int buckets_no = blockno % BUCKETS;acquire(&bcache[buckets_no].lock);// Is the block already cached?for(i = 0; i < NEWNBUF; i++) {b = bcache[buckets_no].buf+i;if(b->dev == dev && b->blockno == blockno){b->refcnt++;release(&bcache[buckets_no].lock);acquiresleep(&b->lock);return b;}}int minstamp = 0xfffffff;int lruindex = -1;// Not cached.// Recycle the least recently used (LRU) unused buffer.for(i = 0; i < NEWNBUF; i++) {b = bcache[buckets_no].buf+i;if(b->timestamp < minstamp && b->refcnt == 0){minstamp = b->timestamp;lruindex = i;}}if(lruindex >= 0) {b = bcache[buckets_no].buf + lruindex;b->dev = dev;b->blockno = blockno;b->valid = 0;b->refcnt = 1;release(&bcache[buckets_no].lock);acquiresleep(&b->lock);return b;}panic("bget: no buffers");
    }
http://www.kefakeji.com/news/858.html

相关文章:

  • 关于同源策略和跨域请求
  • Codeforces Round 1039 (Div. 2) 1 ~ D
  • 【转】[C#] 参数前加 in 的作用
  • 循环链表实现的队列
  • Codeforces Round 1039 (Div. 2)(A~E1)
  • 关于博客主题的一些思考
  • PG故障处理:PG_AUTO_FAILOVER自动切换失败的故障处理
  • Codeforces Round 1039 (Div. 2)
  • 21天
  • 【转】[C#] Enum 的 Flags 特性
  • 白嫖claude code的100美元额度,anyrouter中转服务
  • 2025.7.27 东师集训测试总结
  • POLIR-Laws-电商交易: 三方的法律关系确定: 网络交易双方与网络交易平台三者之间的法律关系
  • Umi-OCR完全指南:开源离线OCR识别软件下载安装使用教程|支持批量PDF/二维码识别
  • Docker
  • 7.28
  • 图像预处理 + Tesseract OCR 实战
  • 实现验证码识别:图像预处理 + Tesseract OCR 实战
  • java 网络编程
  • systemd 的unit配置文件里[Service]里的WorkingDirectory有什么用,如何配置
  • Python实现验证码识别:图像预处理 + Tesseract OCR 实战
  • 一些未来的思考
  • 学习之道 反思 记忆
  • Reference
  • 学习之道 反思 自信
  • 博弈论 冯 诺伊曼
  • Moq 的使用
  • InnoDB架构
  • 离线安装node.js node-red,及设置为服务注意事项
  • 北航操作系统上机实验使用vscode指南