商丘網(wǎng)站制作軟件西安seo外包優(yōu)化
【MIT6.S081-2020Fall】Lab: locks
- 【MIT6.S081-2020Fall】Lab: locks
- 內存分配實驗
- 內存分配實驗準備
- 實驗目的
- 1. 舉一個例子說明修改前的**kernel/kalloc.c**中如果沒有鎖會導致哪些進程間競爭(races)問題
- 2. 說明修改前的kernel/kalloc.c中鎖競爭contention問題及其后果
- 3. 解釋acquire和release中push_off/pop_off的作用
- 4. 對上頁實驗內容的實現(xiàn)思路、代碼設計、測試結果
- 實驗思路
- 代碼設計
- 測試結果
- 同步互斥實驗目標
- Q1:關系分析,請寫出題目中存在的互斥和同步的關系
- Q2:上述關系可以抽象為幾個進程? 寫出偽代碼描述和實現(xiàn)思路
- 偽代碼展示
【MIT6.S081-2020Fall】Lab: locks
內存分配實驗
內存分配實驗準備
環(huán)境配置
- commit lab0的代碼
git commit -am lab0
- 切換到lock分支
git fetch
git checkout lock
make clean
- make; make qemu并執(zhí)行kalloctest觀察鎖競爭情況
實驗目的
1. 為每個CPU維護一個空閑頁面鏈表,每個鏈表都有自己的鎖**
? → 不同CPU上的分配和釋放可以并行執(zhí)行
2. 當某個CPU的空閑頁面用盡時,需要借用另一CPU的空閑頁面**
? → 雖然“借用”時可能引入鎖競爭,但這種情況較少發(fā)生
? 完成實驗后,需通過kalloctest的3個測試 (測試時間可能較長)
1. 舉一個例子說明修改前的kernel/kalloc.c中如果沒有鎖會導致哪些進程間競爭(races)問題
答:
- Double Free問題:如果沒有鎖來保護釋放內存的操作,多個進程可能會同時嘗試釋放同一塊內存。這可能導致內存被釋放多次,破壞內存管理的一致性。當后續(xù)的內存分配請求發(fā)生時,可能會分配到之前已經(jīng)被釋放但未及時回收的內存塊,從而導致不可預測的錯誤和安全問題。
- 內存泄漏問題:如果沒有鎖來保護內存分配的操作,多個進程可能會同時嘗試分配內存。這可能導致內存分配失敗,因為內存池可能在分配之前已經(jīng)被其他進程用盡。此時,一些進程可能無法獲得所需的內存,導致內存泄漏。
- 數(shù)據(jù)不一致性問題:如果多個進程同時修改內存池的數(shù)據(jù)結構,沒有適當?shù)逆i來同步這些操作,就可能導致數(shù)據(jù)結構的損壞,從而破壞了內存管理的一致性。
- 性能問題:沒有適當?shù)逆i來管理內存池,可能導致競爭條件,降低了系統(tǒng)的性能。競爭條件會導致進程等待或頻繁地重試內存分配/釋放操作,從而增加了系統(tǒng)的負載和延遲。
2. 說明修改前的kernel/kalloc.c中鎖競爭contention問題及其后果
答:
1、修改前的kalloc.c代碼中,只有一個內核內存分配器(kmem),這會導致在很多線程想要獲取鎖,想要CPU 調度分配內存的時候造成擁塞情況,導致大量的線程獲取鎖失敗,使得程序運行效率降低。
3. 解釋acquire和release中push_off/pop_off的作用
答:“acquire” 和 “release” 中的 “push_off” 和 “pop_off” 是通常用于實現(xiàn)中斷禁用(中斷屏蔽)的操作。這些操作通常在多任務操作系統(tǒng)中用于確保獲取鎖或釋放鎖的相關代碼的原子性執(zhí)行,以防止并發(fā)執(zhí)行的任務相互干擾。
4. 對上頁實驗內容的實現(xiàn)思路、代碼設計、測試結果
實驗思路
1、為每個CPU維護一個空閑頁面鏈表,我們可以通過修改kalloc.c
代碼中的kmem
結構體,修改為長度為NCPU
的結構體數(shù)組,再init函數(shù)中,初始化8個結構體中的鎖,進行free操作的時候,注意獲取當前CPU的id,然后對對應id的CPU進行釋放
實現(xiàn)的核心代碼:
struct
{struct spinlock lock;struct run *freelist;
} kmem[NCPU];
2、當其它CPU空閑的時候,我們怎樣借用其它CPU來完成任務呢?我們可以進行一個簡單的for循環(huán)遍歷,當發(fā)現(xiàn)這個CPU是空閑的,那么我們就可以借用這個CPU
實現(xiàn)的核心代碼
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 junkreturn (void *)r;
}
測試結果
同步互斥實驗目標
Q1:關系分析,請寫出題目中存在的互斥和同步的關系
答:同步關系包含互斥關系
1、店面的窗口屬于臨界區(qū)資源,且煎餅果子占用了該窗口后,即將該臨界區(qū)資源上鎖,雞蛋灌餅不能再占用該臨界區(qū)資源,反之亦然,此過程既體現(xiàn)了同步關系又體現(xiàn)了互斥關系
2、同學們排隊購買早餐,并且根據(jù)自己的需求站成了兩隊,體現(xiàn)出來同步關系,不至于讓整個隊伍亂掉,有序地進行,去獲取窗口上的資源,符合FIFO思想、同學們位于同步阻塞態(tài),但是當8點后,沒買到的同學到其它窗口去了,又體現(xiàn)出來非阻塞態(tài)
Q2:上述關系可以抽象為幾個進程? 寫出偽代碼描述和實現(xiàn)思路
答:上述幾個關系可以抽象為4個進程
實現(xiàn)思路:
1、可以先定義四個wait函數(shù),分別表示煎餅果子等待籃子為空,雞蛋灌餅等待籃子為空,第一支隊伍等待雞蛋灌餅被添上,第二支隊伍等待煎餅果子被添上,然后在主函數(shù)中fork四個線程,宏觀并行跑這四個函數(shù),知道到達早上8點收攤位置
2、因為題目說每天都會出現(xiàn)從一開始就排隊直到 8 點結束,所以我們可以默認;兩邊排隊的人數(shù)都足夠多,當然實際中我們還可以為隊伍人物增加設置一個人數(shù)信號量
偽代碼展示