免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
【原創(chuàng)】Linux Mutex機(jī)制分析

背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

  • Mutex互斥鎖是Linux內(nèi)核中用于互斥操作的一種同步原語;
  • 互斥鎖是一種休眠鎖,鎖爭用時可能存在進(jìn)程的睡眠與喚醒,context的切換帶來的代價較高,適用于加鎖時間較長的場景;
  • 互斥鎖每次只允許一個進(jìn)程進(jìn)入臨界區(qū),有點(diǎn)類似于二值信號量;
  • 互斥鎖在鎖爭用時,在鎖被持有時,選擇自選等待,而不立即進(jìn)行休眠,可以極大的提高性能,這種機(jī)制(optimistic spinning)也應(yīng)用到了讀寫信號量上;
  • 互斥鎖的缺點(diǎn)是互斥鎖對象的結(jié)構(gòu)較大,會占用更多的CPU緩存和內(nèi)存空間;
  • 與信號量相比,互斥鎖的性能與擴(kuò)展性都更好,因此,在內(nèi)核中總是會優(yōu)先考慮互斥鎖;
  • 互斥鎖按為了提高性能,提供了三條路徑處理:快速路徑,中速路徑,慢速路徑;

前戲都已經(jīng)講完了,來看看實(shí)際的實(shí)現(xiàn)過程吧。

2. optimistic spinning

2.1 MCS鎖

  • 上文中提到過Mutex在實(shí)現(xiàn)過程中,采用了optimistic spinning自旋等待機(jī)制,這個機(jī)制的核心就是基于MCS鎖機(jī)制來實(shí)現(xiàn)的;
  • MCS鎖機(jī)制是由John Mellor CrummeyMichael Scott在論文中《algorithms for scalable synchronization on shared-memory multiprocessors》提出的,并以他倆的名字來命名;
  • MCS鎖機(jī)制要解決的問題是:在多CPU系統(tǒng)中,自旋鎖都在同一個變量上進(jìn)行自旋,在獲取鎖時會將包含鎖的cache line移動到本地CPU,這種cache-line bouncing會很大程度影響性能;
  • MCS鎖機(jī)制的核心思想:每個CPU都分配一個自旋鎖結(jié)構(gòu)體,自旋鎖的申請者(per-CPU)在local-CPU變量上自旋,這些結(jié)構(gòu)體組建成一個鏈表,申請者自旋等待前驅(qū)節(jié)點(diǎn)釋放該鎖;
  • osq(optimistci spinning queue)是基于MCS算法的一個具體實(shí)現(xiàn),并經(jīng)過了迭代優(yōu)化;

2.2 osq流程分析

optimistic spinning,樂觀自旋,到底有多樂觀呢?當(dāng)發(fā)現(xiàn)鎖被持有時,optimistic spinning相信持有者很快就能把鎖釋放,因此它選擇自旋等待,而不是睡眠等待,這樣也就能減少進(jìn)程切換帶來的開銷了。

看一下數(shù)據(jù)結(jié)構(gòu)吧:

osq_lock如下:

  • osq加鎖有幾種情況:
    1. 無人持有鎖,那是最理想的狀態(tài),直接返回;
    2. 有人持有鎖,將當(dāng)前的Node加入到OSQ隊(duì)列中,在沒有高優(yōu)先級任務(wù)搶占時,自旋等待前驅(qū)節(jié)點(diǎn)釋放鎖;
    3. 自旋等待過程中,如果遇到高優(yōu)先級任務(wù)搶占,那么需要做的事情就是將之前加入到OSQ隊(duì)列中的當(dāng)前節(jié)點(diǎn),從OSQ隊(duì)列中移除,移除的過程又分為三個步驟,分別是處理prev前驅(qū)節(jié)點(diǎn)的next指針指向、當(dāng)前節(jié)點(diǎn)Node的next指針指向、以及將prev節(jié)點(diǎn)與next后繼節(jié)點(diǎn)連接;
  • 加鎖過程中使用了原子操作,來確保正確性;

osq_unlock如下:

  • 解鎖時也分為幾種情況:
    1. 無人爭用該鎖,那直接可以釋放鎖;
    2. 獲取當(dāng)前節(jié)點(diǎn)指向的下一個節(jié)點(diǎn),如果下一個節(jié)點(diǎn)不為NULL,則將下一個節(jié)點(diǎn)解鎖;
    3. 當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)為NULL,則調(diào)用osq_wait_next,來等待獲取下一個節(jié)點(diǎn),并在獲取成功后對下一個節(jié)點(diǎn)進(jìn)行解鎖;
  • 從解鎖的情況可以看出,這個過程相當(dāng)于鎖的傳遞,從上一個節(jié)點(diǎn)傳遞給下一個節(jié)點(diǎn);

在加鎖和解鎖的過程中,由于可能存在操作來更改osq隊(duì)列,因此都調(diào)用了osq_wait_next來獲取下一個確定的節(jié)點(diǎn):

3. mutex

3.1 數(shù)據(jù)結(jié)構(gòu)

終于來到了主題了,先看一下數(shù)據(jù)結(jié)構(gòu):

struct mutex {
	atomic_long_t		owner;           //原子計(jì)數(shù),用于指向鎖持有者的task struct結(jié)構(gòu)
	spinlock_t		wait_lock;              //自旋鎖,用于wait_list鏈表的保護(hù)操作
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	struct optimistic_spin_queue osq; /* Spinner MCS lock */        //osq鎖
#endif
	struct list_head	wait_list;          //鏈表,用于管理所有在該互斥鎖上睡眠的進(jìn)程
#ifdef CONFIG_DEBUG_MUTEXES
	void			*magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};

