<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > Linux內核的同步機制

Linux內核的同步機制

作者: 時(shí)間:2017-06-06 來(lái)源:網(wǎng)絡(luò ) 收藏

一、引言

在現代操作系統里,同一時(shí)間可能有多個(gè)內核執行流在執行,因此內核其實(shí)象多進(jìn)程多線(xiàn)程編程一樣也需要一些來(lái)同步各執行單元對共享數據的訪(fǎng)問(wèn)。尤其是在多處理器系統上,更需要一些來(lái)同步不同處理器上的執行單元對共享的數據的訪(fǎng)問(wèn)。

在主流的內核中包含了幾乎所有現代的操作系統具有的,這些同步機制包括:原子操作、信號量(semaphore)、讀寫(xiě)信號量(rw_semaphore)、spinlock、BKL(Big Kernel Lock)、rwlock、brlock(只包含在2.4內核中)、RCU(只包含在2.6內核中)和seqlock(只包含在2.6內核中)。

二、原子操作

所謂原子操作,就是該操作絕不會(huì )在執行完畢前被任何其他任務(wù)或事件打斷,也就說(shuō),它的最小的執行單位,不可能有比它更小的執行單位,因此這里的原子實(shí)際是使用了物理學(xué)里的物質(zhì)微粒的概念。

原子操作需要硬件的支持,因此是架構相關(guān)的,其API和原子類(lèi)型的定義都定義在內核源碼樹(shù)的include/asm/atomic.h文件中,它們都使用匯編語(yǔ)言實(shí)現,因為C語(yǔ)言并不能實(shí)現這樣的操作。

原子操作主要用于實(shí)現資源計數,很多引用計數(refcnt)就是通過(guò)原子操作實(shí)現的。原子類(lèi)型定義如下:

typedef struct { volatile int counter; } atomic_t;

volatile修飾字段告訴gcc不要對該類(lèi)型的數據做優(yōu)化處理,對它的訪(fǎng)問(wèn)都是對內存的訪(fǎng)問(wèn),而不是對寄存器的訪(fǎng)問(wèn)。

原子操作API包括:

atomic_read(atomic_t * v);

該函數對原子類(lèi)型的變量進(jìn)行原子讀操作,它返回原子類(lèi)型的變量v的值。

atomic_set(atomic_t * v, int i);

該函數設置原子類(lèi)型的變量v的值為i。

void atomic_add(int i, atomic_t *v);

該函數給原子類(lèi)型的變量v增加值i。

atomic_sub(int i, atomic_t *v);

該函數從原子類(lèi)型的變量v中減去i。

int atomic_sub_and_test(int i, atomic_t *v);

該函數從原子類(lèi)型的變量v中減去i,并判斷結果是否為0,如果為0,返回真,否則返回假。

void atomic_inc(atomic_t *v);

該函數對原子類(lèi)型變量v原子地增加1。

void atomic_dec(atomic_t *v);

該函數對原子類(lèi)型的變量v原子地減1。

int atomic_dec_and_test(atomic_t *v);

該函數對原子類(lèi)型的變量v原子地減1,并判斷結果是否為0,如果為0,返回真,否則返回假。

int atomic_inc_and_test(atomic_t *v);

該函數對原子類(lèi)型的變量v原子地增加1,并判斷結果是否為0,如果為0,返回真,否則返回假。

int atomic_add_negative(int i, atomic_t *v);

該函數對原子類(lèi)型的變量v原子地增加I,并判斷結果是否為負數,如果是,返回真,否則返回假。

int atomic_add_return(int i, atomic_t *v);

該函數對原子類(lèi)型的變量v原子地增加i,并且返回指向v的指針。

int atomic_sub_return(int i, atomic_t *v);

該函數從原子類(lèi)型的變量v中減去i,并且返回指向v的指針。

int atomic_inc_return(atomic_t * v);

該函數對原子類(lèi)型的變量v原子地增加1并且返回指向v的指針。

int atomic_dec_return(atomic_t * v);

該函數對原子類(lèi)型的變量v原子地減1并且返回指向v的指針。

