20.實現同步機制--鎖

上一篇 博文中 運行了 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操作,也是原子操作
}

本文只是實現了鎖,但是關於鎖的使用將放在後面的博文中。

😁😁😁😁😁

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章