refcount_t API 與 atomic_t 的比較¶
簡介¶
refcount_t API 的目標是為實現物件的引用計數器提供一個最小化的 API。雖然 lib/refcount.c 中通用的、與架構無關的實現底層使用了原子操作,但在記憶體排序保證方面,一些 refcount_*() 和 atomic_*() 函式之間存在一些差異。本文件概述了這些差異並提供了相應的示例,以幫助維護者根據這些記憶體排序保證的變化來驗證他們的程式碼。
本文件中使用的術語試圖遵循 tools/memory-model/Documentation/explanation.txt 中定義的正式 LKMM。
memory-barriers.txt 和 atomic_t.txt 提供了關於記憶體排序的一般背景知識,以及特別是關於原子操作的更多背景知識。
相關記憶體排序型別¶
注意
以下部分僅涵蓋了與原子操作和引用計數器相關並貫穿本文件使用的部分記憶體排序型別。要了解更廣泛的內容,請查閱 memory-barriers.txt 文件。
在沒有任何記憶體排序保證(即完全無序)的情況下,原子操作和引用計數器僅提供原子性以及(在同一 CPU 上的)程式順序 (po) 關係。它保證每個 atomic_*() 和 refcount_*() 操作都是原子的,並且指令在單個 CPU 上以程式順序執行。這透過使用 READ_ONCE()/WRITE_ONCE() 和比較-交換原語來實現。
強(完全)記憶體排序保證同一 CPU 上所有先前的載入和儲存(所有 po-早期指令)在同一 CPU 上執行任何 po-後續指令之前完成。它還保證同一 CPU 上所有 po-早期的儲存以及從其他 CPU 傳播的所有儲存必須在原始 CPU 上執行任何 po-後續指令之前傳播到所有其他 CPU(A-累積屬性)。這透過使用 smp_mb() 來實現。
釋放 (RELEASE) 記憶體排序保證同一 CPU 上所有先前的載入和儲存(所有 po-早期指令)在操作之前完成。它還保證同一 CPU 上所有 po-早期的儲存以及從其他 CPU 傳播的所有儲存必須在釋放操作之前傳播到所有其他 CPU(A-累積屬性)。這透過使用 smp_store_release() 來實現。
獲取 (ACQUIRE) 記憶體排序保證同一 CPU 上所有後續的載入和儲存(所有 po-後續指令)在獲取操作之後完成。它還保證同一 CPU 上所有 po-後續的儲存必須在獲取操作執行後傳播到所有其他 CPU。這透過使用 smp_acquire__after_ctrl_dep() 來實現。
引用計數器的控制依賴(成功時)保證,如果成功獲取了物件的引用(引用計數器遞增或添加發生,函式返回 true),則後續儲存與此操作有序。對儲存的控制依賴不是使用任何顯式屏障實現的,而是依賴 CPU 不對儲存進行推測。這只是一個單 CPU 關係,不為其他 CPU 提供任何保證。
函式比較¶
情況 1) - 非“讀/修改/寫” (RMW) 操作¶
函式變化
記憶體排序保證變化
無(兩者都完全無序)
情況 2) - 帶有釋放 (RELEASE) 排序的非“讀/修改/寫” (RMW) 操作¶
函式變化
記憶體排序保證變化
無(兩者都提供釋放 (RELEASE) 排序)
情況 3) - 不返回值的基於增量的操作¶
函式變化
記憶體排序保證變化
無(兩者都完全無序)
情況 4) - 不返回值的基於減量的 RMW 操作¶
函式變化
記憶體排序保證變化
完全無序 --> 釋放 (RELEASE) 排序
情況 5) - 返回值的基於增量的 RMW 操作¶
函式變化
無原子對應項 -->
refcount_add_not_zero()
記憶體排序保證變化
完全有序 --> 儲存成功時控制依賴
注意
我們在此確實假設,獲取物件指標會提供必要的排序!
情況 6) - 帶有獲取 (ACQUIRE) 排序且返回值的基於增量的 RMW 操作¶
函式變化
記憶體排序保證變化
完全有序 --> 成功時獲取 (ACQUIRE) 排序
情況 7) - 返回值的通用 dec/sub 基於減量的 RMW 操作¶
函式變化
記憶體排序保證變化
完全有序 --> 釋放 (RELEASE) 排序 + 成功時獲取 (ACQUIRE) 排序
情況 8) 返回值的其他基於減量的 RMW 操作¶
函式變化
無原子對應項 -->
refcount_dec_if_one()
atomic_add_unless(&var, -1, 1)-->refcount_dec_not_one(&var)
記憶體排序保證變化
完全有序 --> 釋放 (RELEASE) 排序 + 控制依賴
注意
atomic_add_unless() 僅在成功時提供完全有序。
情況 9) - 基於鎖的 RMW¶
函式變化
atomic_dec_and_lock() -->
refcount_dec_and_lock()
atomic_dec_and_mutex_lock()-->refcount_dec_and_mutex_lock()
記憶體排序保證變化
完全有序 --> 釋放 (RELEASE) 排序 + 控制依賴 + 成功時持有 spin_lock()