在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux讀寫鎖邏輯解析—嘗試獲取寫鎖

冬至子 ? 來源:內核工匠 ? 作者:郭健Cojack ? 2023-12-04 11:12 ? 次閱讀

Rwsem的count成員還有一些bit用來標記當前讀寫鎖狀態(waiter bit和handoff bit),也需要根據情況進行調整:

1.jpg

A、如果等待隊列為空了,肯定是要清除waiter flag,同時要清除handoff flag,畢竟沒有什么等待任務可以遞交鎖了。

B、雖然隊列非空,但已經喚醒了reader,那么需要清除handoff標記,畢竟top waiter已經被喚醒去持鎖了,完成了鎖的遞交。

C、完成sem->count的調整

第二輪將喚醒的reader加入喚醒隊列,具體的邏輯如下:

1.jpg

主要是把等待任務對象的task成員設置為NULL,喚醒之后根據這個成員來判斷是正常喚醒還是異常喚醒路徑。

這里對喚醒等待隊列上的reader和writer處理是不一樣的。對于writer,喚醒之然后被調度到之后再去試圖持鎖。對于reader,在喚醒路徑上就已經持鎖(增加rwsem的reader count,并且修改了相關的狀態標記)。之所以這么做主要是降低調度的開銷,畢竟若干個reader線程被喚醒之后,獲得CPU資源再去持鎖,持鎖失敗然后繼續阻塞,這些都會增加調度的負載。

七、嘗試獲取寫鎖

和down_write不一樣,down_write_trylock只是嘗試獲取寫鎖,如果成功,那么自然是好的,直接返回1,如果失敗,也不會阻塞,只是返回0就可以了。代碼主邏輯在rwsem_write_trylock函數中,如下:

1.jpg

tmp的初始值設定為RWSEM_UNLOCKED_VALUE(0值),對于writer而言,只有rwsem是空鎖的時候才能進入臨界區。如果當前的sem->count等于0,那么給sem->count賦值RWSEM_WRITER_LOCKED,標記持鎖成功,并且把owner設定為當前task。

atomic_long_try_cmpxchg_acquire函數有三個參數,從左到右分別是value,old和new。該函數會對比value和old,如果相等那么執行賦值value=new同時返回true。如果不相等,不執行賦值操作,直接返回false。

八、獲取寫鎖

Writer獲取寫鎖的代碼主要在__down_write_common函數中,如下:

1.jpg

rwsem_write_trylock(快速路徑)上一節已經描述,我們主要看慢速路徑的邏輯(樂觀自旋我們下面會講,這里暫且略過):

1.jpg

首先準備好一個等待任務對象(棧上)并初始化,將其掛入等待隊列。在真正睡眠之前,我們需要做一些喚醒動作(和reader持鎖過程類似,有可能在掛入等待隊列的時候,臨界區線程恰好離開,變成空鎖),具體邏輯如下:

1.jpg

A、如果我們是等待隊列的top waiter(等待隊列從空變為非空),那么需要設定RWSEM_FLAG_WAITERS標記,直接進入后續阻塞邏輯。如果不是,那么邏輯要復雜點,需要掃描一下之前掛入隊列的任務,看看是否需要喚醒。

B、如果是writer持鎖,那么不需要任何喚醒動作,畢竟writer是排他的

C、如果是空鎖狀態,我們需要喚醒top waiter(RWSEM_WAKE_ANY,top writer或者reader們)。你可能會疑問:為何空鎖還要喚醒等待隊列的線程?當前線程快馬加鞭去持鎖不就OK了嗎?這主要是和handoff邏輯相關,這時候更應該持鎖的是等待隊列中設置了handoff的那個waiter,而不是當前writer。如果是reader在臨界區內,那么,我們將喚醒本等待隊列頭部的所有reader(RWSEM_WAKE_READERS)。

D、上面僅僅是標記喚醒者,這里的代碼段完成具體的喚醒動作

下面進入具體writer的阻塞過程:

1.jpg

