隨着硬件的發展,SMP(對稱多處理器)已經很廣泛,若是內核的調度機制是可搶佔的,那麼SMP和內核搶佔是多線程執行的兩種場景。當多個線程同時訪問內核的數據結構時,咱們就須要對其作串行化處理。linux
自旋鎖和互斥體數據結構
訪問共享資源的代碼區稱爲臨界區。自旋鎖(spinlock)和互斥體(mutex, mutual exclusion)是保護內核臨界區的兩種基本機制。多線程
自旋鎖能夠肯定在同時只有一個線程進入臨界區。其餘想進入臨界區域的線程(執行線程)必須不停的在原地打轉,知道第一個線程釋放的自旋鎖。下面是spinlock的用法併發
#include <linux/spinlock.h> /*初始化*/ spinlock_t _lock = SPIN_LOCK_UNLOCKED; /*上鎖*/ spin_lock(&_lock); /*臨界區代碼*/ /*釋放鎖*/ spin_unlock(&_lock);互斥體與自旋鎖不同,當在臨界區外等待時,它不會不停的打轉,而是使當前的線程進入睡眠狀態。若是執行臨界區須要很長的時間,那麼互斥體更適合自旋鎖,這樣但是更好的利用cpu,而不是長時間在哪裏打轉。
那對於這兩個怎麼取捨?函數
1.若是臨界區須要睡眠,只能使用互斥體,由於在得到自旋鎖後進行調度、搶佔以及在等待隊列上睡眠都是非法的。spa
2.因爲互斥體會在面臨競爭的狀況下將當前的線程置於睡眠狀態,所以,在中斷處理函數中只能使用自旋鎖。線程
下面是互斥體的使用方法code
#include <linux/mutex.h> static DEFINE_MUTEX(_mutex); mutex_lock(&_mutex); /* 臨界區*/ mutex_unlock(&_mutex)
1.進程上下文,單cpu,非搶佔式內核隊列
這種是最簡單的,不須要加鎖進程
2.進程和中斷上下文,單cpu,非搶佔式內核
這種狀況下,在保護臨界區的時候,僅僅只須要對中斷禁止。單cpu和非搶佔式內核決定進程不會切換,但是在臨界區時,進程可能被中斷打斷,因此須要禁止中斷,也就是在進入臨界區時保存中斷狀態,離開臨界區後恢復中斷狀態。
3.進程和中斷上下文,單cpu,搶佔式內核
這種狀況與2相比,內核可搶佔,那麼就須要對臨界區加鎖來禁止內核的搶佔,中斷仍是和2同樣。
4.進程和中斷上下文,SMP,搶佔式內核
如今執行在SMP機器上,並且你的內核配置了CONFIG_SMP和CONFIG_PREEMPT。在SMP系統中,得到自旋鎖時,僅僅本CPU的中斷被禁止,而後進程上下文和中斷處理函數能夠在不一樣的CPU上進行,因此非本CPU的中斷處理函數須要等待本CPU上的進程上下文代碼推出臨界區。中斷上下文中須要使用spin_lock和spin_unlock。
原子操做
原子操做用於執行輕量級的,僅執行一次的操做,例如修改定時器、有條件的增長值,設置位等。原子操做但是實現操做的串行化,也就不準要加鎖處理。原子操做的時間取決了體系結構。
讀寫鎖
自旋鎖的變體--讀寫鎖。若是每一個執行單元在訪問臨界區的時候要麼是讀要麼是寫數據結構,可是他們都不會同時進行讀寫操做,那麼讀寫鎖很適合。讀寫鎖容許同一時候多個線程進入臨界區,但若是一個寫線程進入臨界區,那麼其餘的讀寫進程都必須等待。讀寫鎖使用方法:
rwlock_t _rwlock = RW_LOCK_UNLOCKED; read_lock(&_rwlock); /* 臨界區代碼*/ read_unlock(&_rwlock); -------------------------------------- rwlock_t _rwlock = RW_LOCK_UNLOCKED; wirte_lock(&_rwlock); /* 臨界區代碼*/ wirte_unlock(&_rwlock);