环境配置
git commit -am lab0
git fetch
git checkout lock
make clean
1. 为每个CPU维护一个空闲页面链表,每个链表都有自己的锁**
→ 不同CPU上的分配和释放可以并行执行
2. 当某个CPU的空闲页面用尽时,需要借用另一CPU的空闲页面**
→ 虽然“借用”时可能引入锁竞争,但这种情况较少发生
完成实验后,需通过kalloctest的3个测试 (测试时间可能较长)
答:
答:
1、修改前的kalloc.c代码中,只有一个内核内存分配器(kmem),这会导致在很多线程想要获取锁,想要CPU 调度分配内存的时候造成拥塞情况,导致大量的线程获取锁失败,使得程序运行效率降低。
答:“acquire” 和 “release” 中的 “push_off” 和 “pop_off” 是通常用于实现中断禁用(中断屏蔽)的操作。这些操作通常在多任务操作系统中用于确保获取锁或释放锁的相关代码的原子性执行,以防止并发执行的任务相互干扰。
1、为每个CPU维护一个空闲页面链表,我们可以通过修改kalloc.c
代码中的kmem
结构体,修改为长度为NCPU
的结构体数组,再init函数中,初始化8个结构体中的锁,进行free操作的时候,注意获取当前CPU的id,然后对对应id的CPU进行释放
实现的核心代码:
struct
{
struct spinlock lock;
struct run *freelist;
} kmem[NCPU];
2、当其它CPU空闲的时候,我们怎样借用其它CPU来完成任务呢?我们可以进行一个简单的for循环遍历,当发现这个CPU是空闲的,那么我们就可以借用这个CPU
实现的核心代码
if (!r)
{
for (int j = 0; j < NCPU; j++)
{
if (i != j)
{
// 尝试获取锁
acquire(&kmem[j].lock);
if (kmem[j].freelist)
{
// 如果获取到了,那么就分配,并退出
r = kmem[j].freelist;
kmem[j].freelist = r->next;
release(&kmem[j].lock);
break;
}
release(&kmem[j].lock);
}
}
}
// Physical memory allocator, for user processes,
// kernel stacks, page-table pages,
// and pipe buffers. Allocates whole 4096-byte pages.
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "riscv.h"
#include "defs.h"
void freerange(void *pa_start, void *pa_end);
extern char end[]; // first address after kernel.
// defined by kernel.ld.
struct run
{
struct run *next;
};
struct
{
struct spinlock lock;
struct run *freelist;
} kmem[NCPU];
void kinit()
{
for (int i = 0; i < 8; i++)
initlock(&kmem[i].lock, "kmem");
freerange(end, (void *)PHYSTOP);
}
void freerange(void *pa_start, void *pa_end)
{
char *p;
p = (char *)PGROUNDUP((uint64)pa_start);
for (; p + PGSIZE <= (char *)pa_end; p += PGSIZE)
kfree(p);
}
// Free the page of physical memory pointed at by pa,
// which normally should have been returned by a
// call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.)
void kfree(void *pa)
{
struct run *r;
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;
int i = r_tp();
push_off();
acquire(&kmem[i].lock);
r->next = kmem[i].freelist;
kmem[i].freelist = r;
release(&kmem[i].lock);
pop_off();
}
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{
struct run *r;
push_off();
int i = r_tp();
acquire(&kmem[i].lock);
r = kmem[i].freelist;
if (r)
kmem[i].freelist = r->next;
release(&kmem[i].lock);
if (!r)
{
for (int j = 0; j < NCPU; j++)
{
if (i != j)
{
// 尝试获取锁
acquire(&kmem[j].lock);
if (kmem[j].freelist)
{
// 如果获取到了,那么就分配,并退出
r = kmem[j].freelist;
kmem[j].freelist = r->next;
release(&kmem[j].lock);
break;
}
release(&kmem[j].lock);
}
}
}
pop_off();
if (r)
memset((char *)r, 5, PGSIZE); // fill with junk
return (void *)r;
}
答:同步关系包含互斥关系
1、店面的窗口属于临界区资源,且煎饼果子占用了该窗口后,即将该临界区资源上锁,鸡蛋灌饼不能再占用该临界区资源,反之亦然,此过程既体现了同步关系又体现了互斥关系
2、同学们排队购买早餐,并且根据自己的需求站成了两队,体现出来同步关系,不至于让整个队伍乱掉,有序地进行,去获取窗口上的资源,符合FIFO思想、同学们位于同步阻塞态,但是当8点后,没买到的同学到其它窗口去了,又体现出来非阻塞态
答:上述几个关系可以抽象为4个进程
实现思路:
1、可以先定义四个wait函数,分别表示煎饼果子等待篮子为空,鸡蛋灌饼等待篮子为空,第一支队伍等待鸡蛋灌饼被添上,第二支队伍等待煎饼果子被添上,然后在主函数中fork四个线程,宏观并行跑这四个函数,知道到达早上8点收摊位置
2、因为题目说每天都会出现从一开始就排队直到 8 点结束,所以我们可以默认;两边排队的人数都足够多,当然实际中我们还可以为队伍人物增加设置一个人数信号量