A、調用rwsem_try_write_lock試圖持鎖,如果成功持鎖則退出循環,不再阻塞。有兩個邏輯路徑會路過這里。一個是線程持鎖失敗進入這里,另外一個是阻塞后被喚醒試圖持鎖。

B、有pending的信號,異常路徑退出

C、持鎖失敗但是設置了handoff,那么該線程對owner進行自旋等待,以便加快鎖的傳遞。

D、進入阻塞狀態

E、喚醒之后,重新試圖持鎖。Writer和reader不一樣,writer是喚醒之后自己再通過rwsem_try_write_lock試圖持鎖,而reader是在喚醒路徑上持鎖。

rwsem_try_write_lock代碼如下:

1.jpg

A、如果已經設置了handoff,并且自己不是top waiter(top waiter才是鎖要遞交的對象),返回false,持鎖失敗。如果是top waiter,那么就設置handoff_set,標記自己就是鎖遞交的目標任務。

B、如果當前rwsem已經有了owner,那么說明該鎖被偷走了。在適當的條件下(等待超時)設置handoff標記,防止后續繼續被搶。如果已經設置了handoff就不必重復設置了。

C、如果當前rwsem沒有owner,則持鎖成功,清除handoff標記并根據情況設置waiter標記。

D、通過原子操作來持鎖,成功操作后退出循環,否則是有其他線程插入,需要重復上面的邏輯。

1.jpg

至此我們要不獲取了鎖并清除了handoff bit(B邏輯塊),或者沒有獲取鎖,僅僅是設置了handoff bit(A邏輯塊)。

九、釋放寫鎖

除了清除了owner task成員,其他邏輯和釋放讀鎖類似,不再贅述。

十、樂觀自旋的條件

只有writer在進入慢速路徑的時候才會進行樂觀自旋,而rwsem_can_spin_on_owner函數用來判斷writer是否可以樂觀自旋:

1.jpg

A、本cpu上需要reschedule,還自旋個毛線,趕緊去睡眠也順便觸發一次調度

B、讀取sem->owner,標記部分保存在flags臨時變量中,任務指針保存在owner中

C、如果該rwsem已經禁止了對應的nonspinnable標志,那么肯定是不能樂觀自旋了。如果當前rwsem沒有禁止,那么需要看看owner的狀態。這里需要特別說明的是:為了方便debug,我們在釋放讀鎖的時候并不會清除owner task。也就是說,對于reader而言,owner中的task信息是最后進入臨界區的那個reader,僅此而已,實際這個task可能已經離開臨界區,甚至已經銷毀都有可能。所以,如果rwsem是reader擁有,那么其實判斷owner是否在cpu上運行是沒有意義的,因此owner是reader的話是允許進行樂觀自旋的(ret的缺省值是true),通過超時來控制自旋的退出。如果rwsem是writer擁有,那么owner的的確確是正在持鎖的線程,如果該線程沒有在CPU上運行(不能很快離開臨界區),那么也不能樂觀自旋。

十一、rwsem_spin_on_owner

函數rwsem_spin_on_owner的功能是對rwsem的owner task進行樂觀自旋(即不斷輪詢其狀態,僅writer有效),詳細的代碼邏輯如下:

1.jpg

A、在自旋之前,首先要獲得初始的狀態(owner task指針以及2-bit LSB flag),當這些狀態發生變化才好退出自旋。

B、rwsem_owner_state函數會根據當前的owner task和flag判斷當前的owner state。owner state的狀態總結如下:

1.jpg

只有明確的知道當前rwsem的owner是某個writer線程且沒有禁止自旋的時候才開啟下面的自旋過程。對于其他情況,例如reader owned的場景,我們不需要spin on owner,直接返回。

C、只要owner task或者flag其一發生變化,這里就會停止輪詢,同時也會返回當前的狀態,說明停止自旋的原因。例如當owner task(一定是writer)離開臨界區的時候會清空rwsem的owner域(owner task和flag會清零),這時候自旋的writer會停止自旋,到外層函數會去試圖持鎖。當然也有可能是其他自旋writer搶到了鎖,owner task從A切到B。無論那種情況,統一終止對owner的自旋。