原子操作通常用于實(shí)現資源的引用計數,在TCP/IP協(xié)議棧的IP碎片處理中,就使用了引用計數,碎片隊列結構struct ipq描述了一個(gè)IP碎片,字段refcnt就是引用計數器,它的類(lèi)型為atomic_t,當創(chuàng )建IP碎片時(shí)(在函數ip_frag_create中),使用atomic_set函數把它設置為1,當引用該IP碎片時(shí),就使用函數atomic_inc把引用計數加1。

當不需要引用該IP碎片時(shí),就使用函數ipq_put來(lái)釋放該IP碎片,ipq_put使用函數atomic_dec_and_test把引用計數減1并判斷引用計數是否為0,如果是就釋放IP碎片。函數ipq_kill把IP碎片從ipq隊列中刪除,并把該刪除的IP碎片的引用計數減1(通過(guò)使用函數atomic_dec實(shí)現)。

三、信號量(semaphore)

內核的信號量在概念和原理上與用戶(hù)態(tài)的System V的IPC機制信號量是一樣的,但是它絕不可能在內核之外使用,因此它與System V的IPC機制信號量毫不相干。

信號量在創(chuàng )建時(shí)需要設置一個(gè)初始值,表示同時(shí)可以有幾個(gè)任務(wù)可以訪(fǎng)問(wèn)該信號量保護的共享資源,初始值為1就變成互斥鎖(Mutex),即同時(shí)只能有一個(gè)任務(wù)可以訪(fǎng)問(wèn)信號量保護的共享資源。

一個(gè)任務(wù)要想訪(fǎng)問(wèn)共享資源,首先必須得到信號量,獲取信號量的操作將把信號量的值減1,若當前信號量的值為負數,表明無(wú)法獲得信號量,該任務(wù)必須掛起在該信號量的等待隊列等待該信號量可用;若當前信號量的值為非負數,表示可以獲得信號量,因而可以立刻訪(fǎng)問(wèn)被該信號量保護的共享資源。

當任務(wù)訪(fǎng)問(wèn)完被信號量保護的共享資源后,必須釋放信號量,釋放信號量通過(guò)把信號量的值加1實(shí)現,如果信號量的值為非正數,表明有任務(wù)等待當前信號量,因此它也喚醒所有等待該信號量的任務(wù)。

信號量的API有:

DECLARE_MUTEX(name)

該宏聲明一個(gè)信號量name并初始化它的值為0,即聲明一個(gè)互斥鎖。

DECLARE_MUTEX_LOCKED(name)

該宏聲明一個(gè)互斥鎖name,但把它的初始值設置為0,即鎖在創(chuàng )建時(shí)就處在已鎖狀態(tài)。因此對于這種鎖,一般是先釋放后獲得。

void sema_init (struct semaphore *sem, int val);

該函用于數初始化設置信號量的初值,它設置信號量sem的值為val。

void init_MUTEX (struct semaphore *sem);

該函數用于初始化一個(gè)互斥鎖,即它把信號量sem的值設置為1。

void init_MUTEX_LOCKED (struct semaphore *sem);

該函數也用于初始化一個(gè)互斥鎖,但它把信號量sem的值設置為0,即一開(kāi)始就處在已鎖狀態(tài)。

void down(struct semaphore * sem);

該函數用于獲得信號量sem,它會(huì )導致睡眠,因此不能在中斷上下文(包括IRQ上下文和softirq上下文)使用該函數。該函數將把sem的值減1,如果信號量sem的值非負,就直接返回,否則調用者將被掛起,直到別的任務(wù)釋放該信號量才能繼續運行。

int down_interruptible(struct semaphore * sem);

本文引用地址:http://dyxdggzs.com/article/201706/349380.htm
該函數功能與down類(lèi)似,不同之處為,down不會(huì )被信號(signal)打斷,但down_interruptible能被信號打斷,因此該函數有返回值來(lái)區分是正常返回還是被信號中斷,如果返回0,表示獲得信號量正常返回,如果被信號打斷,返回-EINTR。

