上一篇 博文中 運行了 3 個線程(主線程、k_thread_a、k_thread_b ),運行一段時間就會出現異常,這是由於產生了競爭條件。
有 2 種競爭條件:(1)對顯存未實現互斥訪問。(2)對“光標寄存器”未實現互斥訪問。
1. 相關概念
-
公共資源
所有任務共享的一套資源。 -
臨界區
各個任務訪問公共資源的臨界代碼,臨界區是指令,不是受訪的靜態公共資源。 -
互斥
某一時刻,公共資源只能被一個任務獨享。 -
競爭條件
多個任務以非互斥的方式同時進入臨界區。 -
信號量
-
鎖
2. 實現鎖
thread.c 中添加兩個函數實現線程的阻塞狀態。
/* 當前線程將自己阻塞,標誌其狀態爲stat. */
void thread_block(enum task_status stat) {
/* stat取值爲TASK_BLOCKED,TASK_WAITING,TASK_HANGING,也就是隻有這三種狀態纔不會被調度*/
ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING)));
enum intr_status old_status = intr_disable();
struct task_struct* cur_thread = running_thread();
cur_thread->status = stat; // 置其狀態爲stat
schedule(); // 將當前線程換下處理器
/* 待當前線程被解除阻塞後才繼續運行下面的intr_set_status */
intr_set_status(old_status);
}
/* 將線程pthread解除阻塞 */
void thread_unblock(struct task_struct* pthread) {
enum intr_status old_status = intr_disable();
ASSERT(((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGING)));
if (pthread->status != TASK_READY) {
ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag));
if (elem_find(&thread_ready_list, &pthread->general_tag)) {
PANIC("thread_unblock: blocked thread in ready_list\n");
}
list_push(&thread_ready_list, &pthread->general_tag); // 放到隊列的最前面,使其儘快得到調度
pthread->status = TASK_READY;
}
intr_set_status(old_status);
}
再實現兩個文件,實現鎖。
sync.h
#ifndef __THREAD_SYNC_H
#define __THREAD_SYNC_H
#include "../lib/kernel/list.h"
#include "../lib/std_int.h"
#include "thread.h"
#define NULL ((void*)0)
/* 信號量結構 */
struct semaphore {
uint8_t value;
struct list waiters;
};
/* 鎖結構 */
struct lock {
struct task_struct* holder; // 鎖的持有者
struct semaphore semaphore; // 用二元信號量實現鎖
uint32_t holder_repeat_nr; // 鎖的持有者重複申請鎖的次數
};
void sema_init(struct semaphore* psema, uint8_t value);
void sema_down(struct semaphore* psema);
void sema_up(struct semaphore* psema);
void lock_init(struct lock* plock);
void lock_acquire(struct lock* plock);
void lock_release(struct lock* plock);
#endif
sync.c
#include "sync.h"
#include "../lib/kernel/list.h"
//#include "global.h"
#include "../kernel/debug.h"
#include "../kernel/my_interrupt.h"
/* 初始化信號量 */
void sema_init(struct semaphore* psema, uint8_t value) {
psema->value = value; // 爲信號量賦初值
list_init(&psema->waiters); //初始化信號量的等待隊列
}
/* 初始化鎖plock */
void lock_init(struct lock* plock) {
plock->holder = NULL;
plock->holder_repeat_nr = 0;
sema_init(&plock->semaphore, 1); // 信號量初值爲1
}
/* 信號量down操作 */
void sema_down(struct semaphore* psema) {
/* 關中斷來保證原子操作 */
enum intr_status old_status = intr_disable();
while(psema->value == 0) { // 若value爲0,表示已經被別人持有
ASSERT(!elem_find(&psema->waiters, &running_thread()->general_tag));
/* 當前線程不應該已在信號量的waiters隊列中 */
if (elem_find(&psema->waiters, &running_thread()->general_tag)) {
PANIC("sema_down: thread blocked has been in waiters_list\n");
}
/* 若信號量的值等於0,則當前線程把自己加入該鎖的等待隊列,然後阻塞自己 */
list_append(&psema->waiters, &running_thread()->general_tag);
thread_block(TASK_BLOCKED); // 阻塞線程,直到被喚醒
}
/* 若value爲1或被喚醒後,會執行下面的代碼,也就是獲得了鎖。*/
psema->value--;
ASSERT(psema->value == 0);
/* 恢復之前的中斷狀態 */
intr_set_status(old_status);
}
/* 信號量的up操作 */
void sema_up(struct semaphore* psema) {
/* 關中斷,保證原子操作 */
enum intr_status old_status = intr_disable();
ASSERT(psema->value == 0);
if (!list_empty(&psema->waiters)) {
struct task_struct* thread_blocked = elem2entry(struct task_struct, general_tag, list_pop(&psema->waiters));
thread_unblock(thread_blocked);
}
psema->value++;
ASSERT(psema->value == 1);
/* 恢復之前的中斷狀態 */
intr_set_status(old_status);
}
/* 獲取鎖plock */
void lock_acquire(struct lock* plock) {
/* 排除曾經自己已經持有鎖但還未將其釋放的情況。*/
if (plock->holder != running_thread()) {
sema_down(&plock->semaphore); // 對信號量P操作,原子操作
plock->holder = running_thread();
ASSERT(plock->holder_repeat_nr == 0);
plock->holder_repeat_nr = 1;
} else {
plock->holder_repeat_nr++;
}
}
/* 釋放鎖plock */
void lock_release(struct lock* plock) {
ASSERT(plock->holder == running_thread());
if (plock->holder_repeat_nr > 1) {
plock->holder_repeat_nr--;
return;
}
ASSERT(plock->holder_repeat_nr == 1);
plock->holder = NULL; // 把鎖的持有者置空放在V操作之前
plock->holder_repeat_nr = 0;
sema_up(&plock->semaphore); // 信號量的V操作,也是原子操作
}
本文只是實現了鎖,但是關於鎖的使用將放在後面的博文中。
😁😁😁😁😁