<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Linux內(nèi)核的同步機(jī)制

          Linux內(nèi)核的同步機(jī)制

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

          一、引言

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

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

          二、原子操作

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

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

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

          typedef struct { volatile int counter; } atomic_t;

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

          原子操作API包括:

          atomic_read(atomic_t * v);

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

          atomic_set(atomic_t * v, int i);

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

          void atomic_add(int i, atomic_t *v);

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

          atomic_sub(int i, atomic_t *v);

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

          int atomic_sub_and_test(int i, atomic_t *v);

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

          void atomic_inc(atomic_t *v);

          該函數(shù)對(duì)原子類型變量v原子地增加1。

          void atomic_dec(atomic_t *v);

          該函數(shù)對(duì)原子類型的變量v原子地減1。

          int atomic_dec_and_test(atomic_t *v);

          該函數(shù)對(duì)原子類型的變量v原子地減1,并判斷結(jié)果是否為0,如果為0,返回真,否則返回假。

          int atomic_inc_and_test(atomic_t *v);

          該函數(shù)對(duì)原子類型的變量v原子地增加1,并判斷結(jié)果是否為0,如果為0,返回真,否則返回假。

          int atomic_add_negative(int i, atomic_t *v);

          該函數(shù)對(duì)原子類型的變量v原子地增加I,并判斷結(jié)果是否為負(fù)數(shù),如果是,返回真,否則返回假。

          int atomic_add_return(int i, atomic_t *v);

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

          int atomic_sub_return(int i, atomic_t *v);

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

          int atomic_inc_return(atomic_t * v);

          該函數(shù)對(duì)原子類型的變量v原子地增加1并且返回指向v的指針。

          int atomic_dec_return(atomic_t * v);

          該函數(shù)對(duì)原子類型的變量v原子地減1并且返回指向v的指針。

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

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

          三、信號(hào)量(semaphore)

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

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

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

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

          信號(hào)量的API有:

          DECLARE_MUTEX(name)

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

          DECLARE_MUTEX_LOCKED(name)

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

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

          該函用于數(shù)初始化設(shè)置信號(hào)量的初值,它設(shè)置信號(hào)量sem的值為val。

          void init_MUTEX (struct semaphore *sem);

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

          void init_MUTEX_LOCKED (struct semaphore *sem);

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

          void down(struct semaphore * sem);

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

          int down_interruptible(struct semaphore * sem);

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

          int down_trylock(struct semaphore * sem);

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

          void up(struct semaphore * sem);

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

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

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

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

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

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

          四、讀寫信號(hào)量(rw_semaphore)

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

          如果一個(gè)讀寫信號(hào)量當(dāng)前沒(méi)有被寫者擁有并且也沒(méi)有寫者等待讀者釋放信號(hào)量,那么任何讀者都可以成功獲得該讀寫信號(hào)量;否則,讀者必須被掛起直到寫者釋放該信號(hào)量。如果一個(gè)讀寫信號(hào)量當(dāng)前沒(méi)有被讀者或?qū)懻邠碛胁⑶乙矝](méi)有寫者等待該信號(hào)量,那么一個(gè)寫者可以成功獲得該讀寫信號(hào)量,否則寫者將被掛起,直到?jīng)]有任何訪問(wèn)者。因此,寫者是排他性的,獨(dú)占性的。

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

          讀寫信號(hào)量的相關(guān)API有:

          DECLARE_RWSEM(name)

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

          void init_rwsem(struct rw_semaphore *sem);

          該函數(shù)對(duì)讀寫信號(hào)量sem進(jìn)行初始化。

          void down_read(struct rw_semaphore *sem);

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

          int down_read_trylock(struct rw_semaphore *sem);

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

          void down_write(struct rw_semaphore *sem);

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

          int down_write_trylock(struct rw_semaphore *sem);

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

          void up_read(struct rw_semaphore *sem);

          讀者使用該函數(shù)釋放讀寫信號(hào)量sem。它與down_read或down_read_trylock配對(duì)使用。如果down_read_trylock返回0,不需要調(diào)用up_read來(lái)釋放讀寫信號(hào)量,因?yàn)楦揪蜎](méi)有獲得信號(hào)量。

          void up_write(struct rw_semaphore *sem);

          寫者調(diào)用該函數(shù)釋放信號(hào)量sem。它與down_write或down_write_trylock配對(duì)使用。如果down_write_trylock返回0,不需要調(diào)用up_write,因?yàn)榉祷?表示沒(méi)有獲得該讀寫信號(hào)量。

          void downgrade_write(struct rw_semaphore *sem);


          該函數(shù)用于把寫者降級(jí)為讀者,這有時(shí)是必要的。因?yàn)閷懻呤桥潘缘模虼嗽趯懻弑3肿x寫信號(hào)量期間,任何讀者或?qū)懻叨紝o(wú)法訪問(wèn)該讀寫信號(hào)量保護(hù)的共享資源,對(duì)于那些當(dāng)前條件下不需要寫訪問(wèn)的寫者,降級(jí)為讀者將,使得等待訪問(wèn)的讀者能夠立刻訪問(wèn),從而增加了并發(fā)性,提高了效率。

          讀寫信號(hào)量適于在讀多寫少的情況下使用,在中對(duì)進(jìn)程的內(nèi)存映像描述結(jié)構(gòu)的訪問(wèn)就使用了讀寫信號(hào)量進(jìn)行保護(hù)。

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

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

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

          五、自旋鎖(spinlock)

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

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

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

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

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

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

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

          自旋鎖的API有:

          spin_lock_init(x)

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

          DEFINE_SPINLOCK(x)

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

          SPIN_LOCK_UNLOCKED

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

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

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

          spin_unlock_wait(x)

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

          spin_trylock(lock)

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

          spin_lock(lock)

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

          spin_lock_irqsave(lock, flags)

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

          spin_lock_irq(lock)

          該宏類似于spin_lock_irqsave,只是該宏不保存標(biāo)志寄存器的值。

          spin_lock_bh(lock)

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

          spin_unlock(lock)

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

          spin_unlock_irqrestore(lock, flags)

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

          spin_unlock_irq(lock)

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

          spin_unlock_bh(lock)

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

          spin_trylock_irqsave(lock, flags)

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

          spin_trylock_irq(lock)

          該宏類似于spin_trylock_irqsave,只是該宏不保存標(biāo)志寄存器。如果該宏獲得自旋鎖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中第一次被定義,在先前的內(nèi)核中并沒(méi)有該宏。

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


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

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

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

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

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

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

          如果被保護(hù)的共享資源只在一個(gè)軟中斷(tasklet和timer除外)上下文訪問(wèn),那么這個(gè)共享資源需要用spin_lock和spin_unlock來(lái)保護(hù),因?yàn)橥瑯拥能浿袛嗫梢酝瑫r(shí)在不同的CPU上運(yùn)行。

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

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

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

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

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

          因?yàn)樗萻pin_lock_irqsave要快一些,但是如果你不能確定是否中斷使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因?yàn)樗鼘⒒謴?fù)訪問(wèn)共享資源前的中斷標(biāo)志而不是直接使能中斷。

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

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



          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();