int down_trylock(struct semaphore * sem);

該函數試著(zhù)獲得信號量sem,如果能夠立刻獲得,它就獲得該信號量并返回0,否則,表示不能獲得信號量sem,返回值為非0值。因此,它不會(huì )導致調用者睡眠,可以在中斷上下文使用。

void up(struct semaphore * sem);

該函數釋放信號量sem,即把sem的值加1,如果sem的值為非正數,表明有任務(wù)等待該信號量,因此喚醒這些等待者。

信號量在絕大部分情況下作為互斥鎖使用,下面以console驅動(dòng)系統為例說(shuō)明信號量的使用。

在內核源碼樹(shù)的kernel/printk.c中,使用宏DECLARE_MUTEX聲明了一個(gè)互斥鎖console_sem,它用于保護console驅動(dòng)列表console_drivers以及同步對整個(gè)console驅動(dòng)系統的訪(fǎng)問(wèn)。

其中定義了函數acquire_console_sem來(lái)獲得互斥鎖 console_sem,定義了release_console_sem來(lái)釋放互斥鎖console_sem,定義了函數 try_acquire_console_sem來(lái)盡力得到互斥鎖console_sem。這三個(gè)函數實(shí)際上是分別對函數down,up和 down_trylock的簡(jiǎn)單包裝。

需要訪(fǎng)問(wèn)console_drivers驅動(dòng)列表時(shí)就需要使用acquire_console_sem來(lái)保護console_drivers列表,當訪(fǎng)問(wèn)完該列表后,就調用release_console_sem釋放信號量console_sem。

函數console_unblank,console_device, console_stop,console_start,register_console和unregister_console都需要訪(fǎng)問(wèn) console_drivers,因此它們都使用函數對acquire_console_sem和release_console_sem來(lái)對 console_drivers進(jìn)行保護。

四、讀寫(xiě)信號量(rw_semaphore)

讀寫(xiě)信號量對訪(fǎng)問(wèn)者進(jìn)行了細分,或者為讀者,或者為寫(xiě)者,讀者在保持讀寫(xiě)信號量期間只能對該讀寫(xiě)信號量保護的共享資源進(jìn)行讀訪(fǎng)問(wèn),如果一個(gè)任務(wù)除了需要讀,可能還需要寫(xiě),那么它必須被歸類(lèi)為寫(xiě)者,它在對共享資源訪(fǎng)問(wèn)之前必須先獲得寫(xiě)者身份,寫(xiě)者在發(fā)現自己不需要寫(xiě)訪(fǎng)問(wèn)的情況下可以降級為讀者。讀寫(xiě)信號量同時(shí)擁有的讀者數不受限制,也就說(shuō)可以有任意多個(gè)讀者同時(shí)擁有一個(gè)讀寫(xiě)信號量。

如果一個(gè)讀寫(xiě)信號量當前沒(méi)有被寫(xiě)者擁有并且也沒(méi)有寫(xiě)者等待讀者釋放信號量,那么任何讀者都可以成功獲得該讀寫(xiě)信號量;否則,讀者必須被掛起直到寫(xiě)者釋放該信號量。如果一個(gè)讀寫(xiě)信號量當前沒(méi)有被讀者或寫(xiě)者擁有并且也沒(méi)有寫(xiě)者等待該信號量,那么一個(gè)寫(xiě)者可以成功獲得該讀寫(xiě)信號量,否則寫(xiě)者將被掛起,直到?jīng)]有任何訪(fǎng)問(wèn)者。因此,寫(xiě)者是排他性的,獨占性的。

讀寫(xiě)信號量有兩種實(shí)現,一種是通用的,不依賴(lài)于硬件架構,因此,增加新的架構不需要重新實(shí)現它,但缺點(diǎn)是性能低,獲得和釋放讀寫(xiě)信號量的開(kāi)銷(xiāo)大;另一種是架構相關(guān)的,因此性能高,獲取和釋放讀寫(xiě)信號量的開(kāi)銷(xiāo)小,但增加新的架構需要重新實(shí)現。在內核配置時(shí),可以通過(guò)選項去控制使用哪一種實(shí)現。

