整數運算
在執行 atomic 整數運算時,只能用使特殊的 atomic_t 資料型別上面。相對於直接使用 C 語言的整數型別而言,會這麼做是有理由的。首先,為了讓 atomic 函式只接受 atomic_t 型別,可以確保這類函式只能用於特定資料。同樣的,也可以避免這種資料傳給其它不是 atomic 函式來使用。其論點在,如果資料的使用過程中不能一致進行 atomic 操作的話,採用的資料又有什麼意義呢?再者,使用 atomic_t 型別資料可以確保編譯器不會自作聰明的進行最佳化的存取方式,對 atomic 運算而言,取得資料的正確位址,而不是使用位於暫存器中的分身的這一點,是相當重要的一件事情。最後,使用這個型別的資料,則可以隱藏在實作上面與平台相依的問題。
雖然在底層實作上面還是採用整數來運算,且在所有 Linux 支援的平台上面都有 32bit 的寬度限制,不過 Linux 曾為了 sparc 平台一度限定 atomic 的資料存取不能超過 24 bit。(容後再補)
atomic_t 的變數定義方式和一般沒什麼兩樣,我們也可以先為它作點初始化:
atomic_t v;
atomic_t u = ATOMIC_INIT(0);
運算的方式也很算單:
atomic_set(&v, 4); /* v = 4 */
atomic_add(2, &v); /* v=v+2 */
atomic_inc(&v); /* v=v+1 */
如果想將 atomic_t 型別轉換成為 int ,可以使用 atomic_read() 的函式。
printf("%d\n", atomic_read(&v));
Atomic 整數常用於計數器 (counter) 設計上面。用複雜的鎖定機制保護計數器過於無聊,所以核心成員採用了 atomic_inc () 與 atomic_dec() 等輕量級的函式,系統負荷也得以減低一些。
完整的函式列表可以參考: asm/atomic.h 這個檔案。
作用在位元上的 atomic
除了整數運算之外,Linux 核心也提供了一組運作在位元層次上的 atomic 運算函式。當然,這些函式都跟硬體架構無關,可以在 asm/bitops.h 底下找到相關的資訊。值得註記的是,這些函式的操作對象都是一般的記憶體位址,所使用的參數有兩個,一個是指標,另外一個則是以位元為單位的偏移量。以下是一個簡單的範例:
unsigned long word = 0;
set_bit (0, &word);
set_bit (1, &word);
printk ("%ul\n", word);
clear_bit (1, &word);
change_bit (0, &word);
註:當然,上面這些操作也有 non-atomic 的版本。可視個別需要來使用。
Spin Locks
要是每個關鍵區域的運算都是遞增運算的話,我們的工作可能就會輕鬆很多,可惜,現實往往不是這麼簡單;也因為這樣,所以我們需要一個同步運算的機制: locks。而在 Linux 核心當中,最常出現的同步鎖,就是 spin lock。這種鎖一次只能被一個執行緒所持有,如果某個執行緒嘗試爭奪 (content) 一個已經被其它執行緒所取走的 lock,它就會開始進行忙碌迴圈(kbusy loop)的情況,意同名稱中所提到的 spin 。這種空轉的機制可以確保在同一時間內只有一個執行緒可以拿得到這個鎖。了解到這點,我們必須先明白,在同步鎖的爭奪上面也同樣會導致執行緒的空轉,進而導致了系統資源上的浪費,這就是 spin lock 的本質,它只是一個輕量級的方案,長時間的執有動作,也是我們應該避免的行為。
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
if (spin_is_locked())
{
// 等待 sleep;
} else {
spin_lock(&mr_lock);
// do sth
spin_unlock(&mr_lock);
}
這一章還有許多有趣的小節沒有在本文裡面提到,我們稍後再回過頭來檢驗。
沒有留言:
張貼留言