自旋鎖和讀寫自旋鎖_第1頁
自旋鎖和讀寫自旋鎖_第2頁
自旋鎖和讀寫自旋鎖_第3頁
自旋鎖和讀寫自旋鎖_第4頁
自旋鎖和讀寫自旋鎖_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、自旋鎖分類: 瘋狂內(nèi)核之同步與互斥2010-05-18 19:59 395人閱讀 評論(0) 收藏 舉報加鎖(locking)是一種廣泛應(yīng)用的同步技術(shù)。當(dāng)內(nèi)核控制路徑必須訪問共享數(shù)據(jù)結(jié)構(gòu)或進(jìn)入臨界區(qū)時,就需要為自己獲取一把“鎖”。由鎖機(jī)制保護(hù)的資源非常類似于限制于房間內(nèi)的資源,當(dāng)某人進(jìn)入房間時,就把門鎖上。如果內(nèi)核控制路徑希望訪問資源,就試圖獲取鑰匙“打開門”。當(dāng)且僅當(dāng)資源空閑時,它才能成功。然后,只要它還想使用這個資源,門就依然鎖著。當(dāng)內(nèi)核控制路徑釋放了鎖時,門就打開,另一個內(nèi)核控制路徑就可以進(jìn)入房間。下圖顯示了鎖的使用。5個內(nèi)核控制路徑(P

2、0,PI,P2,P3和P4)試圖訪問兩個臨界區(qū)(C1和C2)。內(nèi)核控制路徑P0正在C1中,而P2和P4正等待進(jìn)人C1。同時,P1正在C2中,而P3正在等待進(jìn)入C2。注意P0和P1可以并行運行。臨界區(qū)C3的鎖現(xiàn)在打開著,因為沒有內(nèi)核控制路徑需要進(jìn)人C3。  Linux鎖的應(yīng)用之一在多處理器環(huán)境中,取名叫自旋鎖(spin lock)。如果內(nèi)核控制路徑發(fā)現(xiàn)自旋鎖“開著”,就獲取鎖并繼續(xù)自己的執(zhí)行。相反,如果內(nèi)核控制路徑發(fā)現(xiàn)鎖由運行在另一個CPU上的內(nèi)核控制路徑“鎖著”,就在周圍“旋轉(zhuǎn)”,反復(fù)執(zhí)行一條緊湊的循環(huán)指令,直到鎖被釋放。自旋鎖的循環(huán)指令表示“忙等”。即使等待的內(nèi)核控制路徑

3、無事可做(除了浪費時間),它也在CPU上保持運行。不過,自旋鎖通常非常方便,因為很多內(nèi)核資源只鎖1毫秒的時間片段;所以說,等待自旋鎖的釋放不會消耗太多CPU的時間。一般來說,由自旋鎖所保護(hù)的每個臨界區(qū)都是禁止內(nèi)核搶占的。在單處理器系統(tǒng)上,這種鎖本身并不起鎖的作用,自旋鎖技術(shù)僅僅是用來禁止或啟用內(nèi)核搶占。請注意,在自旋鎖忙等期間,因為并沒有進(jìn)入臨界區(qū),所以內(nèi)核搶占還是有效的,因此,等待自旋鎖釋放的進(jìn)程有可能被更高優(yōu)先級的所取代。這種設(shè)計是合理的,因為不能因為占用CPU太久而使系統(tǒng)死鎖。 在Linux中,每個自旋鎖都用spinlock_t結(jié)構(gòu)表示:typedef struct  

4、;   raw_spinlock_t raw_lock;#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)    unsigned int break_lock;#endif#ifdef CONFIG_DEBUG_SPINLOCK    unsigned int magic, owner_cpu;    void *owner;#endif#ifdef CONFIG_DEBUG_LOCK_ALLOC  

5、;  struct lockdep_map dep_map;#endif spinlock_t;typedef struct     volatile unsigned int slock; raw_spinlock_t;其中包含兩個重要的字段意義如下:slock:該字段表示自旋鎖的狀態(tài):值為1表示“未加鎖”狀態(tài),而任何負(fù)數(shù)和0都表示“加鎖”狀態(tài)。break_lock:表示進(jìn)程正在忙等自旋鎖(只在內(nèi)核支持SMP和內(nèi)核搶占的情況下使用該標(biāo)志)。內(nèi)核提供六個宏用于初始化、測試及設(shè)置自旋鎖。所有這些宏都是基于原子操作的,這樣可以保證即使有多個運行在不同CPU上