讀寫(xiě)信號量的相關(guān)API有:

DECLARE_RWSEM(name)

該宏聲明一個(gè)讀寫(xiě)信號量name并對其進(jìn)行初始化。

void init_rwsem(struct rw_semaphore *sem);

該函數對讀寫(xiě)信號量sem進(jìn)行初始化。

void down_read(struct rw_semaphore *sem);

讀者調用該函數來(lái)得到讀寫(xiě)信號量sem。該函數會(huì )導致調用者睡眠,因此只能在進(jìn)程上下文使用。

int down_read_trylock(struct rw_semaphore *sem);

該函數類(lèi)似于down_read,只是它不會(huì )導致調用者睡眠。它盡力得到讀寫(xiě)信號量sem,如果能夠立即得到,它就得到該讀寫(xiě)信號量,并且返回1,否則表示不能立刻得到該信號量,返回0。因此,它也可以在中斷上下文使用。

void down_write(struct rw_semaphore *sem);

寫(xiě)者使用該函數來(lái)得到讀寫(xiě)信號量sem,它也會(huì )導致調用者睡眠,因此只能在進(jìn)程上下文使用。

int down_write_trylock(struct rw_semaphore *sem);

該函數類(lèi)似于down_write,只是它不會(huì )導致調用者睡眠。該函數盡力得到讀寫(xiě)信號量,如果能夠立刻獲得,就獲得該讀寫(xiě)信號量并且返回1,否則表示無(wú)法立刻獲得,返回0。它可以在中斷上下文使用。

void up_read(struct rw_semaphore *sem);

讀者使用該函數釋放讀寫(xiě)信號量sem。它與down_read或down_read_trylock配對使用。如果down_read_trylock返回0,不需要調用up_read來(lái)釋放讀寫(xiě)信號量,因為根本就沒(méi)有獲得信號量。

void up_write(struct rw_semaphore *sem);

寫(xiě)者調用該函數釋放信號量sem。它與down_write或down_write_trylock配對使用。如果down_write_trylock返回0,不需要調用up_write,因為返回0表示沒(méi)有獲得該讀寫(xiě)信號量。

void downgrade_write(struct rw_semaphore *sem);


該函數用于把寫(xiě)者降級為讀者,這有時(shí)是必要的。因為寫(xiě)者是排他性的,因此在寫(xiě)者保持讀寫(xiě)信號量期間,任何讀者或寫(xiě)者都將無(wú)法訪(fǎng)問(wèn)該讀寫(xiě)信號量保護的共享資源,對于那些當前條件下不需要寫(xiě)訪(fǎng)問(wèn)的寫(xiě)者,降級為讀者將,使得等待訪(fǎng)問(wèn)的讀者能夠立刻訪(fǎng)問(wèn),從而增加了并發(fā)性,提高了效率。

讀寫(xiě)信號量適于在讀多寫(xiě)少的情況下使用,在中對進(jìn)程的內存映像描述結構的訪(fǎng)問(wèn)就使用了讀寫(xiě)信號量進(jìn)行保護。

中,每一個(gè)進(jìn)程都用一個(gè)類(lèi)型為task_t或struct task_struct的結構來(lái)描述,該結構的類(lèi)型為struct mm_struct的字段mm描述了進(jìn)程的內存映像,特別是mm_struct結構的mmap字段維護了整個(gè)進(jìn)程的內存塊列表,該列表將在進(jìn)程生存期間被大量地遍利或修改。

因此mm_struct結構就有一個(gè)字段mmap_sem來(lái)對mmap的訪(fǎng)問(wèn)進(jìn)行保護, mmap_sem就是一個(gè)讀寫(xiě)信號量,在proc文件系統里有很多進(jìn)程內存使用情況的接口,通過(guò)它們能夠查看某一進(jìn)程的內存使用情況,命令free、ps 和top都是通過(guò)proc來(lái)得到內存使用信息的,proc接口就使用down_read和up_read來(lái)讀取進(jìn)程的mmap信息。

