spinlock與linux內(nèi)核調(diào)度的關(guān)系
作者:劉洪濤,華清遠(yuǎn)見嵌入式培訓(xùn)中心高級講師,ARM公司授權(quán)ATC講師。
關(guān)于自旋鎖用法介紹的文章,已經(jīng)有很多,但有些細(xì)節(jié)的地方點的還不夠透。我這里就把我個人認(rèn)為大家容易有疑問的地方拿出來討論一下。
一、自旋鎖(spinlock)簡介
自旋鎖在同一時刻只能被最多一個內(nèi)核任務(wù)持有,所以一個時刻只有一個線程允許存在于臨界區(qū)中。這點可以應(yīng)用在多處理機(jī)器、或運(yùn)行在單處理器上的搶占式內(nèi)核中需要的鎖定服務(wù)。
二、信號量簡介
這里也介紹下信號量的概念,因為它的用法和自旋鎖有相似的地方。
Linux中的信號量是一種睡眠鎖。如果有一個任務(wù)試圖獲得一個已被持有的信號量時,信號量會將其推入等待隊列,然后讓其睡眠。這時處理器獲得自由去執(zhí)行其它代碼。當(dāng)持有信號量的進(jìn)程將信號量釋放后,在等待隊列中的一個任務(wù)將被喚醒,從而便可以獲得這個信號量。
三、自旋鎖和信號量對比
在很多地方自旋鎖和信號量可以選擇任何一個使用,但也有一些地方只能選擇某一種。下面對比一些兩者的用法。
表1-1自旋鎖和信號量對比
四、自旋鎖與linux內(nèi)核進(jìn)程調(diào)度關(guān)系
我們討論下表1-1中的第3種情況(其它幾種情況比較好理解),如果臨界區(qū)可能包含引起睡眠的代碼則不能使用自旋鎖,否則可能引起死鎖。
那么為什么信號量保護(hù)的代碼可以睡眠而自旋鎖就不能呢?
先看下自旋鎖的實現(xiàn)方法吧,自旋鎖的基本形式如下:
spin_lock(&mr_lock);
//臨界區(qū)
spin_unlock(&mr_lock);
跟蹤一下spin_lock(&mr_lock)的實現(xiàn)
#define spin_lock(lock) _spin_lock(lock)
#define _spin_lock(lock) __LOCK(lock)
#define __LOCK(lock)
do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)
注意到“preempt_disable()”,這個調(diào)用的功能是“關(guān)搶占”(在spin_unlock中會重新開啟搶占功能)。從中可以看出,使用自旋鎖保護(hù)的區(qū)域是工作在非搶占的狀態(tài);即使獲取不到鎖,在“自旋”狀態(tài)也是禁止搶占的。了解到這,我想咱們應(yīng)該能夠理解為何自旋鎖保護(hù)的代碼不能睡眠了。試想一下,如果在自旋鎖保護(hù)的代碼中間睡眠,此時發(fā)生進(jìn)程調(diào)度,則可能另外一個進(jìn)程會再次調(diào)用spinlock保護(hù)的這段代碼。而我們現(xiàn)在知道了即使在獲取不到鎖的“自旋”狀態(tài),也是禁止搶占的,而“自旋”又是動態(tài)的,不會再睡眠了,也就是說在這個處理器上不會再有進(jìn)程調(diào)度發(fā)生了,那么死鎖自然就發(fā)生了。
咱們可以總結(jié)下自旋鎖的特點:
● 單處理器非搶占內(nèi)核下:自旋鎖會在編譯時被忽略;
● 單處理器搶占內(nèi)核下:自旋鎖僅僅當(dāng)作一個設(shè)置內(nèi)核搶占的開關(guān);
● 多處理器下:此時才能完全發(fā)揮出自旋鎖的作用,自旋鎖在內(nèi)核中主要用來防止多處理器中并發(fā)訪問臨界區(qū),防止內(nèi)核搶占造成的競爭。
五、linux搶占發(fā)生的時間
最后在了解下linux搶占發(fā)生的時間,搶占分為用戶搶占和內(nèi)核搶占。
用戶搶占在以下情況下產(chǎn)生:
● 從系統(tǒng)調(diào)用返回用戶空間
● 從中斷處理程序返回用戶空間
內(nèi)核搶占會發(fā)生在:
● 當(dāng)從中斷處理程序返回內(nèi)核空間的時候,且當(dāng)時內(nèi)核具有可搶占性;
● 當(dāng)內(nèi)核代碼再一次具有可搶占性的時候。(如:spin_unlock時)
● 如果內(nèi)核中的任務(wù)顯式的調(diào)用schedule()
● 如果內(nèi)核中的任務(wù)阻塞。
基本的進(jìn)程調(diào)度就是發(fā)生在時鐘中斷后,并且發(fā)現(xiàn)進(jìn)程的時間片已經(jīng)使用完了,則發(fā)生進(jìn)程搶占。通常我們會利用中斷處理程序返回內(nèi)核空間的時候可以進(jìn)行內(nèi)核搶占這個特性來提高一些I/O操作的實時性,如:當(dāng)I/O事件發(fā)生的是時候,對應(yīng)的中斷處理程序被激活,當(dāng)它發(fā)現(xiàn)有進(jìn)程在等待這個I/O事件的時候,它會激活等待進(jìn)程,并且設(shè)置當(dāng)前正在執(zhí)行進(jìn)程的need_resched標(biāo)志,這樣在中斷處理程序返回的時候,調(diào)度程序被激活,原來在等待I/O事件的進(jìn)程(很可能)獲得執(zhí)行權(quán),從而保證了對I/O事件的相對快速響應(yīng)(毫秒級)??梢钥闯觯贗/O事件發(fā)生的時候,I/O事件的處理進(jìn)程會搶占當(dāng)前進(jìn)程,系統(tǒng)的響應(yīng)速度與調(diào)度時間片的長度無關(guān)。
“本文由華清遠(yuǎn)見http://www.embedu.org/index.htm提供”
華清遠(yuǎn)見