前面完成了多執行緒之後,那麼肯定會涉及到執行緒的同步問題。因為執行緒的執行是隨機的,亂序的。雖然我們這個小kernel實現的排程器演算法比較簡陋,它的隨機性沒那麼強,但是每次進行執行緒切換的時候,還是有可能產生問題。並且問題已經產生了。
int main(void)
return0;}
void k_thread(void *arg)
}
產生這總結果的原因是由上面這段**所致。
我們預期的輸出結果應該是這樣的
可以看到,上面的輸出結果顯得比較雜亂,主要是字串的交界處,會出現卻字元,多空格的現象。而且還引發了gp異常。
這個結果正是證實了之前所說的,每發生一次執行緒切換,都有可能帶來問題。
而帶來問題的原因就是這個裡面的put_str函式。
這個函式主要做了三個事情實現列印
獲取游標值
將游標值轉換為視訊記憶體位址,在此位址處寫入字元
更新游標的值
很明顯這三步應該同時完成,不可拆分的。那麼想象一下這種情況,執行緒1執行完了第一步之後,時間片用完,就被換下了cpu。現在輪到執行緒2執行,執行緒2又會執行第一步,且這個第一步獲取到的游標值會和之前執行緒1獲取到的一樣。然後執行緒2開始向後列印。
終於又輪到執行緒1執行了,當時他是執行到第一步,那麼接下來他就開始執行第二步。可以看到,問題就已經產生了。執行緒1獲取到的游標值早就被執行緒2用到了,那麼執行緒1在列印的過程中就會覆蓋執行緒2列印的資料。
這個是輸出的混亂問題,引發gp異常的原因主要是由於這個簡陋的滾屏操作所致,在滾屏的過程中產生了執行緒切換,導致最後獲取到的視訊記憶體位址會超過視訊記憶體段而引發的gp異常
既然要進行執行緒同步,那麼肯定要在需要同步的地方阻止執行緒的切換。這裡主要通過訊號量的機制對公共資源加鎖,達到同步的目的。
訊號量的原理本省比較簡單。通過p、v操作來表示訊號量的增減。
p、v操作的執行過程
p操作,減少訊號量
1. 判斷訊號量是否大於0
2. 如果大於0, 將其減一
3. 如果小於0,將當前執行緒阻塞
v操作,增加訊號量
1. 將訊號量的值加一
2. 喚醒等待的執行緒
訊號量是乙個全域性的共享資源,所以對其進行增減操作的時候必須是原子操作,這個原子操作通過關中斷來實現。
將執行緒阻塞的操作也很容易實現,只需要將其從就緒佇列中移除即可,下面簡單的看一下實現過程
/* 訊號量結構 */
struct semaphore
;/* 鎖結構 */
struct lock
;
p操作
/* 訊號量down操作 */
void sema_down(struct semaphore *psema)
/* 若訊號量的值等於0,則當前執行緒把自己加入該鎖的等待佇列,然後阻塞自己 */
thread_block(task_blocked); // 阻塞執行緒,直到被喚醒
}/* 若value為1或被喚醒後,會執行下面的**,也就是獲得了鎖。*/
psema->value--;
assert(psema->value == 0);
/* 恢復之前的中斷狀態 */
intr_set_status(old_status);
}
v操作
/* 訊號量的up操作 */
void sema_up(struct semaphore *psema)
psema->value++;
assert(psema->value == 1);
/* 恢復之前的中斷狀態 */
intr_set_status(old_status);
}
獲取鎖和釋放鎖
/* 獲取鎖plock */
void lock_acquire(struct lock *plock)
else
}/* 釋放鎖plock */
void lock_release(struct lock *plock)
assert(plock->holder_repeat_nr == 1);
plock->holder = null; // 把鎖的持有者置空放在v操作之前
plock->holder_repeat_nr = 0;
sema_up(&plock->semaphore); // 訊號量的v操作,也是原子操作
}
阻塞執行緒
/* 當前執行緒將自己阻塞,標誌其狀態為stat. */
void thread_block(task_status stat)
喚醒執行緒
/* 將執行緒pthread解除阻塞 */
void thread_unblock(task_struct* pthread)
list_push(&thread_ready_list, &pthread->general_tag); // 放到佇列的最前面,使其盡快得到排程
pthread->status = task_ready;
}
intr_set_status(old_status);
}
多執行緒(十一)執行緒安全介紹
當多執行緒同時共享,同乙個全域性變數或靜態變數,做寫的操作時,可能會發生資料資料衝突問題。但是做讀的操作時是不會發生資料衝突問題 需求如下 100張車票 有兩個視窗同時搶火車票,請使用多執行緒模擬搶票效果。package com.zuojie author zuojie public class t...
執行緒(一)執行緒的鎖與同步
1.如何避免死鎖 當兩個執行緒需要兩個互斥量,如執行緒一鎖住互斥量a需要互斥量b,但執行緒二鎖住互斥量b需要互斥量a,那麼就會發生死鎖。解決方法1 控制互斥量的枷鎖順序,即不上上述情況發生。2 如果程式複雜,那麼可以嘗試先施放當前鎖,過一段時間在試試。可用pthread mutex trylock函...
Linux學習(二十一) 執行緒
每個程序都有自己單獨的位址空間,這樣在程序切換的時候開銷就會比較大,為了提高系統的效能,許多作業系統規範裡引入了輕量級程序的概念,也被稱為執行緒。在同乙個程序中建立的執行緒共享該程序的位址空間。函式原型 int pthread create pthread t thread,const pthrea...