linux大核心鎖原理

來源:互聯網
上載者:User


linux大核心鎖原理 大核心鎖(BKL)的設計是在kernel hacker們對多處理器的同步還沒有十足把握時,引入的大粒度鎖。他的設計思想是,一旦某個核心路徑擷取了這把鎖,那麼其他所有的核心路徑都不能再擷取到這把鎖。自旋鎖加鎖的對象一般是一個全域變數,大核心鎖加鎖的對象是一段代碼,裡面可能包含多個全域變數。那麼他帶來的問題是,雖然A只需要互斥訪問全域變數a,但附帶鎖了全域變數b,從而導致B不能訪問b了。 大核心鎖最先的實現靠一個全域自旋鎖,但大家覺得這個鎖的開銷太大了,影響了即時性,因此後來將自旋鎖改成了mutex,但阻塞時間一般不是很長,所以加鎖失敗的掛起和喚醒也是非常costly 所以後來又改成了自旋鎖實現。大核心鎖一般是在檔案系統,驅動等中用的比較多。目前kernel hacker們仍然在努力將大核心鎖從linux裡剷除。 下面來分析大核心鎖的實現。我們之前說了大核心鎖有兩種實現,分別是自旋鎖和mutex鎖。如果是mutex鎖實現,自然不能在中斷環境下使用大核心鎖,因為中斷下禁止調度是金科玉律。那麼在大核心鎖內調度是否可以?我們知道,如果一個核心流程擷取到資源後就應該儘快完成操作釋放資源,  www.2cto.com  以便下一個競爭者擷取到資源。所以資源持有人不得睡眠是一個普遍共識。可是大核心鎖不這麼認為,持有大核心鎖的使用者是允許睡眠的-雖然我們並不鼓勵這樣,但是核心的大核心鎖的設計方案裡,會在進程切換時,檢查當前進程是否持有大核心鎖並釋放,當重新擷取到cpu後,再嘗試抓這把大核心鎖。也就是說,進程在持有大核心鎖時是可以睡眠的,這就帶來資源starvation 來看基於自旋鎖的大核心鎖實現 static inline void __lock_kernel(void){preempt_disable();if (unlikely(!_raw_spin_trylock(&kernel_flag))) {/** If preemption was disabled even before this* was called, there's nothing we can be polite* about - just spin.*/if (preempt_count() > 1) {_raw_spin_lock(&kernel_flag);return;  www.2cto.com  } /** Otherwise, let's wait for the kernel lock* with preemption enabled..*/do {preempt_enable();while (spin_is_locked(&kernel_flag))cpu_relax();preempt_disable();} while (!_raw_spin_trylock(&kernel_flag));}} void __lockfunc lock_kernel(void){int depth = current->lock_depth+1;if (likely(!depth))__lock_kernel();current->lock_depth = depth;}  www.2cto.com   這段代碼的意思是,1) 如果當前進程如果不是重複加鎖的話,就嘗試去抓這把鎖,並把鎖深度加1。這麼做的目的是避免鎖重入。2)實際加鎖的時候,先關搶佔,如果嘗試加鎖失敗,則會根據調用lock_kernel之前關搶佔與否,來決定是悶頭死轉,還是大開門戶的輪詢。 如果是mutex實現的大核心鎖kernel_lock,則第2步直接mutex_lock--要麼成功要麼阻塞。 之前我們提到,在進程發生切換時,會檢查當前進程是否持有大核心鎖,這是在schedule裡做的。asmlinkage void __sched schedule(void){  www.2cto.com    release_kernel_lock(prev);  context_switch();  reacquire_kernel_lock(current);} release_kernel_lock會判斷如果當前進程持有大核心鎖,則釋放鎖。 reacquire_kernel_lock在進程再次被調度回來後,檢查當前進程在切換之前是否因為持有大核心鎖。如果有的話,說明在進程切換時,當前進程的大核心鎖被強行釋放了,需要再次擷取。需要說明的是自旋鎖版本:release_kernel_lock在釋放鎖之後還會開搶佔,因為擷取到大核心鎖之後會關閉;reacquire_kernel_lock在重新擷取到鎖之後,會關閉搶佔。 重點關注__reacquire_kernel_lock的實現自旋鎖的實現版本:成功抓到鎖之後關搶佔,如果抓不到鎖,則一直遍曆need resched標誌直至退出。注意和lock_kernel比較。mutex版本就比較扯淡了:int __lockfunc __reacquire_kernel_lock(void){  www.2cto.com  int saved_lock_depth = current->lock_depth; BUG_ON(saved_lock_depth < 0); current->lock_depth = -1;preempt_enable_no_resched();mutex_lock(&kernel_sem); preempt_disable();current->lock_depth = saved_lock_depth; return 0;}我們之前看到kernel_lock的mutex實現就是一句mutex_lock 而這裡的__reacquire_kernel_lock有些細節差別。1)首先將當前進程的加鎖深度設定為-1,代表無人加鎖。這麼做的意義是,第2步的mutex_lock如果產生調度,再次進入shedule時,不會重複釋放大核心鎖,因為__reacquire_kernel_lock之前已經釋放鎖了。2)接著臨時強行開搶佔後執行mutex_lock  www.2cto.com  因為在schedule裡是關搶佔的,此時不能發生進程切換。3)如果抓到鎖則關搶佔恢複到schedule裡調__reacquire_kernel_lock之前的搶佔狀態4)將加鎖深度恢複到__reacquire_kernel_lock之前的深度恢複到schedule裡調__reacquire_kernel_lock之前的大核心鎖持有狀態 總而言之,關於大核心鎖,記住兩點就可以了:1)由spinlock或者mutex_lock鎖住一個全域變數來實現2)進程切換時會檢查當前進程是否持有大核心鎖,而採取釋放和重獲的操作,以支援持有大核心鎖的使用者代碼睡眠。  作者 chenyu105

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.