D、如果當前cpu需要reschedule或者owner task沒有正在運行,那么也需要停止自旋

十二、Writer的樂觀自旋

和mutex的樂觀自旋的概念是類似的,想要進行rwsem的樂觀自旋,首先要獲取osq鎖,只有獲得了osq lock才能進入rwsem的樂觀自旋,否則自旋在per cpu的mcs lock上。Writer通過rwsem_optimistic_spin完成整個樂觀自旋的過程。對于writer owned場景,自旋發生在rwsem_spin_on_owner中,上一節已經描述了,這里我們主要看reader owned的情況,這時候通過for loop不斷自旋去持鎖:

1.jpg

2.jpg

A、對于rwsem,只有writer-owned場景能清楚的知道owner task是哪一個。因此,如果是writer-owned場景,會在rwsem_spin_on_owner函數進行自旋。對于非writer-owned場景(reader-owned場景或者禁止了樂觀自旋),在rwsem_spin_on_owner函數中會直接返回。從rwsem_spin_on_owner函數返回會給出owner state,如果需要退出樂觀自旋,那么這里break掉,自旋失敗,下面就準備掛入等待隊列了。

B、每次退出rwsem_spin_on_owner并且沒有要退出自旋的時候,都試著去獲取rwsem,如果持鎖成功那么退出樂觀自旋。

C、C和D是對reader-owned場景的處理。每次rwsem的owner state發生變化(從non-reader變成reader-owned狀態)時都會重新初始化 rspin_threshold。

D、Owner state沒有發生變化,那么當前試圖持鎖的writer可以進行樂觀自旋,但是需要有一個度,畢竟rwsem的臨界區內可能有多個reader線程,這有可能使得writer樂觀自旋很長時間。設置自旋門限閾值的公式是Spinning threshold = (10 + nr_readers/2)us,最大25us(30 reader)。一旦自旋超期,那么將調用rwsem_set_nonspinnable禁止樂觀自旋。

E、對于writer-owned場景,need_resched在函數rwsem_spin_on_owner中完成,對于reader-owned場景,也是需要檢查owner task所在cpu的resched情況。畢竟當前任務如果有調度需求,無論reader持鎖還是writer持鎖場景都要停止自旋。

F、在reader-owned場景中,由于無法判定臨界區reader們的執行狀態,因此rt線程的樂觀自旋需要更加的謹慎,畢竟有可能自旋的rt線程和臨界區的reader在一個CPU上從而導致活鎖現象。當然也不能禁止rt線程的自旋,畢竟在臨界區為空的情況下,rt自旋會有一定的收益的。允許rt線程自旋的場景有兩個:

a) lock owner正在釋放鎖,sem->owner被清除但是鎖還沒有釋放。

b) 鎖是空閑的并且sem->owner已清除,但是在我們嘗試獲取鎖之前另一個任務剛剛進入并獲取了鎖(例如一個自旋的writer先于我們進入臨界區)。

十三、關于handoff

1、設置handoff標記

設置handoff往往是發生在喚醒持鎖階段。對于等待隊列的writer,喚醒之后要調度執行后才去持鎖,這是一個長路徑,很可能被其他的write或者reader把鎖搶走。喚醒等待隊列中的reader們有點不一樣,在喚醒路徑上就會從這一組待喚醒的reader們選出一個代表(一般是top waiter)去持鎖,然后再一個個的喚醒。在這個reader代表線程持鎖的時候也有可能由于writer偷鎖而失敗(reader雖然也會偷鎖,但是偷鎖的reader也會喚醒等待隊列的reader們,完成top waiter未完成的工作)。

無論是reader還是writer,如果喚醒后持鎖失敗,并且等待時間已經超過了RWSEM_WAIT_TIMEOUT,這時候就會設置handoff bit,防止等待隊列的waiter餓死。具體設置handoff bit的場景如下:

1.jpg

2、清除handoff標記

