信號量、互斥體和自旋鎖_第1頁
信號量、互斥體和自旋鎖_第2頁
信號量、互斥體和自旋鎖_第3頁
信號量、互斥體和自旋鎖_第4頁
信號量、互斥體和自旋鎖_第5頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、信號量、互斥體和自旋鎖分類:linux系統(tǒng)linux驅(qū)動2014-02-2011:0683人閱讀評論(0收藏舉報(bào)一、信號量信號量又稱為信號燈,它是用來協(xié)調(diào)不同進(jìn)程間的數(shù)據(jù)對象的,而最主要的應(yīng)用是共享內(nèi)存方式的進(jìn)程間通信。本質(zhì)上,信號量是一個計(jì)數(shù)器,它用來記錄對某個資源(如共享內(nèi)存的存取狀況。一般說來,為了獲得共享資源,進(jìn)程需要執(zhí)行下列操作:(1測試控制該資源的信號量。(2若此信號量的值為正,則允許進(jìn)行使用該資源。進(jìn)程將信號量減1。(3若此信號量為0,則該資源目前不可用,進(jìn)程進(jìn)入睡眠狀態(tài),直至信號量值大于0,進(jìn)程被喚醒,轉(zhuǎn)入步驟(1。(4當(dāng)進(jìn)程不再使用一個信號量控制的資源時(shí),信號量值加1。如果此

2、時(shí)有進(jìn)程正在睡眠等待此信號量,則喚醒此進(jìn)程。維護(hù)信號量狀態(tài)的是Linux內(nèi)核操作系統(tǒng)而不是用戶進(jìn)程。我們可以從頭文件/usr/src/linux/include/linux/sem.h中看到內(nèi)核用來維護(hù)信號量狀態(tài)的各個結(jié)構(gòu)的定義。信號量是一個數(shù)據(jù)集合,用戶可以單獨(dú)使用這一集合的每個元素。要調(diào)用的第一個函數(shù)是semget,用以獲得一個信號量ID。Linux2.6.26下定義的信號量結(jié)構(gòu)體:struct semaphorespinlock_t lock;unsigned int count;struct list_head wait_list;從以上信號量的定義中,可以看到信號量底層使用到了spi

3、n lock的鎖定機(jī)制,這個spinlock 主要用來確保對count成員的原子性的操作(count-和測試(count0。1.信號量的P操作:(1.void down(struct semaphore*sem;(2.int down_interruptible(struct semaphore*sem;(3.int down_trylock(struct semaphore*sem;說明:(1中的函數(shù)根據(jù)2.6.26中的代碼注釋,這個函數(shù)已經(jīng)out了(Use of this function is deprecated,所以從實(shí)用角度,徹底忘了它吧。(2最常用,函數(shù)原型/*down_inte

4、rruptible-acquirethe semaphore unless interrupted*sem:the semaphore to beacquired*Attempts to acquire thesemaphore.If no more tasks are allowedto*acquire the semaphore,calling this function will put the task to sleep.*If the sleep is interruptedby a signal,this function will return-EINTR.*If the sem

5、aphore issuccessfully acquired,this function returns0.*/int down_interruptible(structsemaphore*semunsigned long flags;int result=0;spin_lock_irqsave(&sem-lock,flags;if(likely(sem-count0sem-count-;elseresult=_down_interruptible(sem;spin_unlock_irqrestore(&sem-lock,flags;return result;對此函數(shù)的理解:在保證原子操作的

6、前提下,先測試count是否大于0,如果是說明可以獲得信號量,這種情況下需要先將count-,以確保別的進(jìn)程能否獲得該信號量,然后函數(shù)返回,其調(diào)用者開始進(jìn)入臨界區(qū)。如果沒有獲得信號量,當(dāng)前進(jìn)程利用structsemaphore中wait_list加入等待隊(duì)列,開始睡眠。對于需要休眠的情況,在_down_interruptible(函數(shù)中,會構(gòu)造一個structsemaphore_waiter類型的變量(struct semaphore_waiter定義如下:struct semaphore_waiterstruct list_head list;struct task_struct*task;

7、int up;,將當(dāng)前進(jìn)程賦給task,并利用其list成員將該變量的節(jié)點(diǎn)加入到以sem中的wait_list為頭部的一個列表中,假設(shè)有多個進(jìn)程在sem上調(diào)用down_interruptible,則sem的wait_list 上形成的隊(duì)列如下圖:(注:將一個進(jìn)程阻塞,一般的經(jīng)過是先把進(jìn)程放到等待隊(duì)列中,接著改變進(jìn)程的狀態(tài),比如設(shè)為TASK_INTERRUPTIBLE,然后調(diào)用調(diào)度函數(shù)schedule(,后者將會把當(dāng)前進(jìn)程從cpu的運(yùn)行隊(duì)列中摘下(3試圖去獲得一個信號量,如果沒有獲得,函數(shù)立刻返回1而不會讓當(dāng)前進(jìn)程進(jìn)入睡眠狀態(tài)。2.信號量的V操作void up(struct semaphore*

8、sem;原型如下:/*up-release the semaphore*sem:the semaphore torelease*Release the semaphore.Unlike mutexes,up(may be called from any*context and even by taskswhich have never called down(.*/void up(struct semaphore*semunsigned long flags;spin_lock_irqsave(&sem-lock,flags;if(likely(list_empty(&sem-wait_lis

9、tsem-count+;else_up(sem;spin_unlock_irqrestore(&sem-lock,flags;如果沒有其他線程等待在目前即將釋放的信號量上,那么只需將count+即可。如果有其他線程正因?yàn)榈却撔盘柫慷?那么調(diào)用_up._up的定義:static noinline void_sched_up(struct semaphore*semstruct semaphore_waiter*waiter=list_first_entry(&sem-wait_list,struct semaphore_waiter,list;list_del(&waiter-list;w

10、aiter-up=1;wake_up_process(waiter-task;這個函數(shù)首先獲得sem所在的wait_list為頭部的鏈表的第一個有效節(jié)點(diǎn),然后從鏈表中將其刪除,然后喚醒該節(jié)點(diǎn)上睡眠的進(jìn)程。由此可見,對于sem上的每次down_interruptible調(diào)用,都會在sem的wait_list鏈表尾部加入一新的節(jié)點(diǎn)。對于sem上的每次up調(diào)用,都會刪除掉wait_list鏈表中的第一個有效節(jié)點(diǎn),并喚醒睡眠在該節(jié)點(diǎn)上的進(jìn)程。關(guān)于Linux環(huán)境下信號量其他API詳見LKD和ULD二、互斥體互斥體實(shí)現(xiàn)了“互相排斥”(mutualexclusion同步的簡單形式(所以名為互斥體(mutex

11、?;コ怏w禁止多個線程同時(shí)進(jìn)入受保護(hù)的代碼“臨界區(qū)”(criticalsection。因此,在任意時(shí)刻,只有一個線程被允許進(jìn)入這樣的代碼保護(hù)區(qū)。任何線程在進(jìn)入臨界區(qū)之前,必須獲取(acquire與此區(qū)域相關(guān)聯(lián)的互斥體的所有權(quán)。如果已有另一線程擁有了臨界區(qū)的互斥體,其他線程就不能再進(jìn)入其中。這些線程必須等待,直到當(dāng)前的屬主線程釋放(release該互斥體。什么時(shí)候需要使用互斥體呢?互斥體用于保護(hù)共享的易變代碼,也就是,全局或靜態(tài)數(shù)據(jù)。這樣的數(shù)據(jù)必須通過互斥體進(jìn)行保護(hù),以防止它們在多個線程同時(shí)訪問時(shí)損壞Linux2.6.26中mutex的定義:struct mutex/*1:unlocked,0:l

12、ocked,negative:locked,possible waiters*/atomic_t count;spinlock_t wait_lock;struct list_head wait_list;#ifdef CONFIG_DEBUG_MUTEXESstruct thread_info*owner;const char*name;void*magic;#endif#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map;#endif;對比前面的struct semaphore,structmutex除了增加了幾個作為debug用

13、途的成員變量外,和semaphore幾乎長得一樣。但是mutex的引入主要是為了提供互斥機(jī)制,以避免多個進(jìn)程同時(shí)在一個臨界區(qū)中運(yùn)行。如果靜態(tài)聲明一個count=1的semaphore變量,可以使用DECLARE_MUTEX(name, DECLARE_MUTEX(name實(shí)際上是定義一個semaphore,所以它的使用應(yīng)該對應(yīng)信號量的P,V函數(shù).如果要定義一個靜態(tài)mutex型變量,應(yīng)該使用DEFINE_MUTEX如果在程序運(yùn)行期要初始化一個mutex變量,可以使用mutex_init(mutex,mutex_init是個宏,在該宏定義的內(nèi)部,會調(diào)用_mutex_init函數(shù)。#define m

14、utex_init(mutexdostatic struct lock_class_key_key;_mutex_init(mutex,#mutex,&_key;while(0_mutex_init定義如下:/*mutex_init-initialize themutex*lock:the mutex to beinitialized*Initialize the mutex tounlocked state.*It is not allowed toinitialize an already locked mutex.*/void_mutex_init(struct mutex*lock,c

15、onst char*name,struct lock_class_key*key atomic_set(&lock-count,1;spin_lock_init(&lock-wait_lock;INIT_LIST_HEAD(&lock-wait_list;debug_mutex_init(lock,name,key;從_mutex_init的定義可以看出,在使用mutex_init宏來初始化一個mutex變量時(shí),應(yīng)該使用mutex的指針型。mutex上的P,V操作:voidmutex_lock(struct mutex*lock和void_schedmutex_unlock(struct mu

16、tex*lock從原理上講,mutex實(shí)際上是count=1情況下的semaphore,所以其PV操作應(yīng)該和semaphore是一樣的。但是在實(shí)際的Linux代碼上,出于性能優(yōu)化的角度,并非只是單純的重用down_interruptible和up的代碼。以ARM平臺的mutex_lock為例,實(shí)際上是將mutex_lock分成兩部分實(shí)現(xiàn):fastpath和slow path,主要是基于這樣一個事實(shí):在絕大多數(shù)情況下,試圖獲得互斥體的代碼總是可以成功獲得。所以Linux的代碼針對這一事實(shí)用ARMV6上的LDREX和STREX指令來實(shí)現(xiàn)fastpath以期獲得最佳的執(zhí)行性能。這里對于mutex 的

17、實(shí)現(xiàn)細(xì)節(jié),不再多說,如欲深入了解,參考APUE和ULD三、自旋鎖自旋鎖它是為為實(shí)現(xiàn)保護(hù)共享資源而提出一種鎖機(jī)制。其實(shí),自旋鎖與互斥鎖比較類似,它們都是為了解決對某項(xiàng)資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個保持者,也就說,在任何時(shí)刻最多只能有一個執(zhí)行單元獲得鎖。但是兩者在調(diào)度機(jī)制上略有不同。對于互斥鎖,如果資源已經(jīng)被占用,資源申請者只能進(jìn)入睡眠狀態(tài)。但是自旋鎖不會引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,自旋一詞就是因此而得名。自旋鎖一般原理跟互斥鎖一樣,一個執(zhí)行單元要想訪問被自旋鎖保護(hù)的共享資源,必須

18、先得到鎖,在訪問完共享資源后,必須釋放鎖。如果在獲取自旋鎖時(shí),沒有任何執(zhí)行單元保持該鎖,那么將立即得到鎖;如果在獲取自旋鎖時(shí)鎖已經(jīng)有保持者,那么獲取鎖操作將自旋在那里,直到該自旋鎖的保持者釋放了鎖。由此我們可以看出,自旋鎖是一種比較低級的保護(hù)數(shù)據(jù)結(jié)構(gòu)或代碼片段的原始方式,這種鎖可能存在兩個問題:死鎖和過多占用cpu資源。Linux內(nèi)核實(shí)現(xiàn)的自旋鎖是不可遞歸的。小心自加鎖。加鎖是對數(shù)據(jù)不是對代碼。自旋鎖適用情況自旋鎖比較適用于鎖使用者保持鎖時(shí)間比較短的情況。正是由于自旋鎖使用者一般保持鎖時(shí)間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠(yuǎn)高于互斥鎖。信號量和讀寫信號量適合于保持時(shí)間較長

19、的情況,它們會導(dǎo)致調(diào)用者睡眠,因此只能在進(jìn)程上下文使用,而自旋鎖適合于保持時(shí)間非常短的情況,它可以在任何上下文使用。如果被保護(hù)的共享資源只在進(jìn)程上下文訪問,使用信號量保護(hù)該共享資源非常合適,如果對共享資源的訪問時(shí)間非常短,自旋鎖也可以。但是如果被保護(hù)的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷,就必須使用自旋鎖。自旋鎖保持期間是搶占失效的,而信號量和讀寫信號量保持期間是可以被搶占的。自旋鎖只有在內(nèi)核可搶占或SMP(多處理器的情況下才真正需要,在單CPU且不可搶占的內(nèi)核下,自旋鎖的所有操作都是空操作。另外格外注意一點(diǎn):自旋鎖不能遞歸使用。關(guān)于自旋鎖的定義以及相應(yīng)的AP

20、I自旋鎖定義:linux/Spinlock.htypedef struct spinlockunion/聯(lián)合struct raw_spinlock rlock;#ifdef CONFIG_DEBUG_LOCK_ALLOC#define LOCK_PADSIZE(offsetof(structraw_spinlock,dep_mapstructu8_paddingLOCK_PADSIZE;struct lockdep_mapdep_map;#endif; spinlock_t; 定義和初始化 spinlock_t my_lock =SPIN_LOCK_UNLOCKED; void spin_lo

21、ck_init(spinlock_t *lock; 自旋鎖操作: /加鎖一個自旋鎖函數(shù) void spin_lock(spinlock_t *lock; 鎖 void spin_lock_irq(spinlock_t*lock; 指定的鎖 voidspin_lock_irqsave(spinlock_t *lock, unsigned long flags; 禁止本地中斷,并獲取指定的鎖 void spin_lock_bh(spinlock_t *lock 而仍然允許硬件中斷被服務(wù) /安全地避免死鎖, /保存本地中斷的狀態(tài), /禁止本地中斷獲取 /獲取指定的自旋 /釋放一個自旋鎖函數(shù) void

22、 spin_unlock(spinlock_t *lock; voidspin_unlock_irq(spinlock_t *lock; 激活本地中斷 /釋放指定的鎖 /釋放指定的鎖,并 voidspin_unlock_irqrestore(spinlock_t *lock, unsigned long flags; /釋放指定的鎖,并讓本 地中斷恢復(fù)到以前的狀態(tài) void spin_unlock_bh(spinlock_t*lock; spin_lock_bh /對應(yīng)于 /非阻塞鎖 int spin_trylock(spinlock_t *lock; 鎖已經(jīng)被爭用,該方法會立刻返回一個非0值

23、, /而不會自旋等待鎖被釋放,如 果成果獲得了這個鎖,那么就返回0. int spin_trylock_bh(spinlock_t*lock; /這些函數(shù)成功時(shí)返回非零( 獲得了鎖 , 否則 0. 沒有try版本來禁止中斷. /試圖獲得某個特定的自旋鎖,如果該 /其他 int spin_is_locked(spinlock_t*lock; /和 try_lock(差不多 四、信號量、互斥體和自旋鎖的區(qū)別 信號量/互斥體和自旋鎖的區(qū)別 信號量/互斥體允許進(jìn)程睡眠屬于睡眠鎖,自旋鎖則不允許調(diào)用者睡眠,而是讓其循環(huán)等待, 所以有以下區(qū)別應(yīng)用 1) 、信號量和讀寫信號量適合于保持時(shí)間較長的情況,它們會導(dǎo)致調(diào)用者睡眠,因而 自旋鎖適合于保持時(shí)間非常短的情況 2) 、自旋鎖可以用于中斷,不能用于進(jìn)程上下文(會引起死鎖。而信號量不允許使用在 中斷中,而可以用于進(jìn)程上下文 3) 、自旋鎖保持期間是搶占失效的,自

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論