这个lab比起上个lab难度大了一些,需要熟练掌握锁的使用。
笔者用时约7h(我太菜啦
Memory allocator
第一部分相对比较简单,就是为每个CPU独立出一个内存分配器(管理内存的链表),减少锁的竞争,提高程序并行度。具体来说,一开始锁争用的根本原因就是内核只维护了一个内存空闲列表,由一个锁去保护,于是多个CPU争用该列表的频率很高。优化的基本思想是为每个CPU维护一个空闲列表,每个列表都有自己的锁,因为不同CPU在不同的列表中运行,所以它们的分配和释放可以并行运行。
当一个CPU的列表为空时,它必须夺取其他CPU列表中的空闲内存,虽然这会导致锁争用,但是频率并不高。
具体实现上,首先将kmem结构体定义为结构体数组,表示多个CPU的内存空闲列表,如下所示:
struct {
struct spinlock lock;
struct run *freelist;
} kmem[NCPU];
在kinit函数中初始化这些结构体中的锁:
void
kinit()
{
int id;
for (id = 0; id < NCPU; id ++ )
initlock(&kmem[id].lock, "kmem");
freerange(end, (void*)PHYSTOP);
}
然后在kalloc的时候,首先从CPU本身的内存空闲列表分配,如果该CPU的空闲列表已经空了,那么从其他CPU的空闲列表中抢一个过来即可,如下所示。其中,锁的添加依赖于CPUid。
void *
kalloc(void)
{
struct run *r;
int cpuid = getcpuid();
int id;
acquire(&kmem[cpuid].lock);
r = kmem[cpuid].freelist;
if(r)
kmem[cpuid].freelist = r->next;
release(&kmem[cpuid].lock);
if(!r) {
for (id = 0; id < NCPU; id ++ ) {
if (id != cpuid) {
acquire(&kmem[id].lock);
r = kmem[id].freelist;
if(r)
kmem[id].freelist = r->next;
release(&kmem[id].lock);
if(r) break;
}
}
}
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
在kfree中,将内存结点回收到本CPU的空闲列表中即可,如下:
void
kfree(void *pa)
{
struct run *r

文章介绍了如何通过为每个CPU分配独立的内存分配器和使用哈希表分散锁的使用来降低锁的竞争,从而提高内存分配和缓冲区缓存的并行度。在内存分配器部分,通过为每个CPU维护单独的内存列表和锁,减少了锁的争用。而在缓冲区缓存部分,通过哈希表按磁盘块编号分桶并为每个桶添加锁,减少了锁的争用,同时使用时间戳实现LRU缓存策略。
最低0.47元/天 解锁文章
3345





