本Lab比較簡單,就是為xv6添加一個用戶級的多線程功能,然后熟悉一下Linux下多線程編程。
筆者用時約2h
Uthread: switching between threads
這一部分的代碼不涉及內(nèi)核代碼,所以也比較簡單,根據(jù)提示修改user/uthread.c中的代碼即可。仿照內(nèi)核中進程轉(zhuǎn)換函數(shù)swtch
的實現(xiàn)即可。首先,添加一個context
上下文結(jié)構(gòu)體用于保存被調(diào)用者保存寄存器(從kernel/proc.h中復(fù)制過來就行啦)。
struct context {
uint64 ra;
uint64 sp;
// callee-saved
uint64 s0;
uint64 s1;
uint64 s2;
uint64 s3;
uint64 s4;
uint64 s5;
uint64 s6;
uint64 s7;
uint64 s8;
uint64 s9;
uint64 s10;
uint64 s11;
};
并且為thread
添加一個context
字段
struct thread {
char stack[STACK_SIZE]; /* the thread's stack */
int state; /* FREE, RUNNING, RUNNABLE */
struct context context;
};
然后需要仿照swtch
函數(shù)(定義在kernel/swtch.S中)實現(xiàn)一個thread_switch
函數(shù)(定義在user/uthread_switch.S中),其實也就是復(fù)制過來就好啦
thread_switch:
/* YOUR CODE HERE */
sd ra, 0(a0)
sd sp, 8(a0)
sd s0, 16(a0)
sd s1, 24(a0)
sd s2, 32(a0)
sd s3, 40(a0)
sd s4, 48(a0)
sd s5, 56(a0)
sd s6, 64(a0)
sd s7, 72(a0)
sd s8, 80(a0)
sd s9, 88(a0)
sd s10, 96(a0)
sd s11, 104(a0)
ld ra, 0(a1)
ld sp, 8(a1)
ld s0, 16(a1)
ld s1, 24(a1)
ld s2, 32(a1)
ld s3, 40(a1)
ld s4, 48(a1)
ld s5, 56(a1)
ld s6, 64(a1)
ld s7, 72(a1)
ld s8, 80(a1)
ld s9, 88(a1)
ld s10, 96(a1)
ld s11, 104(a1)
ret /* return to ra */
然后為thread_create
函數(shù)添加代碼,為線程初始化上下文字段,最重要的兩個寄存器是ra
和sp
,其中ra
寄存器需要指向傳進來的線程函數(shù),在thread_switch
函數(shù)中將被替換到CPU的ra
,作為返回地址,即起到了一個跳板的作用,跳到了線程函數(shù)中;sp
寄存器則需要指向線程的棧。
void
thread_create(void (*func)())
{
struct thread *t;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->state = RUNNABLE;
// YOUR CODE HERE
t->context.ra = (uint64) func;
t->context.sp = (uint64) (t->stack + STACK_SIZE);
}
在thread_schedule
函數(shù)中,增加一句代碼調(diào)用thread_switch
函數(shù)進行線程轉(zhuǎn)換即可。
if (current_thread != next_thread) { /* switch threads? */
next_thread->state = RUNNING;
t = current_thread;
current_thread = next_thread;
/* YOUR CODE HERE
* Invoke thread_switch to switch from t to next_thread:
* thread_switch(??, ??);
*/
thread_switch((uint64)&t->context, (uint64)¤t_thread->context);
}
Using threads
首先回答一個問題:
Q: 為什么兩個線程都丟失了鍵,而不是一個線程?確定可能導(dǎo)致鍵丟失的具有2個線程的事件序列。
A: 當(dāng)兩個線程同時調(diào)用put且插入的哈希bucket是同一個時,涉及到鏈表插入的并發(fā)性問題,
其實在xv6-book中第六章有講到過。由于這里是頭插法,修改頭指針指向當(dāng)前新插入的節(jié)點時,
如果兩個線程的操作交叉運行,則后運行的一個會被前運行的一個覆蓋,使得插入的節(jié)點少了一個,
這種行為是不可預(yù)測的,所以兩個線程都有可能丟失。
然后需要為代碼加鎖以修復(fù)并發(fā)put的問題,考慮如果兩個線程并發(fā)put到兩個不同的bucket中,不會引發(fā)問題,于是為每一個bucket設(shè)置一個鎖即可。
pthread_mutex_t lock[NBUCKET];
然后在put
函數(shù)中加上鎖即可。文章來源:http://www.zghlxwxcb.cn/news/detail-406444.html
if(e){
pthread_mutex_lock(&lock[i]);
// update the existing key.
e->value = value;
pthread_mutex_unlock(&lock[i]);
} else {
// the new is new.
pthread_mutex_lock(&lock[i]);
insert(key, value, &table[i], table[i]);
pthread_mutex_unlock(&lock[i]);
}
Barrier
這一部分據(jù)文檔描述就是加上一層屏障,進行線程同步,需要使用條件變量的知識,這里就不細說了,簡單來說就是,讓一些快的線程等一會,讓最后一個到達的線程通知大家說可以繼續(xù)走了,實現(xiàn)起來也很簡單。文章來源地址http://www.zghlxwxcb.cn/news/detail-406444.html
static void
barrier()
{
// YOUR CODE HERE
//
// Block until all threads have called barrier() and
// then increment bstate.round.
//
pthread_mutex_lock(&bstate.barrier_mutex);
bstate.nthread ++;
if (bstate.nthread < nthread) {
pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
pthread_mutex_unlock(&bstate.barrier_mutex);
return;
}
bstate.nthread = 0;
bstate.round ++;
pthread_mutex_unlock(&bstate.barrier_mutex);
pthread_cond_broadcast(&bstate.barrier_cond);
}
到了這里,關(guān)于【MIT 6.S081】Lab7: Multithreading的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!