Linux内核的并发与竞态、信号量、互斥锁、自旋锁
/************************************************************************************
*本文為個人學習記錄,如有錯誤,歡迎指正。
*本文參考資料:?
* https://blog.csdn.net/liu_sheng_1991/article/details/52291427
* https://blog.csdn.net/smallfish_love/article/details/50753932
* https://www.cnblogs.com/biaohc/p/6679195.html
* https://www.cnblogs.com/lonelyxmas/p/4287338.html?utm_source=debugrun&utm_medium=referral
************************************************************************************/
1. 并發與競態
(1)并發:并發是指多個執行單元同時、并行被執行。
(2)競態:并發的執行單元對共享資源(硬件資源和軟件上的全局變量,靜態變量等)的訪問容易發生競態。
(3)競態發生的情況:
1)對稱多處理器(SMP)的多個CPU:SMP是一種緊耦合、共享存儲的系統模型,它的特點是多個CPU使用共同的系統總線,因此可以訪問共同的外設和存儲器。
2)單CPU內進程與搶占它的進程:Linux內核支持內核搶占,一個進程在內核執行的時候可能被另一個優先級高的進程打斷,進程與搶占它的進程訪問共享資源的情況類似于SMP的多個CPU。
3)中斷(硬中斷、軟中斷)與進程之間:中斷可以打斷正在執行的進程,如果中斷處理程序訪問進程正在訪問的資源,競態也會發生。中斷也可能被新的更高優先級的中斷打斷,因此,多個中斷之間也可能引起并發而導致競態發生。
2. 競態的解決方法
解決競態的途徑是保證對共享資源的互斥訪問,所謂互斥訪問是指一個執行單元在訪問共享資源的時候,其他的執行單元是被禁止訪問的。
訪問共享資源的代碼區域被稱為臨界區,臨界區需要被以某種互斥機制加以保護。中斷屏蔽、原子操作、自旋鎖和信號量等是Linux設備驅動中可采用的互斥途徑。
2.1 中斷屏蔽
(1)基本概念
在單個CPU范圍內避免競態的一種簡單省事的方法是在進入臨界區之前屏蔽系統的中斷。CPU一般具備屏蔽和打開中斷的能力,這樣可以保證正在執行的內核路徑不被中斷處理程序搶占,防止競態條件的發生。具體而言,中斷屏蔽使得中斷與進程之間的并發不再發生,而且,由于Linux內核的進程調度等操作依賴中斷來實現,內核搶占進程之間的并發得以避免。但是不能長時間屏蔽中斷,因為在中斷屏蔽期間,所有的中斷得不到處理,有可能造成數據丟失和系統崩潰的可能。這就要求在中斷屏蔽之后,當前的內核執行路徑應當盡快的執行完臨界區的代碼。
(2)操作方法
Linux內核中提供以下API來實現中斷屏蔽。
local_irq_disable(); //屏蔽中斷 local_irq_enable(); //打開中斷 local_irq_save(flags);//禁止中斷并保存當前cpu的中斷位信息2.2 原子操作
原子操作是指在執行過程中不會被別的代碼途徑所中斷的操作,分為整形原子操作和位原子操作。
2.3 信號量(進程級)
(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); //釋放信號量,喚醒等待進程2.4 互斥鎖(進程級)
(1)基本概念
互斥鎖是一種特殊的信號量。
(2)操作方法
Linux內核中提供以下API來操作互斥鎖。
//1)定義互斥鎖lock mutex_init(struct mutex* lock);//2)獲取互斥鎖mutex_lock(struct mutex *lock);//3)釋放互斥鎖mutex_unlock(struct mutex *lock);2.5 自旋鎖
(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.:信號量與自旋鎖的區別:
自旋鎖不會引起調用者睡眠,如果自旋鎖已經被別的執行單元保持,調用者就一直循環查看是否該自旋鎖的保持者已經釋放了鎖,"自旋"就是"在原地打轉"。而信號量則引起調用者睡眠,它把進程從運行隊列上拖出去,除非獲得鎖。
鑒于自旋鎖與信號量的上述特點,一般而言,自旋鎖適合于保持時間非常短的情況,它可以在任何上下文使用;信號量適合于保持時間較長的情況,會只能在進程上下文使用。如果被保護的共享資源只在進程上下文訪問,則可以以信號量來保護該共享資源,如果對共享資源的訪問時間非常短,自旋鎖也是好的選擇。但是,如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。
轉載于:https://www.cnblogs.com/linfeng-learning/p/9510459.html
總結
以上是生活随笔為你收集整理的Linux内核的并发与竞态、信号量、互斥锁、自旋锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux centos7 下 svn
- 下一篇: 继承Thread类使用多线程