標記了hand off之后,快速路徑、樂觀偷鎖(reader)、樂觀自旋(writer)都無法完成持鎖,鎖最終會遞交給top waiter的線程,完成持鎖。一旦完成持鎖,handoff標記就會被清除。具體清除handoff bit的場景包括:

1.jpg

3、確保鎖的所有權遞交給top waiter

1.jpg

十四、結論

標準linux內核的讀寫鎖是在公平性、吞吐量和延遲選擇了比較均衡的策略,這樣的策略在手機平臺上(特別是重載場景下)不能算是“優秀”,只能是合格吧。實際上,在手機用戶交互場景中,我們更期望是確保用戶體驗相關線程的持鎖時延,同時兼顧吞吐量。在這樣的背景下,OPPO內核團隊對linux中的讀寫鎖進行了優化,下一次有機會可以分享我們在讀寫鎖的持鎖時延方面做的改進。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • cpu
    cpu
    +關注

    關注

    68

    文章

    11011

    瀏覽量

    215242
  • Linux
    +關注

    關注

    87

    文章

    11420

    瀏覽量

    212359
  • 狀態機
    +關注

    關注

    2

    文章

    493

    瀏覽量

    27979
  • Spin
    +關注

    關注

    0

    文章

    4

    瀏覽量

    8095