6、的進(jìn)程試圖同時修改自旋鎖,自旋鎖也能夠被正確地更新。 1、spin_lock_init 初始化自旋鎖,并把自旋鎖的lock->raw_lock置為1(未鎖)# define spin_lock_init(lock)                    /do                  

7、0;              /    static struct lock_class_key _key;            /                         &#

8、160;      /    _spin_lock_init(lock), #lock, &_key);        / while (0)void _spin_lock_init(spinlock_t *lock, const char *name,              struct lock_class_key *key)#ifdef C

9、ONFIG_DEBUG_LOCK_ALLOC    /*     * Make sure we are not reinitializing a held lock:     */    debug_check_no_locks_freed(void *)lock, sizeof(*lock);    lockdep_init_map(&lock->dep_map, name, key, 0);#endif

10、60;   lock->raw_lock = (raw_spinlock_t)_RAW_SPIN_LOCK_UNLOCKED;     lock->magic = SPINLOCK_MAGIC;    lock->owner = SPINLOCK_OWNER_INIT;    lock->owner_cpu = -1;#define _RAW_SPIN_LOCK_UNLOCKED    1  #define SP

11、INLOCK_MAGIC        0xdead4ead#define SPINLOCK_OWNER_INIT    (void *)-1L)2、spin_unlock 把自旋鎖置為1(未鎖)#if defined(CONFIG_DEBUG_SPINLOCK) | defined(CONFIG_PREEMPT) | /    !defined(CONFIG_SMP)# define spin_unlock(lock)     

12、60;  _spin_unlock(lock)#else /我們還是重點關(guān)注后面的吧# define spin_unlock(lock)        _raw_spin_unlock(&(lock)->raw_lock)#endif void _lockfunc _spin_unlock(spinlock_t *lock)    spin_release(&lock->dep_map, 1, _RET_IP_);    _ra

13、w_spin_unlock(lock);    preempt_enable();# define _raw_spin_unlock(lock)        _raw_spin_unlock(&(lock)->raw_lock)static inline void _raw_spin_unlock(raw_spinlock_t *lock)    _asm_ _volatile_(        _r

14、aw_spin_unlock_string    );#define _raw_spin_unlock_string /    "movb $1,%0" /        :"+m" (lock->slock) : : "memory"spin_unlock宏釋放以前獲得的自旋鎖,上面的代碼本質(zhì)上執(zhí)行下列匯編語言指令:movb $1, slp->slock并在隨后調(diào)用preempt_enable()(如果

15、不支持內(nèi)核搶占,preempt_enable()什么都做)。注意,因為現(xiàn)在的80x86微處理器總是原子地執(zhí)行內(nèi)存中的只寫訪問,所以不用lock字節(jié)。3、spin_unlock_wait 等待,直到自旋鎖變?yōu)?(未鎖)#define spin_unlock_wait(lock)    _raw_spin_unlock_wait(&(lock)->raw_lock)#define _raw_spin_unlock_wait(lock) /    do while (_raw_spin_is_locked(lock) cpu_

16、relax(); while (0)#define _raw_spin_is_locked(x) /        (*(volatile signed char *)(&(x)->slock) <= 0) /如果大于0則為真,表示未鎖,則跳出while循環(huán)#define cpu_relax()    rep_nop() /在循環(huán)中執(zhí)行一條空指令:static inline void rep_nop(void)    _asm_ _volatile_(&q

17、uot;rep;nop": : :"memory");4、spin_is_locked( ) 如果自旋鎖被置為1(未鎖),返回0;否則,返回1#define spin_is_locked(lock)    _raw_spin_is_locked(&(lock)->raw_lock)#define _raw_spin_is_locked(x) /        (*(volatile signed char *)(&(x)->slock) <=

18、 0)5、spin_trylock( ) 把自旋鎖置為0(鎖上),如果原來鎖的值是1,則返回1;否則,返回0#define spin_trylock(lock)        _cond_lock(_spin_trylock(lock)int _lockfunc _spin_trylock(spinlock_t *lock)    preempt_disable();    if (_raw_spin_trylock(lock)     