當進(jìn)程動(dòng)態(tài)地分配或釋放內存時(shí),需要修改mmap來(lái)反映分配或釋放后的內存映像,因此動(dòng)態(tài)內存分配或釋放操作需要以寫(xiě)者身份獲得讀寫(xiě)信號量 mmap_sem來(lái)對mmap進(jìn)行更新。系統調用brk和munmap就使用了down_write和 up_write來(lái)保護對mmap的訪(fǎng)問(wèn)。

五、自旋鎖(spinlock)

自旋鎖與互斥鎖有點(diǎn)類(lèi)似,只是自旋鎖不會(huì )引起調用者睡眠,如果自旋鎖已經(jīng)被別的執行單元保持,調用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,自旋一詞就是因此而得名。

由于自旋鎖使用者一般保持鎖時(shí)間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠高于互斥鎖。

信號量和讀寫(xiě)信號量適合于保持時(shí)間較長(cháng)的情況,它們會(huì )導致調用者睡眠,因此只能在進(jìn)程上下文使用(_trylock的變種能夠在中斷上下文使用),而自旋鎖適合于保持時(shí)間非常短的情況,它可以在任何上下文使用。

如果被保護的共享資源只在進(jìn)程上下文訪(fǎng)問(wèn),使用信號量保護該共享資源非常合適,如果對共巷資源的訪(fǎng)問(wèn)時(shí)間非常短,自旋鎖也可以。但是如果被保護的共享資源需要在中斷上下文訪(fǎng)問(wèn)(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。

自旋鎖保持期間是搶占失效的,而信號量和讀寫(xiě)信號量保持期間是可以被搶占的。自旋鎖只有在內核可搶占或SMP的情況下才真正需要,在單CPU且不可搶占的內核下,自旋鎖的所有操作都是空操作。

跟互斥鎖一樣,一個(gè)執行單元要想訪(fǎng)問(wèn)被自旋鎖保護的共享資源,必須先得到鎖,在訪(fǎng)問(wèn)完共享資源后,必須釋放鎖。如果在獲取自旋鎖時(shí),沒(méi)有任何執行單元保持該鎖,那么將立即得到鎖;如果在獲取自旋鎖時(shí)鎖已經(jīng)有保持者,那么獲取鎖操作將自旋在那里,直到該自旋鎖的保持者釋放了鎖。

無(wú)論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)保持者,也就說(shuō),在任何時(shí)刻最多只能有一個(gè)執行單元獲得鎖。

自旋鎖的API有:

spin_lock_init(x)

該宏用于初始化自旋鎖x。自旋鎖在真正使用前必須先初始化。該宏用于動(dòng)態(tài)初始化。

DEFINE_SPINLOCK(x)

該宏聲明一個(gè)自旋鎖x并初始化它。該宏在2.6.11中第一次被定義,在先前的內核中并沒(méi)有該宏。

SPIN_LOCK_UNLOCKED

該宏用于靜態(tài)初始化一個(gè)自旋鎖。

DEFINE_SPINLOCK(x)等同于spinlock_t x = SPIN_LOCK_UNLOCKEDspin_is_locked(x)

該宏用于判斷自旋鎖x是否已經(jīng)被某執行單元保持(即被鎖),如果是,返回真,否則返回假。

spin_unlock_wait(x)

該宏用于等待自旋鎖x變得沒(méi)有被任何執行單元保持,如果沒(méi)有任何執行單元保持該自旋鎖,該宏立即返回,否則將循環(huán)在那里,直到該自旋鎖被保持者釋放。

spin_trylock(lock)

該宏盡力獲得自旋鎖lock,如果能立即獲得鎖,它獲得鎖并返回真,否則不能立即獲得鎖,立即返回假。它不會(huì )自旋等待lock被釋放。

spin_lock(lock)

該宏用于獲得自旋鎖lock,如果能夠立即獲得鎖,它就馬上返回,否則,它將自旋在那里,直到該自旋鎖的保持者釋放,這時(shí),它獲得鎖并返回??傊?,只有它獲得鎖才返回。

