<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文讀懂|內(nèi)核順序鎖

          共 3018字,需瀏覽 7分鐘

           ·

          2023-09-05 21:00

          Linux 內(nèi)核有非常多的鎖機(jī)制,如:自旋鎖、讀寫鎖、信號(hào)量和 RCU 鎖等。本文介紹一種和讀寫鎖比較相似的鎖機(jī)制:順序鎖(seqlock)。

          順序鎖與讀寫鎖一樣,都是針對(duì)多讀少寫且快速處理的鎖機(jī)制。而順序鎖和讀寫鎖的區(qū)別就在于:讀寫鎖的讀鎖會(huì)阻塞寫鎖,而順序鎖的讀鎖不會(huì)阻塞寫鎖。

          讀鎖原理

          為了讓讀鎖不阻塞寫鎖,讀鎖并不會(huì)真正進(jìn)行上鎖操作。那么讀鎖是如何避免在讀取臨界區(qū)數(shù)據(jù)時(shí),數(shù)據(jù)被其他進(jìn)程修改了?

          為了解決這個(gè)問題,順序鎖使用了一種類似于版本號(hào)的機(jī)制:序號(hào)。序號(hào)是一個(gè)只增不減的計(jì)數(shù)器,可以從順序鎖對(duì)象的定義看出,如下代碼所示:

          typedef struct {
               struct seqcount seqcount; // 序號(hào)
               spinlock_t lock;          // 自旋鎖,寫鎖上鎖時(shí)使用
          seqlock_t;

          在讀取臨界區(qū)數(shù)據(jù)前,首先需要調(diào)用 read_seqbegin() 函數(shù)來獲取讀鎖,read_seqbegin() 函數(shù)的核心邏輯是讀取順序鎖的序號(hào)。代碼如下所示:

          static inline unsigned read_seqbegin(const seqlock_t *sl)
          {
              unsigned ret;

          repeat:
              // 讀取順序鎖的序號(hào)
              ret = sl->sequence;

              // 如果序號(hào)是單數(shù),需要重新獲取
              if (unlikely(ret & 1)) {
                  ...
                  goto repeat;
              }
              ...
              return ret;
          }

          從上面的代碼可以看出,read_seqbegin() 函數(shù)只獲取順序鎖的序號(hào),并不會(huì)進(jìn)行上鎖操作,所以讀鎖并不會(huì)阻塞寫鎖。

          注意:序號(hào)是單數(shù)時(shí)需要重新獲取的原因,會(huì)在分析寫鎖實(shí)現(xiàn)原理時(shí)說明。

          既然讀鎖并不會(huì)進(jìn)行上鎖操作,如果在讀取臨界區(qū)數(shù)據(jù)時(shí),數(shù)據(jù)被修改了怎么辦呢?答案就是:在退出臨界區(qū)時(shí),比較一下當(dāng)前順序鎖的序號(hào)跟之前讀取的序號(hào)是否一致。如果一致表示數(shù)據(jù)沒有被修改,否則說明數(shù)據(jù)已經(jīng)被修改。如果數(shù)據(jù)被修改了,那么需要重新讀取臨界區(qū)的數(shù)據(jù)。

          比較序號(hào)是否一致可以使用 read_seqretry() 函數(shù),所以讀鎖的正確用法如下代碼所示:

          do {
              // 獲取順序鎖序號(hào)
              unsigned seq = read_seqbegin(&seqlock);
              // 讀取臨界區(qū)數(shù)據(jù)
              ...
          while (read_seqretry(&seqlock, seq)); // 對(duì)比序號(hào)是否一致?

          read_seqretry() 函數(shù)的實(shí)現(xiàn)非常簡單,如下所示:

          static inline unsigned 
          read_seqretry(const seqlock_t *sl, unsigned start)
          {
              ...
              return sl->sequence != start;
          }

          從上面代碼可以看出,read_seqretry() 函數(shù)只是簡單比較當(dāng)前序號(hào)與之前讀取到的序號(hào)是否一致。

          寫鎖原理

          從上面的分析可知,讀鎖是通過對(duì)比前后序號(hào)是否一致來判斷數(shù)據(jù)是否被修改的。那么序號(hào)在什么時(shí)候被修改呢?答案就是:獲取寫鎖時(shí)。

          獲取寫鎖是通過 write_seqlock() 函數(shù)來實(shí)現(xiàn)的,其實(shí)現(xiàn)也比較簡單,代碼如下所示:

          static inline void write_seqlock(seqlock_t *sl)
          {
              spin_lock(&sl->lock);

              sl->sequence++;
              ...
          }

          write_seqlock() 函數(shù)首先會(huì)獲取自旋鎖(所以寫鎖與寫鎖之間是互斥的),然后對(duì)序號(hào)進(jìn)行加一操作。所以,在修改臨界區(qū)數(shù)據(jù)前,寫鎖先會(huì)增加序號(hào)的值,這樣就會(huì)導(dǎo)致讀鎖前后兩次獲取的序號(hào)不一致。我們可以用下圖來說明這種情況:

          seqlock原理

          可以看出,當(dāng)在讀臨界區(qū)前后獲取的序號(hào)值不一致時(shí),就表示數(shù)據(jù)已經(jīng)被修改,這時(shí)就需要重新讀取被修改后的數(shù)據(jù)。

          寫鎖解鎖也很簡單,代碼如下:

          static inline void write_sequnlock(seqlock_t *sl)
          {
            ...
           s->sequence++;
           spin_unlock(&sl->lock);
          }

          解鎖也需要對(duì)序號(hào)進(jìn)行加一操作,然后釋放自旋鎖。

          由于 write_seqlock() 函數(shù)與 write_sequnlock() 函數(shù)都會(huì)對(duì)序號(hào)進(jìn)行加一操作,所以解鎖后,序號(hào)的值必定為雙數(shù)。

          我們在分析讀鎖時(shí)看到,如果序號(hào)是單數(shù)時(shí)會(huì)重新獲取序號(hào),直到序號(hào)為雙數(shù)為止。這是因?yàn)樾蛱?hào)單數(shù)時(shí),表示正在更新數(shù)據(jù)。此時(shí)讀取臨界區(qū)的值是沒有意義的,所以需要等到更新完畢再讀取。


          瀏覽 1941
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  特级大胆西西4444人体 | 国产黄色视频在线观看免费 | 婷婷好色五月天 | 国产精品大香蕉娱乐在线 | 日本免费看黄 |