19、0;   spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);        return 1;            preempt_enable();    return 0; 6、spin_lock 加鎖:循環(huán),直到自旋鎖變?yōu)?(未鎖),然后,把自旋鎖置為0(鎖上) spin_lock是最重要的一個宏。首先,我

20、們看到在include/spinlock.h頭文件里,有:#if defined(CONFIG_SMP) | defined(CONFIG_DEBUG_SPINLOCK)# include <linux/spinlock_api_smp.h>  /多處理器情況#else# include <linux/spinlock_api_up.h>   /單處理器情況#endif#define spin_lock(lock)            _spin_l

21、ock(lock)#ifdef _LINUX_SPINLOCK_API_UP_H   #define _spin_lock(lock)            _LOCK(lock)   /單處理器情況 #else  注意,該代碼上邊有一句#if !defined(CONFIG_PREEMPT) | !defined(CONFIG_SMP) | /    defined(CONFIG_DE

22、BUG_LOCK_ALLOC)別去管它,因為上邊的英文注釋寫得很清楚了,這句代碼的意思是即使沒有定義內(nèi)核搶占或SMP,或者是自旋鎖調(diào)試,只要lockdep激活了,也就是我們在剛才spinlock_t定義的那里看到的#ifdef CONFIG_DEBUG_LOCK_ALLOC,都會假設(shè)在整個鎖的調(diào)試期間保持關(guān)中斷。這句#if就是這個意思,千萬別理解成沒有定義內(nèi)核搶占、SMP或自旋鎖調(diào)試了,切記切記。/* * If lockdep is enabled then we use the non-preemption spin-ops * even on CONFIG_PREEMP

23、T, because lockdep assumes that interrupts are * not re-enabled during lock-acquire (which the preempt-spin-ops do): */多處理器情況,并且允許內(nèi)核搶占: void _lockfunc _spin_lock(spinlock_t *lock)    /禁止搶占    preempt_disable();    /這個函數(shù)在沒有定義自旋鎖調(diào)試的時候是空函數(shù),我們不

24、去管它    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);    /相當(dāng)于_raw_spin_lock(lock)    LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);     在沒有定義自旋鎖調(diào)試的時候,LOCK_CONTENDED宏定義如下#define LOCK_CONTENDED(_lock, try, lock) /

25、60;   lock(_lock)我們看到其實就是調(diào)用_raw_spin_lock宏(位于include/linux/spinlock.h):# define _raw_spin_lock(lock)        _raw_spin_lock(&(lock)->raw_lock)于是,定位到include/asm-i386/Spinlock.hstatic inline void _raw_spin_lock(raw_spinlock_t *lock)    asm(_

26、raw_spin_lock_string : "+m" (lock->slock) : : "memory");展開:#define _raw_spin_lock_string /    "/n1:/t" /    /原子減,如果不為負(fù),跳轉(zhuǎn)到3f,3f后面沒有任何指令,即為退出    LOCK_PREFIX " ; decb %0/n/t" /      &#

27、160; "jns 3f/n" /    "2:/t" /    /重復(fù)執(zhí)行nop,nop是x86的小延遲函數(shù),執(zhí)行空操作    "rep;nop/n/t" /    /比較0與lock->slock的值,如果lock->slock不大于0,跳轉(zhuǎn)到標(biāo)號2,即繼續(xù)重復(fù)執(zhí)行nop    "cmpb $0,%0/n/t" /   

28、"jle 2b/n/t" /    /如果lock->slock大于0,跳轉(zhuǎn)到標(biāo)號1,重新判斷鎖的slock成員    "jmp 1b/n" /    "3:/n/t"在上面的函數(shù)中,大家可能對"jmp 1b/n“比較難以理解。在我們一般的觀念里,獲得一個鎖,將其值減1;釋放鎖時將其值加1;實際上在自旋鎖的實現(xiàn)中l(wèi)ock->slock只有兩個可能值,一個是0. 一個是1。釋放鎖的時候并不是將lock->slock加1,

