/************************************************************************************html
*本文爲我的學習記錄,若有錯誤,歡迎指正。併發
* https://blog.csdn.net/liu_sheng_1991/article/details/52291427測試
* https://blog.csdn.net/smallfish_love/article/details/50753932
spa
* http://www.javashuo.com/article/p-mqrlziqc-bm.html.net
* https://www.cnblogs.com/lonelyxmas/p/4287338.html?utm_source=debugrun&utm_medium=referraldebug
************************************************************************************/code
(1)併發:併發是指多個執行單元同時、並行被執行。htm
(2)競態:併發的執行單元對共享資源(硬件資源和軟件上的全局變量,靜態變量等)的訪問容易發生競態。blog
(3)競態發生的狀況:
1)對稱多處理器(SMP)的多個CPU:SMP是一種緊耦合、共享存儲的系統模型,它的特色是多個CPU使用共同的系統總線,所以能夠訪問共同的外設和存儲器。
2)單CPU內進程與搶佔它的進程:Linux內核支持內核搶佔,一個進程在內核執行的時候可能被另外一個優先級高的進程打斷,進程與搶佔它的進程訪問共享資源的狀況相似於SMP的多個CPU。
3)中斷(硬中斷、軟中斷)與進程之間:中斷能夠打斷正在執行的進程,若是中斷處理程序訪問進程正在訪問的資源,競態也會發生。中斷也可能被新的更高優先級的中斷打斷,所以,多箇中斷之間也可能引發併發而致使競態發生。
解決競態的途徑是保證對共享資源的互斥訪問,所謂互斥訪問是指一個執行單元在訪問共享資源的時候,其餘的執行單元是被禁止訪問的。
訪問共享資源的代碼區域被稱爲臨界區,臨界區須要被以某種互斥機制加以保護。中斷屏蔽、原子操做、自旋鎖和信號量等是Linux設備驅動中可採用的互斥途徑。
(1)基本概念
在單個CPU範圍內避免競態的一種簡單省事的方法是在進入臨界區以前屏蔽系統的中斷。CPU通常具有屏蔽和打開中斷的能力,這樣能夠保證正在執行的內核路徑不被中斷處理程序搶佔,防止競態條件的發生。具體而言,中斷屏蔽使得中斷與進程之間的併發再也不發生,並且,因爲Linux內核的進程調度等操做依賴中斷來實現,內核搶佔進程之間的併發得以免。可是不能長時間屏蔽中斷,由於在中斷屏蔽期間,全部的中斷得不處處理,有可能形成數據丟失和系統崩潰的可能。這就要求在中斷屏蔽以後,當前的內核執行路徑應當儘快的執行完臨界區的代碼。
(2)操做方法
Linux內核中提供如下API來實現中斷屏蔽。
local_irq_disable(); //屏蔽中斷 local_irq_enable(); //打開中斷 local_irq_save(flags);//禁止中斷並保存當前cpu的中斷位信息
原子操做是指在執行過程當中不會被別的代碼途徑所中斷的操做,分爲整形原子操做和位原子操做。
(1)基本概念
本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取情況。它是對臨界區保護的一種經常使用方法,他的使用方法和自旋鎖差很少。與自旋鎖相同只有獲得信號量的進程才能執行臨界區的代碼。可是與自旋鎖不一樣的是,當獲取不到信號量時,進程不會原地打轉而是進入休眠等待狀態。
(2)操做方法
Linux內核中提供如下API來操做信號量。
//1)定義信號量 Struct semaphore sem; //2)初始化信號量 void sema_init(struct semaphore *sem, int val); //初始化sem爲val,固然還有系統定義的其餘宏初始化,這裏不列舉 //3)得到信號量 void down(struct semaphore *sem); //得到信號量sem,其會致使睡眠,並不能被信號打斷 int down_interruptible(struct semaphore *sem); //進入睡眠能夠被信號打斷 int down_trylock(struct semaphore *sem); //不會睡眠 //4)釋放信號量 void up(struct semaphore *sem); //釋放信號量,喚醒等待進程
(1)基本概念
互斥鎖是一種特殊的信號量。
(2)操做方法
Linux內核中提供如下API來操做互斥鎖。
//1)定義互斥鎖lock mutex_init(struct mutex* lock); //2)獲取互斥鎖 mutex_lock(struct mutex *lock); //3)釋放互斥鎖 mutex_unlock(struct mutex *lock);
(1)基本概念
自旋鎖是一種典型的對臨界資源進行互斥訪問的手段,其名稱來源於他的工做方式。爲了得到一個自旋鎖,在某CPU上運行的代碼須要先執行一個原子操做,該操做測試並設置某個內存變量,因爲是原子操做,因此在該操做完成以前其餘執行單元不可能訪問這個內存變量。若是測試代表鎖已經空閒,則程序得到這個自旋鎖並繼續執行;若是測試代表自旋鎖被佔用,程序將在一個小的循環中重複這個「測試並設置」的操做,即所謂的「自旋」。當自旋鎖的持有者經過重置該變量釋放這個自旋鎖以後,某個「等待的測試並設置」操做向其調用者報告鎖已釋放。
(2)操做方法
Linux內核中提供如下API來操做自旋鎖。
//1)定義自旋鎖 spinlock_t lock;
//2)初始化自旋鎖 spin_lock_init(lock);
//3)獲取自旋鎖 spin_lock(lock); //得到自旋鎖lock spin_trylock(lock);//嘗試獲取lock若是不能得到鎖,返回假值,不在原地打轉
//4)釋放自旋鎖 spin_unlock(lock); //釋放自旋鎖
P.S.:信號量與自旋鎖的區別:
自旋鎖不會引發調用者睡眠,若是自旋鎖已經被別的執行單元保持,調用者就一直循環查看是否該自旋鎖的保持者已經釋放了鎖,"自旋"就是"在原地打轉"。而信號量則引發調用者睡眠,它把進程從運行隊列上拖出去,除非得到鎖。
鑑於自旋鎖與信號量的上述特色,通常而言,自旋鎖適合於保持時間很是短的狀況,它能夠在任何上下文使用;信號量適合於保持時間較長的狀況,會只能在進程上下文使用。若是被保護的共享資源只在進程上下文訪問,則能夠以信號量來保護該共享資源,若是對共享資源的訪問時間很是短,自旋鎖也是好的選擇。可是,若是被保護的共享資源須要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。