spin_lock_irqsave(lock, flags)

該宏獲得自旋鎖的同時(shí)把標志寄存器的值保存到變量flags中并失效本地中斷。

spin_lock_irq(lock)

該宏類(lèi)似于spin_lock_irqsave,只是該宏不保存標志寄存器的值。

spin_lock_bh(lock)

該宏在得到自旋鎖的同時(shí)失效本地軟中斷。

spin_unlock(lock)

該宏釋放自旋鎖lock,它與spin_trylock或spin_lock配對使用。如果spin_trylock返回假,表明沒(méi)有獲得自旋鎖,因此不必使用spin_unlock釋放。

spin_unlock_irqrestore(lock, flags)

該宏釋放自旋鎖lock的同時(shí),也恢復標志寄存器的值為變量flags保存的值。它與spin_lock_irqsave配對使用。

spin_unlock_irq(lock)

該宏釋放自旋鎖lock的同時(shí),也使能本地中斷。它與spin_lock_irq配對應用。

spin_unlock_bh(lock)

該宏釋放自旋鎖lock的同時(shí),也使能本地的軟中斷。它與spin_lock_bh配對使用。

spin_trylock_irqsave(lock, flags)

該宏如果獲得自旋鎖lock,它也將保存標志寄存器的值到變量flags中,并且失效本地中斷,如果沒(méi)有獲得鎖,它什么也不做。
因此如果能夠立即獲得鎖,它等同于spin_lock_irqsave,如果不能獲得鎖,它等同于spin_trylock。如果該宏獲得自旋鎖lock,那需要使用spin_unlock_irqrestore來(lái)釋放。

spin_trylock_irq(lock)

該宏類(lèi)似于spin_trylock_irqsave,只是該宏不保存標志寄存器。如果該宏獲得自旋鎖lock,需要使用spin_unlock_irq來(lái)釋放。

spin_trylock_bh(lock)

該宏如果獲得了自旋鎖,它也將失效本地軟中斷。如果得不到鎖,它什么也不做。因此,如果得到了鎖,它等同于spin_lock_bh,如果得不到鎖,它等同于spin_trylock。如果該宏得到了自旋鎖,需要使用spin_unlock_bh來(lái)釋放。

spin_can_lock(lock)

該宏用于判斷自旋鎖lock是否能夠被鎖,它實(shí)際是spin_is_locked取反。如果lock沒(méi)有被鎖,它返回真,否則,返回假。該宏在2.6.11中第一次被定義,在先前的內核中并沒(méi)有該宏。

獲得自旋鎖和釋放自旋鎖有好幾個(gè)版本,因此讓讀者知道在什么樣的情況下使用什么版本的獲得和釋放鎖的宏是非常必要的。


如果被保護的共享資源只在進(jìn)程上下文訪(fǎng)問(wèn)和軟中斷上下文訪(fǎng)問(wèn),那么當在進(jìn)程上下文訪(fǎng)問(wèn)共享資源時(shí),可能被軟中斷打斷,從而可能進(jìn)入軟中斷上下文來(lái)對被保護的共享資源訪(fǎng)問(wèn),因此對于這種情況,對共享資源的訪(fǎng)問(wèn)必須使用spin_lock_bh和 spin_unlock_bh來(lái)保護。

當然使用spin_lock_irq和spin_unlock_irq以及 spin_lock_irqsave和spin_unlock_irqrestore也可以,它們失效了本地硬中斷,失效硬中斷隱式地也失效了軟中斷。但是使用spin_lock_bh和spin_unlock_bh是最恰當的,它比其他兩個(gè)快。

如果被保護的共享資源只在進(jìn)程上下文和tasklet或timer上下文訪(fǎng)問(wèn),那么應該使用與上面情況相同的獲得和釋放鎖的宏,因為tasklet和timer是用軟中斷實(shí)現的。