29、而是將其賦為1。請看在前面的自旋鎖釋放代碼spin_unlock中的詳細(xì)分析。 讀寫自旋鎖分類: 瘋狂內(nèi)核之同步與互斥2010-05-18 20:24 337人閱讀 評論(0) 收藏 舉報讀/寫自旋鎖同樣是在保護(hù)SMP體系下的共享數(shù)據(jù)結(jié)構(gòu)而引入的,它的引入是為了增加內(nèi)核的并發(fā)能力。只要內(nèi)核控制路徑?jīng)]有對數(shù)據(jù)結(jié)構(gòu)進(jìn)行修改,讀/寫自旋鎖就允許多個內(nèi)核控制路徑同時讀同一數(shù)據(jù)結(jié)構(gòu)。如果一個內(nèi)核控制路徑想對這個結(jié)構(gòu)進(jìn)行寫操作,那么它必須首先獲取讀/寫鎖的寫鎖,寫鎖授權(quán)獨占訪問這個資源。這樣設(shè)計的目的,即允許對數(shù)據(jù)結(jié)構(gòu)并發(fā)讀可以提高系統(tǒng)性能。下圖

30、顯示有兩個受讀/寫鎖保護(hù)的臨界區(qū)(CI和C2)。內(nèi)核控制路徑R0和R1正在同時讀取C1中的數(shù)據(jù)結(jié)構(gòu),而W0正等待獲取寫鎖。內(nèi)核控制路徑W1正對C2中的數(shù)據(jù)結(jié)構(gòu)進(jìn)行寫操作,而R2和W2分別等待獲取讀鎖和寫鎖。 每個讀/寫自旋鎖都是一個rwlock_t結(jié)構(gòu):typedef struct     raw_rwlock_t raw_lock;#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)    unsigned int break_lock;#endif#ifde

31、f CONFIG_DEBUG_SPINLOCK    unsigned int magic, owner_cpu;    void *owner;#endif#ifdef CONFIG_DEBUG_LOCK_ALLOC    struct lockdep_map dep_map;#endif rwlock_t;typedef struct     volatile unsigned int lock; raw_rwlock_t;其lock字段是一個32位的字段,分為兩個不同的

32、部分:(1)24位計數(shù)器,表示對受保護(hù)的數(shù)據(jù)結(jié)構(gòu)并發(fā)地進(jìn)行讀操作的內(nèi)核控制路徑的數(shù)目。這個計數(shù)器的二進(jìn)制補碼存放在這個字段的023位。(2)“未鎖”標(biāo)志字段,當(dāng)沒有內(nèi)核控制路徑在讀或?qū)憰r設(shè)置該位,否則清0。這個“未鎖”標(biāo)志存放在lock字段的第24位。注意,如果自旋鎖為空(設(shè)置了“未鎖”標(biāo)志且無讀者),那么lock字段的值為0x01000000;如果寫者已經(jīng)獲得自旋鎖(“未鎖”標(biāo)志清0且無讀者),那么lock字段的值為0x00000000;如果一個、兩個或多個進(jìn)程因為讀獲取了自旋鎖,那么,lock字段的值為Ox00ffffff,Ox00fffffe等(“未鎖”標(biāo)志清0表示寫鎖定,不允許寫該數(shù)據(jù)

33、結(jié)構(gòu)的進(jìn)程,讀者個數(shù)的二進(jìn)制補碼在023位上;如果全為0,則表示有一個寫進(jìn)程在操作此數(shù)據(jù)結(jié)構(gòu))。與spinlock_t結(jié)構(gòu)一樣,rwlock_t結(jié)構(gòu)也包括break_lock字段。rwlock_init宏把讀/寫自旋鎖的lock字段初始化為0x01000000(“未鎖”),把break_lock初始化為0,算法類似spin_lock_init。進(jìn)程獲得讀寫自旋鎖的方式不僅有是否設(shè)置了內(nèi)核搶占選項而不同外,還跟讀或?qū)懙牟僮飨嚓P(guān)。前者的算法跟自旋鎖機(jī)制幾乎一樣,下面我們就重點討論后者: 1 為讀獲取和釋放一個鎖read_lock宏,作用于讀/寫自旋鎖的地址*lock,與上一篇博文所描述的