收藏 人收藏

    評論

    相關推薦

    Linux下線程間通訊---讀寫和條件變量

    讀寫,它把對共享資源的訪問者劃分成讀者和者,讀者只對共享資源進行讀訪問,者則需要對共享資源進行操作。件變量是線程可用的一種同步機制,
    的頭像 發表于 08-26 20:44 ?1695次閱讀
    <b class='flag-5'>Linux</b>下線程間通訊---<b class='flag-5'>讀寫</b><b class='flag-5'>鎖</b>和條件變量

    Linux讀寫邏輯解析Linux為何會引入讀寫

    除了mutex,在linux內核中,還有一個經常用到的睡眠就是rw semaphore(后文簡稱為rwsem),它到底和mutex有什么不同呢?
    的頭像 發表于 12-04 11:04 ?1220次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>讀寫</b><b class='flag-5'>鎖</b><b class='flag-5'>邏輯</b><b class='flag-5'>解析</b>—<b class='flag-5'>Linux</b>為何會引入<b class='flag-5'>讀寫</b><b class='flag-5'>鎖</b>?

    FPGA代碼時,產生了存器有什么影響嗎

    經常看到各種HDL代碼時說要避免生成存器,但是在某些情況,我不關心那種情況即使它生成了存器,對我的工程實現也沒有什么影響啊,想請教下各位大神,既然這樣,為什么還要避免生成存器(
    發表于 01-08 23:54

    Lock體系結構和讀寫機制解析

    問題,JDK中還有另一套讀寫機制。讀寫中維護一個共享讀和一個排它
    發表于 01-05 17:53

    《有》/《無》/《簽約》/《解鎖》/《越獄》/《激活》專

    《有》/《無》/《簽約》/《解鎖》/《越獄》/《激活》專業技術詞解析 在討論區里,大家看到:《有版》,《無版》,《解
    發表于 02-03 11:05 ?1010次閱讀

    Linux 自旋spinlock

    背景 由于在多處理器環境中某些資源的有限性,有時需要互斥訪問(mutual exclusion),這時候就需要引入的概念,只有獲取的任務才能夠對資源進行訪問,由于多線程的核心是CPU的時間分片
    的頭像 發表于 09-11 14:36 ?2233次閱讀

    詳談Linux操作系統的三種狀態的讀寫

    讀寫是另一種實現線程間同步的方式。與互斥量類似,但讀寫將操作分為讀、兩種方式,可以多個線程同時占用讀模式的
    的頭像 發表于 09-27 14:57 ?3261次閱讀

    Linux中的傷害/等待互斥介紹

    序言:近期讀Linux 5.15的發布說明,該版本合并了實時機制,當開啟配置宏CONFIG_PREEMPT_RT的時候,這些被基于實時互斥的變體替代:mutex、ww_mutex
    的頭像 發表于 11-06 17:27 ?2824次閱讀

    使用Linux自旋實現互斥點燈

    自旋最多只能被一個可執行線程持有。如果一個線程試圖獲得一個已經被持有的自旋,那么該線程將循環等待,然后不斷的判斷是否能夠被成功獲取,直到獲取
    的頭像 發表于 04-13 15:09 ?909次閱讀
    使用<b class='flag-5'>Linux</b>自旋<b class='flag-5'>鎖</b>實現互斥點燈

    Linux實例:多線程和互斥到底該如何使用

    最近在多進程和Linux中的各種的文章,總覺得只有文字講解雖然能夠知道多進程和互斥是什么,但是還是不知道到底該怎么用。
    發表于 05-18 14:16 ?537次閱讀
    <b class='flag-5'>Linux</b>實例:多線程和互斥<b class='flag-5'>鎖</b>到底該如何使用

    Linux互斥的作用 互斥是什么

    。如果釋放互斥時有一個以上的線程阻塞,那么這些阻塞的線程會被喚醒,它們都會嘗試對互斥進行加鎖,當有一個線程成功對互斥鎖上鎖之后,其它線程就不能再次上鎖了,只能再次陷入阻塞,等待下一次解鎖。 初始化互斥
    的頭像 發表于 07-21 11:13 ?1119次閱讀

    自旋和互斥的區別有哪些

    之間的區別: 實現方式上的區別:互斥是基于自旋而實現的,所以自旋鎖相較于互斥更加底層; 開銷上的區別:獲取不到互斥
    的頭像 發表于 07-21 11:19 ?9756次閱讀

    讀寫的實現原理規則

    讀寫 互斥或自旋要么是加鎖狀態、要么是不加鎖狀態,而且一次只有一個線程可以對其加鎖。 讀寫
    的頭像 發表于 07-21 11:21 ?1130次閱讀
    <b class='flag-5'>讀寫</b><b class='flag-5'>鎖</b>的實現原理規則

    AQS獨占獲取

    AQS提供了兩種,獨占和共享。獨占只有一把,同一時間只允許一個線程獲得;而共享
    的頭像 發表于 10-13 14:51 ?604次閱讀
    AQS獨占<b class='flag-5'>鎖</b>的<b class='flag-5'>獲取</b>

    互斥和自旋的區別 自旋臨界區可以被中斷嗎?

    獲得了互斥時,其他線程如果要獲取,則必須等待直到該線程釋放。互斥的實現通常會利用操作系統提供的原子操作和線程調度機制。當某個線程
    的頭像 發表于 11-22 17:41 ?1045次閱讀
    主站蜘蛛池模板: 丁香六月五月婷婷 | 人人狠狠综合88综合久久 | 欧美日韩亚洲国内综合网俺 | 狠狠色欧美亚洲狠狠色www | 扒开双腿猛进入jk校视频 | 亚洲系列中文字幕一区二区 | 成人欧美一区二区三区的电影 | h网站亚洲 | 亚洲免费网站在线观看 | 天天操夜夜拍 | 成年大片免费播放视频人 | 天天干夜夜爱 | 色婷婷久久免费网站 | 国产亚洲精品久久久久久午夜 | 欧美另类色 | 久久综合欧美 | 手机看片福利盒子久久 | 国产在线成人一区二区 | 一级毛片真人免费观看 | 国产精品免费久久久久影院 | 久久青草国产精品一区 | 日韩a级毛片 | 国产午夜精品一区二区 | 福利视频一区二区三区 | 天天爽夜夜爽每晚高澡 | 性生生活三级视频在线观看 | 色成人免费网站 | 操干| 色香焦 | 亚洲欧美视频网站 | 久久福利青草精品资源站免费 | 国产综合在线视频 | 久久久久久久综合色一本 | 色狠狠网 | 欧洲色妇 | 综合网激情五月 | 天天爆操| 在线观看视频你懂的 | 成人综合网址 | 黄色一级片在线观看 | 日本免费网 |