在使用mutex時,有以下幾點(diǎn)需要注意的:

  • 一次只能有一個進(jìn)程能持有互斥鎖;
  • 只有鎖的持有者能進(jìn)行解鎖操作;
  • 禁止多次解鎖操作;
  • 禁止遞歸加鎖操作;
  • mutex結(jié)構(gòu)只能通過API進(jìn)行初始化;
  • mutex結(jié)構(gòu)禁止通過memset或者拷貝來進(jìn)行初始化;
  • 已經(jīng)被持有的mutex鎖禁止被再次初始化;
  • mutex不允許在硬件或軟件上下文(tasklets, timer)中使用;

3.2 加鎖流程分析

mutex_lock加鎖來看一下大概的流程:

  • mutex_lock為了提高性能,分為三種路徑處理,優(yōu)先使用快速和中速路徑來處理,如果條件不滿足則會跳轉(zhuǎn)到慢速路徑來處理,慢速路徑中會進(jìn)行睡眠和調(diào)度,因此開銷也是最大的。

3.2.1 fast-path

  • 快速路徑是在__mutex_trylock_fast中實(shí)現(xiàn)的,該函數(shù)的實(shí)現(xiàn)也很簡單,直接調(diào)用atomic_long_cmpxchg_release(&lock->owner, 0UL, curr)函數(shù)來進(jìn)行判斷,如果lock->owner == 0表明鎖未被持有,將curr賦值給lock->owner標(biāo)識curr進(jìn)程持有該鎖,并直接返回;
  • lock->owner不等于0,表明鎖被持有,需要進(jìn)入下一個路徑來處理了;

3.2.2 mid-path

  • 中速路徑和慢速路徑的處理都是在__mutex_lock_common中實(shí)現(xiàn)的;
  • __mutex_lock_common的傳入?yún)?shù)為(lock, TASK_INTERRUPTIBLE, 0, NULL, _RET_IP_, false),該函數(shù)中很多路徑覆蓋不到,接下來的分析也會剔除掉無效代碼;

中速路徑的核心代碼如下:

  • 當(dāng)發(fā)現(xiàn)mutex鎖的持有者正在運(yùn)行(另一個CPU)時,可以不進(jìn)行睡眠調(diào)度,而可以選擇自選等待,當(dāng)鎖持有者正在運(yùn)行時,它很有可能很快會釋放鎖,這個就是樂觀自旋的原因;

  • 自旋等待的條件是持有鎖者正在臨界區(qū)運(yùn)行,自旋等待才有價值;

  • __mutex_trylock_or_owner函數(shù)用于嘗試獲取鎖,如果獲取失敗則返回鎖的持有者。互斥鎖的結(jié)構(gòu)體中owner字段,分為兩個部分:1)鎖持有者進(jìn)程的task_struct(由于L1_CACHE_BYTES對齊,低位比特沒有使用);2)MUTEX_FLAGS部分,也就是對應(yīng)低三位,如下:

    1. MUTEX_FLAG_WAITERS:比特0,標(biāo)識存在非空等待者鏈表,在解鎖的時候需要執(zhí)行喚醒操作;
    2. MUTEX_FLAG_HANDOFF:比特1,表明解鎖的時候需要將鎖傳遞給頂部的等待者;
    3. MUTEX_FLAG_PICKUP:比特2,表明鎖的交接準(zhǔn)備已經(jīng)做完了,可以等待被取走了;
  • mutex_optimistic_spin用于執(zhí)行樂觀自旋,理想的情況下鎖持有者執(zhí)行完釋放,當(dāng)前進(jìn)程就能很快的獲取到鎖。實(shí)際需要考慮,如果鎖的持有者如果在臨界區(qū)被調(diào)度出去了,task_struct->on_cpu == 0,那么需要結(jié)束自旋等待了,否則豈不是傻傻等待了。

    1. mutex_can_spin_on_owner:進(jìn)入自旋前檢查一下,如果當(dāng)前進(jìn)程需要調(diào)度,或者鎖的持有者已經(jīng)被調(diào)度出去了,那么直接就返回了,不需要做接下來的osq_lock/oqs_unlock工作了,節(jié)省一些額外的overhead;
    2. osq_lock用于確保只有一個等待者參與進(jìn)來自旋,防止大量的等待者蜂擁而至來獲取互斥鎖;
    3. for(;;)自旋過程中調(diào)用__mutex_trylock_or_owner來嘗試獲取鎖,獲取到后皆大歡喜,直接返回即可;
    4. mutex_spin_on_owner,判斷不滿足自旋等待的條件,那么返回,讓我們進(jìn)入慢速路徑吧,畢竟不能強(qiáng)求;

3.2.3 slow-path

慢速路徑的主要代碼流程如下:

  • for(;;)部分的流程可以看到,當(dāng)沒有獲取到鎖時,會調(diào)用schedule_preempt_disabled將本身的任務(wù)進(jìn)行切換出去,睡眠等待,這也是它慢的原因了;

3.3 釋放鎖流程分析

  • 釋放鎖的流程相對來說比較簡單,也分為快速路徑與慢速路徑,快速路徑只有在調(diào)試的時候打開;
  • 慢速路徑釋放鎖,針對三種不同的MUTEX_FLAG來進(jìn)行判斷處理,并最終喚醒等待在該鎖上的任務(wù);

參考

Generic Mutex Subsystem
MCS locks and qspinlocks

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
pthread的各種同步機(jī)制
c++11 atomic_flag實(shí)現(xiàn)高效的mutex
自旋鎖spinlock剖析與改進(jìn)
linux內(nèi)核部件分析(四)——更強(qiáng)的鏈表klist
關(guān)于JVM的Thin Lock, Fat Lock, SPIN Lock與Tasuki Lock - David.Turing's blog - BlogJava
并發(fā)編程的15條建議(譯)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服