34、spin_lock宏非常相似。如果編譯內(nèi)核時選擇了內(nèi)核搶占選項,read_lock宏執(zhí)行與spin_lock()非常相似的操作,只有一點不同:該宏執(zhí)行_raw_read_trylock( )函數(shù)以在第2步有效地獲取讀/寫自旋鎖。void _lockfunc _read_lock(rwlock_t *lock)    preempt_disable();    rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);    LOCK_CONTEN

35、DED(lock, _raw_read_trylock, _raw_read_lock);在沒有定義調(diào)試自旋鎖操作時rwlock_acquire_read為空函數(shù),我們不去管它。所以_read_lock的實務(wù)函數(shù)是_raw_read_trylock:# define _raw_read_trylock(rwlock)    _raw_read_trylock(&(rwlock)->raw_lock)static inline int _raw_read_trylock(raw_rwlock_t *lock)    ato

36、mic_t *count = (atomic_t *)lock;    atomic_dec(count);    if (atomic_read(count) >= 0)        return 1;    atomic_inc(count);    return 0;讀/寫鎖計數(shù)器lock字段是通過原子操作來訪問的。注意,盡管如此,但整個函數(shù)對計數(shù)器的操作并不是原子性的,利用原子操作主要目的是禁止內(nèi)核搶

37、占。例如,在用if語句完成對計數(shù)器值的測試之后并返回1之前,計數(shù)器的值可能發(fā)生變化。不過,函數(shù)能夠正常工作:實際上,只有在遞減之前計數(shù)器的值不為0或負(fù)數(shù)的情況下,函數(shù)才返回1,因為計數(shù)器等于0x01000000表示沒有任何進(jìn)程占用鎖,等于Ox00ffffff表示有一個讀者,等于0x00000000表示有一個寫者(因為只可能有一個寫者)。如果編譯內(nèi)核時沒有選擇內(nèi)核搶占選項,read_lock宏產(chǎn)生下面的匯編語言代碼:        movl $rwlp->lock,%eax    

38、    lock; subl $1,(%eax)        jns 1f        call _ _read_lock_failed    1:這里,_read_lock_failed()是下列匯編語言函數(shù):    _ _read_lock_failed:        lock; incl

39、 (%eax)    1:  pause        cmpl $1,(%eax)        js 1b        lock; decl (%eax)        js _ _read_lock_failed     

40、   retread_lock宏原子地把自旋鎖的值減1,由此增加讀者的個數(shù)。如果遞減操作產(chǎn)生一個非負(fù)值,就獲得自旋鎖;否則就算作失敗。我們看到lock字段的值由Ox00ffffff到0x00000000要減多少次才可能出現(xiàn)負(fù)值,所以幾乎很難出現(xiàn)調(diào)用_read_lock_failed()函數(shù)的情況。該函數(shù)原子地增加lock字段以取消由read_lock宏執(zhí)行的遞減操作,然后循環(huán),直到lock字段變?yōu)檎龜?shù)(大于或等于0)。接下來,_read_lock_failed()又試圖獲取自旋鎖(正好在cmpl指令之后,另一個內(nèi)核控制路徑可能為寫獲取自旋鎖)。釋放讀自旋鎖是相當(dāng)簡單的,因為

41、read_unlock宏只需要使用匯編語言指令簡單地增加lock字段的計數(shù)器:    lock; incl rwlp->lock以減少讀者的計數(shù),然后調(diào)用preempt_enable()重新啟用內(nèi)核搶占。2 為寫獲取或釋放一個鎖write_lock宏實現(xiàn)的方式與spin_lock()和read_lock()相似。例如,如果支持內(nèi)核搶占,則該函數(shù)禁用內(nèi)核搶占并通過調(diào)用_raw_write_trylock()立即獲得鎖。如果該函數(shù)返回0,說明鎖已經(jīng)被占用,因此,該宏像前面博文描述的那樣重新啟用內(nèi)核搶占并開始忙等待循環(huán)。#define write_lock(loc

42、k)        _write_lock(lock)void _lockfunc _write_lock(rwlock_t *lock)    preempt_disable();    rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);    LOCK_CONTENDED(lock, _raw_write_trylock, _raw_write_lock);_raw_write_try