如果被保護的共享資源只在一個(gè)tasklet或timer上下文訪(fǎng)問(wèn),那么不需要任何自旋鎖保護,因為同一個(gè)tasklet或timer只能在一個(gè) CPU上運行,即使是在SMP環(huán)境下也是如此。實(shí)際上tasklet在調用 tasklet_schedule標記其需要被調度時(shí)已經(jīng)把該tasklet綁定到當前CPU,因此同一個(gè)tasklet決不可能同時(shí)在其他CPU上運行。

timer也是在其被使用add_timer添加到timer隊列中時(shí)已經(jīng)被幫定到當前CPU,所以同一個(gè)timer絕不可能運行在其他CPU上。當然同一個(gè)tasklet有兩個(gè)實(shí)例同時(shí)運行在同一個(gè)CPU就更不可能了。

如果被保護的共享資源只在兩個(gè)或多個(gè)tasklet或timer上下文訪(fǎng)問(wèn),那么對共享資源的訪(fǎng)問(wèn)僅需要用spin_lock和 spin_unlock來(lái)保護,不必使用_bh版本,因為當tasklet或timer運行時(shí),不可能有其他 tasklet或timer在當前CPU上運行。

如果被保護的共享資源只在一個(gè)軟中斷(tasklet和timer除外)上下文訪(fǎng)問(wèn),那么這個(gè)共享資源需要用spin_lock和spin_unlock來(lái)保護,因為同樣的軟中斷可以同時(shí)在不同的CPU上運行。

如果被保護的共享資源在兩個(gè)或多個(gè)軟中斷上下文訪(fǎng)問(wèn),那么這個(gè)共享資源當然更需要用spin_lock和spin_unlock來(lái)保護,不同的軟中斷能夠同時(shí)在不同的CPU上運行。

如果被保護的共享資源在軟中斷(包括tasklet和timer)或進(jìn)程上下文和硬中斷上下文訪(fǎng)問(wèn),那么在軟中斷或進(jìn)程上下文訪(fǎng)問(wèn)期間,可能被硬中斷打斷,從而進(jìn)入硬中斷上下文對共享資源進(jìn)行訪(fǎng)問(wèn),因此,在進(jìn)程或軟中斷上下文需要使用 spin_lock_irq和spin_unlock_irq來(lái)保護對共享資源的訪(fǎng)問(wèn)。

而在中斷處理句柄中使用什么版本,需依情況而定,如果只有一個(gè)中斷處理句柄訪(fǎng)問(wèn)該共享資源,那么在中斷處理句柄中僅需要spin_lock和spin_unlock來(lái)保護對共享資源的訪(fǎng)問(wèn)就可以了。

因為在執行中斷處理句柄期間,不可能被同一CPU上的軟中斷或進(jìn)程打斷。但是如果有不同的中斷處理句柄訪(fǎng)問(wèn)該共享資源,那么需要在中斷處理句柄中使用spin_lock_irq和spin_unlock_irq來(lái)保護對共享資源的訪(fǎng)問(wèn)。

在使用spin_lock_irq和spin_unlock_irq的情況下,完全可以用 spin_lock_irqsave和spin_unlock_irqrestore取代,那具體應該使用哪一個(gè)也需要依情況而定,如果可以確信在對共享資源訪(fǎng)問(wèn)前中斷是使能的,那么使用spin_lock_irq更好一些。

因為它比spin_lock_irqsave要快一些,但是如果你不能確定是否中斷使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因為它將恢復訪(fǎng)問(wèn)共享資源前的中斷標志而不是直接使能中斷。

當然,有些情況下需要在訪(fǎng)問(wèn)共享資源時(shí)必須中斷失效,而訪(fǎng)問(wèn)完后必須中斷使能,這樣的情形使用spin_lock_irq和spin_unlock_irq最好。

需要特別提醒讀者,spin_lock用于阻止在不同CPU上的執行單元對共享資源的同時(shí)訪(fǎng)問(wèn)以及不同進(jìn)程上下文互相搶占導致的對共享資源的非同步訪(fǎng)問(wèn),而中斷失效和軟中斷失效卻是為了阻止在同一CPU上軟中斷或中斷對共享資源的非同步訪(fǎng)問(wèn)。



關(guān)鍵詞: linux內核 同步機制 Linux

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>