在閱讀linux核心**時,毫無疑問會遇到spin-lock,下面談談我對於spin-lock的arm原始碼分析。
首先看一下spinlock_t的結構
//arm/include/asm/spinlock_types.h
typedef struct tickets;
};} arch_spinlock_t;
//include/linux/spinlock_types.h
typedef struct raw_spinlock raw_spinlock_t;
//include/linux/spinlock_types.h
typedef struct spinlock ;
} spinlock_t;
上面**去除了一些debug模式時才有的成員。最終我們看到spinlock_t就是乙個4位元組的變數:乙個u32的slock或者兩個u16的next、owner,這裡使用聯合體以便在不同場景中以不同的模式進行訪問。在普通鎖模式時,使用的是slock,在讀寫鎖模式時,使用的是next、owner。
下面來看鎖的初始化。
//arm/include/asm/spinlock_types.h
#define __arch_spin_lock_unlocked }
//include/linux/spinlock_types.h
#define __raw_spin_lock_initializer(lockname) \
#define __raw_spin_lock_unlocked(lockname) \
(raw_spinlock_t) __raw_spin_lock_initializer(lockname)
//include/linux/spinlock.h
# define raw_spin_lock_init(lock) \
do while (0)
#endif
#define spin_lock_init(_lock) \
do while (0)
可以看到,鎖的初始化就是將其4個位元組置0.
下面來看上鎖的過程
//include/linux/spinlock.h
static inline int do_raw_spin_trylock(raw_spinlock_t *lock)
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
#define lock_contended(_lock, try, lock) \
do \
lock_acquired(&(_lock)->dep_map, _ret_ip_); \//展開後為空
} while (0)
//include/linux/spinlock_api_smp.h
static inline void __raw_spin_lock(raw_spinlock_t *lock)
//kernel/locking/spinlock.c
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
export_symbol(_raw_spin_lock);
//include/linux/spinlock.h
#define raw_spin_lock(lock) _raw_spin_lock(lock)
static inline void spin_lock(spinlock_t *lock)
可以看到,上鎖的第一步就是關閉核心搶占,以免在上鎖過程中當前cpu核心執行別的**,就是避免該cpu核心執行緒排程產生同步問題。 下一步是開始嘗試獲取鎖,如果嘗試獲取鎖不成功,將執行正式獲取鎖邏輯。arch_spin_lock()和arch_spin_trylock()是體系結構相關的函式。下面來看一下arm架構上的實現。
//arm/include/asm/spinlock.h
static inline int arch_spin_trylock(arch_spinlock_t *lock)
while (res);
if (!contended) else
}
實現跟體系結構有關,需要執行特定體系的彙編**。
**邏輯為:將lock的值迴圈右移16位後減去原來的自己,也就是判斷低16位是否等於高16位。如果相等,將lock的值加上0x10000(也就是高16位加1)寫回lock,寫回成功就返回1,寫回不成功則重試一次.如果不相等則返回0。
static inline void arch_spin_lock(arch_spinlock_t *lock)
smp_mb();
}
**邏輯為:將lock的值加上0x10000(也就是高16位加1)寫回lock,直到成功。然後迴圈判斷舊的lock值(未加1前)的高16位是否和當前的lock的值的低16位相等,如果不相等就進入低功耗狀態,迴圈直到相等。
可以看到,核心自旋鎖是通過特定於體系結構的同步原語來實現的。獲取鎖時,如果高16位和低16位相等,表明可以獲取鎖,將高16位原子加1,表明占有鎖;如果不相等,表明其他cpu核占有該鎖,則將高16位加1後該處理核心進入低功耗模式;等待其他處理核心喚醒。
注意這裡已經關閉核心搶占,不存在該處理核心的其他執行緒搶占該鎖的情況,只可能是其他處理核心來獲取該鎖,使用16位數加1來判斷是否獲得鎖,在65536個處理核心內的系統中都能正常工作。
下面來看一下解鎖的過程
//include/linux/spinlock.h
static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
//include/linux/spinlock_api_smp.h
static inline void __raw_spin_unlock(raw_spinlock_t *lock)
//kernel/locking/spinlock.c
void __lockfunc _raw_spin_unlock(raw_spinlock_t *lock)
export_symbol(_raw_spin_unlock);
//include/linux/spinlock.h
#define raw_spin_unlock(lock) _raw_spin_unlock(lock)
static inline void spin_unlock(spinlock_t *lock)
//arm/include/asm/spinlock.h
static inline void arch_spin_unlock(arch_spinlock_t *lock)
可以看到比較簡單,只是將owner成員也就是低16位加1,然後喚醒其他處理核心就可以了。
下面附加介紹一下arm架構同步原語,至於上面用到的記憶體屏障,後面再單獨寫一篇討論。
arm架構使用兩條指令來實現同步原語,分別是ldrex和strex。這些指令操作與cpu中的乙個叫位址監視器協同工作,監視器提供了記憶體訪問的機器狀態和相關系統控制。根據記憶體是否具有可共享或不可共享記憶體屬性,存在兩種不同的監控模型。
在處理核心的非共享記憶體中:
執行ldrex指令會導致:
1)執行核心標記對應的實體地址為獨佔訪問狀態。影響的實體地址大小由實現定義。
2)該核心的本地監視器轉為獨佔訪問狀態。
執行strex指令會導致:
1)如果該核心的本地監視器處於獨佔訪問狀態。並且儲存的位址和本地監視器監視的位址相同,則儲存成功,rd的值為0,否則由實現定義是否儲存失敗,如果失敗rd暫存器的值為1。本地監視器轉為開放訪問狀態。
2)如果該核心的本地監視器處於開放訪問狀態,儲存必然失敗,rd的值為1,本地監視器保持開放訪問狀態。
另外,如果使用非獨佔儲存指令來將值儲存到乙個位址,該位址沒有標記為獨佔訪問狀態但本地狀態監視器為獨佔狀態,其結果由具體實現定義。如果該位址標記為來獨佔訪問狀態,其結果也由具體實現定義。
在處理器共享記憶體中:
執行ldrex指令會導致:
1)被載入的位址標記為獨佔訪問狀態,並且其他的該處理核心載入並標記的位址的獨佔訪問狀態將被清楚。即同時只有一處位址被乙個處理核心載入而標記。
2)該處理核心的本地監視器轉為獨佔訪問狀態。
3)全域性監視器轉為獨佔訪問狀態。
執行strex指令時會導致:
1)如果滿足這兩個條件,儲存操作會保證成功:1、儲存的位址被標記為獨佔訪問狀態。2、該處理核心的全域性監視器和本地監視器都為獨佔模式。這種情況下,rd暫存器返回0。該核心的全域性監視器的狀態可能變為開放訪問模式。如果該位址被其他核心的全域性監視器標記為獨佔訪問狀態,則該核心的全域性監視器保證轉為開放訪問模式。
2)如果該處理核心沒有標記任何位址,儲存操作將會失敗。這時,rd暫存器返回1,該核心的全域性監視器保持開放訪問狀態。
3)如果該處理核心之前標記獨佔訪問的位址和儲存位址不一樣,儲存是否成功依賴具體實現。這是,如果成功,rd為0,如果失敗rd為1。該處理核心的全域性監視器的狀態也由具體實現來定義。
核心 自旋鎖
自旋鎖用於多處理器環境下保護資料。如果核心發現資料未鎖,就獲取鎖並執行 如果資料被鎖,就一直旋轉 反覆執行一條指令 自旋鎖在單處理器環境下 非搶占式核心 下,不起作用 單處理器搶占式核心的情況下,自旋鎖起到禁止搶占的作用。注釋 核心搶占 可搶占式核心 即當程序位於核心空間時,有乙個更高優先順序的任務...
核心同步機制之自旋鎖 讀 寫鎖
自旋鎖 spin lock 是用來在多處理器環境中工作的一種特殊的鎖。如果核心控制路徑發現自旋鎖 開著 就獲取鎖並繼續自己的執行。相反,如果核心控制路徑發現由執行在另乙個cpu上的核心控制路徑 鎖著 就在一直迴圈等待,反覆執行一條緊湊的迴圈指令,直到鎖被釋放。自旋鎖與互斥鎖有點類似,只是自旋鎖不會引...
核心與驅動 03 自旋鎖
使用佇列自旋鎖提高效能 使用如下 可以初始化乙個自旋鎖 kspin lock spinlock keinitlizespinlock spinlock 這個函式無返回值 要注意,多執行緒使用自旋鎖同步時 定義的自選鎖必須是乙個全域性的 不然是沒有任何意義的 kspin lock g spinlock...