43、lock()函數(shù)描述如下:    int _raw_write_trylock(rwlock_t *lock)            atomic_t *count = (atomic_t *)lock->lock;        if (atomic_sub_and_test(0x01000000, count)      

44、60;     return 1;        atomic_add(0x01000000, count);        return 0;    static _inline_ int atomic_sub_and_test(int i, atomic_t *v)    unsigned char c;    _asm_

45、_volatile_(        LOCK "subl %2,%0; sete %1"        :"=m" (v->counter), "=qm" (c)        :"ir" (i), "m" (v->counter) : "memory");  &#

46、160; return c;函數(shù)_raw_write_trylock()調(diào)用tomic_sub_and_test(0x01000000, count)從讀/寫自旋鎖lock->lock的值中減去0x01000000,從而清除未上鎖標(biāo)志(看見沒有?正好是第24位)。如果減操作產(chǎn)生0值(沒有讀者),則獲取鎖并返回1;否則,函數(shù)原子地在自旋鎖的值上加0x01000000,以取消減操作。釋放寫鎖同樣非常簡單,因為write_unlock宏只需使用匯編語言指令:lock; addl $0x01000000,rwlp把lock字段中的“未鎖”標(biāo)識置位,然后再調(diào)用preempt_enable()。順序

47、鎖 分類: 瘋狂內(nèi)核之同步與互斥 2010-05-19 14:14 217人閱讀 評論(0) 收藏 舉報 當(dāng)使用讀/寫自旋鎖時,內(nèi)核控制路徑發(fā)出的執(zhí)行read_lock或write_lock操作的請求具有相同的優(yōu)先權(quán):讀者必須等待,直到寫操作完成。同樣地,寫者也必須等待,直到讀操作完成。Linux 2.6中引入了順序鎖(seqlock),它與讀/寫自旋鎖非常相似,只是它為寫者賦予了較高的優(yōu)先級:事實上,即使在讀者正在讀的時候也允許寫者繼續(xù)運行。這種策略的好處是寫者永遠(yuǎn)不會等待讀(除非另外一個寫者正在寫),缺點是有些時候讀者不得不反復(fù)讀多次相同的數(shù)據(jù)直到它獲得有效的結(jié)果。每個順序鎖都是包括兩個字

48、段的seqlock_t結(jié)構(gòu):typedef struct     unsigned sequence;    spinlock_t lock; seqlock_t;一個類型為spinlock_t的lock字段和一個整型的sequence字段,第二個字段sequence是一個順序計數(shù)器。每個讀者都必須在讀數(shù)據(jù)前后兩次讀順序計數(shù)器,并檢查兩次讀到的值是否相同,如果不相同,說明新的寫者已經(jīng)開始寫并增加了順序計數(shù)器,因此暗示讀者剛讀到的數(shù)據(jù)是無效的。通過把SEQLOCK_UNLOCKED賦給變量seqlock_t或執(zhí)行seqlock_init

49、宏,把seqlock_t變量初始化為“未上鎖”,并把sequence設(shè)為0:#define _SEQLOCK_UNLOCKED(lockname) /          0, _SPIN_LOCK_UNLOCKED(lockname) #define SEQLOCK_UNLOCKED /         _SEQLOCK_UNLOCKED(old_style_seqlock_init)# define _SPIN_LOCK_UNLOCKED(locknam

50、e) /    (spinlock_t)        .raw_lock = _RAW_SPIN_LOCK_UNLOCKED,    /                SPIN_DEP_MAP_INIT(lockname) #define _RAW_SPIN_LOCK_UNLOCKED    1 寫者通過調(diào)用write_seq

51、lock()和write_sequnlock()獲取和釋放順序鎖。write_seqlock()函數(shù)獲取seqlock_t數(shù)據(jù)結(jié)構(gòu)中的自旋鎖,然后使順序計數(shù)器sequence加1;write_sequnlock()函數(shù)再次增加順序計數(shù)器sequence,然后釋放自旋鎖。這樣可以保證寫者在整個寫的過程中,計數(shù)器sequence的值是奇數(shù),并且當(dāng)沒有寫者在改變數(shù)據(jù)的時候,計數(shù)器的值是偶數(shù)。讀者進(jìn)程執(zhí)行下面的臨界區(qū)代碼:    unsigned int seq;    do      

52、60;  seq = read_seqbegin(&seqlock);        /* . CRITICAL REGION . */    while (read_seqretry(&seqlock, seq);read_seqbegin()返回順序鎖的當(dāng)前順序號;如果局部變量seq的值是奇數(shù)(寫者在read_seqbegin()函數(shù)被調(diào)用后,正更新數(shù)據(jù)結(jié)構(gòu)),或seq的值與順序鎖的順序計數(shù)器的當(dāng)前值不匹配(當(dāng)讀者正執(zhí)行臨界區(qū)代碼時,寫者開始工作),read_seq

53、retry()就返回1:static _always_inline int read_seqretry(const seqlock_t *sl, unsigned iv)    smp_rmb();    return (iv & 1) | (sl->sequence iv);注意在順序鎖機(jī)制里,讀者可能反復(fù)讀多次相同的數(shù)據(jù)直到它獲得有效的結(jié)果(read_seqretry返回0)。另外,當(dāng)讀者進(jìn)入臨界區(qū)時,不必禁用內(nèi)核搶占;另一方面,由寫者獲取自旋鎖,所以它進(jìn)入臨界區(qū)時自動禁用內(nèi)核搶占。并不是每一種資源都可以使用順序鎖來

54、保護(hù)。一般來說,必須在滿足下述條件時才能使用順序鎖:(1)被保護(hù)的數(shù)據(jù)結(jié)構(gòu)不包括被寫者修改和被讀者間接引用 的指針(否則,寫者可能在讀者的眼皮子底下就修改指針)。(2)讀者的臨界區(qū)代碼沒有副作用(否則,多個讀者的操作會與單獨的讀操作有不同的結(jié)果)。此外,讀者的臨界區(qū)代碼應(yīng)該簡短,而且寫者應(yīng)該不常獲取順序鎖,否則,反復(fù)的讀訪問會引起嚴(yán)重的開銷。在Linux 2.6中,使用順序鎖主要是保護(hù)一些與系統(tǒng)時間處理相關(guān)的數(shù)據(jù)結(jié)構(gòu)。RCU機(jī)制 分類: 瘋狂內(nèi)核之同步與互斥 2010-05-19 14:46 338人閱讀 評論(0) 收藏 舉報 讀-拷貝-更新(RCU)是為了保護(hù)在多數(shù)情況下被多個CPU讀的數(shù)

55、據(jù)結(jié)構(gòu)而設(shè)計的另一種同步技術(shù)。RCU允許多個讀者和寫者并發(fā)執(zhí)行(相對于只允許一個寫者執(zhí)行的順序鎖有了改進(jìn))。而且,RCU是不使用鎖的,就是說,它不使用被所有CPU共享的鎖或計數(shù)器,在這一點上與讀/寫自旋鎖和順序鎖(由于高速緩存行竊用和失效而有很高的開銷)相比RCU具有更大的優(yōu)勢。RCU是如何不使用共享數(shù)據(jù)結(jié)構(gòu)而令人驚訝地實現(xiàn)多個CPU同步呢?其關(guān)鍵的思想包包括限制RCP的范圍的兩個約束條件,這個是重中之重,如下所述:1. RCU只保護(hù)被動態(tài)分配并通過指針引用的數(shù)據(jù)結(jié)構(gòu)。2. 在被RCU保護(hù)的臨界區(qū)中,任何內(nèi)核控制路徑都不能睡眠。當(dāng)內(nèi)核控制路徑要讀取被RCU保護(hù)的數(shù)據(jù)結(jié)構(gòu)時,執(zhí)行宏rcu_rea

56、d_lock(),它僅僅是preempt_disable():#define rcu_read_lock() /    do /        preempt_disable(); /        _acquire(RCU); /      while(0)接下來,讀者間接引用該數(shù)據(jù)結(jié)構(gòu)指針?biāo)鶎?yīng)的內(nèi)存單元并開始讀這個數(shù)據(jù)結(jié)構(gòu)。正如在前面所強(qiáng)調(diào)的,讀者在完成對數(shù)據(jù)結(jié)構(gòu)的讀操作之前,是不能睡眠的。最后,用等同于preempt_enable()的宏rcu_read_unlock()標(biāo)記臨界區(qū)的結(jié)束:#define rcu_read_unlock() /    do /        _release(RCU); /        preempt_enable(); /  

溫馨提示

  • 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)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論