1.併發介紹
一般來說,作業系統都是支援併發執行能力的,多個執行單元訪問同乙個模組時,如果不能支援併發,則會讓這個模組功能紊亂,像讀寫操作時。兩個使用者同時讀寫,那麼可能乙個使用者執行讀操作時,另乙個使用者可以執行了它的寫操作,這就會出現功能不協調的情況。因此這裡通過併發,將**放在臨界區,通過特定的互斥機制來對這一塊進行保護,使得多個執行單元訪問這塊時只能執行乙個,其餘都需要等待或其他。
2.併發處理方法
(1)中斷遮蔽
cpu一般都具備遮蔽中斷和開啟中斷的功能,這項功能可以保證正在執行的核心執行路徑不被中斷處理程式所搶占,防止某些競態條件的發生。具體而言,中斷遮蔽將使得中斷與程序之間的併發不再發生,而且,由於linux核心的程序排程等操作都依賴中斷來實現,核心搶占程序之間的併發也得以避免了。
local_irq_disable() /* 遮蔽中斷 */
. . .
critical section /* 臨界區*/
. . .
local_irq_enable() /* 開中斷*/
(2)自旋鎖
自旋鎖(spin lock)是一種典型的對臨界資源進行互斥訪問的手段,其名稱**於它的工作方式。為了獲得乙個自旋鎖,在某cpu上執行的**需先執行乙個原子操作,該操作測試並設定(test-and-set)某個記憶體變數
spinlock_t lock; /* 定義乙個自旋鎖*/
spin_lock_init(&lock); /* 初始化乙個自旋鎖*/
spin_lock (&lock) ; /* 獲取自旋鎖,保護臨界區 */
. . ./* 臨界區*/
spin_unlock (&lock) ; /* 解鎖*/
自旋鎖主要針對smp或單cpu但核心可搶占的情況,對於單cpu和核心不支援搶占的系統,自旋鎖退化為空操作
(3)訊號量
訊號量(semaphore)是作業系統中最典型的用於同步和互斥的手段,訊號量的值可以是0、1或者n。訊號量與作業系統中的經典概念pv操作對應。
p(s):①將訊號量s的值減1,即s=s-1;
②如果s≥0,則該程序繼續執行;否則該程序置為等待狀態,排入等待佇列。
v(s):①將訊號量s的值加1,即s=s+1;
②如果s>0,喚醒佇列中等待訊號量的程序。
linux中與訊號量相關的操作主要有下面幾種。
struct semaphore sem; /* 定義乙個訊號量 */
void sema_init(struct semaphore *sem, int val); //初始化型號量,並設定訊號量的值為val
/*獲得訊號量*/
void down(struct semaphore * sem); //獲得訊號量,它會導致睡眠,因此不能在中斷上下文中使用。
int down_interruptible(struct semaphore * sem); //獲得訊號量,進入睡眠狀態的程序能被訊號打斷
int down_trylock(struct semaphore * sem); /*該函式嘗試獲得訊號量sem,如果能夠立刻獲得,它就獲得該訊號量並返回0,否則,返回非0值。。它不會導致呼叫者睡眠,可以在中斷上下文中使用。*/
/*釋放訊號量*/
void up(struct semaphore * sem);
(4)互斥體
struct mutex my_mutex; //定義乙個互斥體
mutex_init(&my_mutex); //初始化互斥體
/*獲取互斥體*/
void mutex_lock(struct mutex *lock);
int mutex_lock_interruptible(struct mutex *lock);
int mutex_trylock(struct mutex *lock);
/*釋放互斥體*/
void mutex_unlock(struct mutex *lock);
mutex的使用方法和訊號量用於互斥的場合完全一樣:
eg:struct mutex my_mutex; /* 定義mutex */
mutex_init(&my_mutex); /* 初始化mutex */
mutex_lock(&my_mutex); /* 獲取mutex */
... /* 臨界資源*/
mutex_unlock(&my_mutex); /* 釋放mutex */
互斥體與自旋鎖區別:
互斥體和自旋鎖屬於不同層次的互斥手段,前者的實現依賴於後者。在互斥體本身的實現上,為了保證互斥體結構訪問的原子性,需要自旋鎖來互斥。所以自旋鎖屬於更底層的手段。
互斥體是程序級的,用於多個程序之間對資源的互斥,雖然也是在核心中,但是該核心執行路徑是以程序的身份,代表程序來爭奪資源的。如果競爭失敗,會發生程序上下文切換,當前程序進入睡眠狀態,cpu將執行其他程序。鑑於程序上下文切換的開銷也很大,因此,只有當程序占用資源時間較長時,用互斥體才是較好的選擇。
當所要保護的臨界區訪問時間比較短時,用自旋鎖是非常方便的,因為它可節省上下文切換的時間。
由此,可以總結出自旋鎖和互斥體選用的3項原則。
1)當鎖不能被獲取到時,使用互斥體的開銷是程序上下文切換時間,使用自旋鎖的開銷是等待獲取自旋鎖(由臨界區執行時間決定)。若臨界區比較小,宜使用自旋鎖,若臨界區很大,應使用互斥體。
2)互斥體所保護的臨界區可包含可能引起阻塞的**,而自旋鎖則絕對要避免用來保護包含這樣**的臨界區。因為阻塞意味著要進行程序的切換,如果程序被切換出去後,另乙個程序企圖獲取本自旋鎖,死鎖就會發生。
3)互斥體存在於程序上下文,因此,如果被保護的共享資源需要在中斷或軟中斷情況下使用,則在互斥體和自旋鎖之間只能選擇自旋鎖。當然,如果一定要使用互斥體,則只能通過mutex_trylock()方式進行,不能獲取就立即返回以避免阻塞。
(5)完成量(completion)
它用於乙個執行單元等待另乙個執行單元執行完某事。
struct completion my_completion; //定義名為my_completion的完成量
/* 初始化完成量*/
init_completion(&my_completion);
reinit_completion(&my_completion)
/*等待完成量*/
void wait_for_completion(struct completion *c);
/*喚醒完成量*/
linux 2 6核心程式設計 裝置驅動中的併發控制
test bingfa program include include include include include include include include int main printf 11111111111aaaaaaaaaaaaaaaaaaaaaaaaaaaaa n fd open...
Linux 裝置驅動併發控制簡述
答案 在linux中會遇到多個程序對共享資源的併發訪問,併發訪問會導致競態的發生,所以需要併發控制機制。併發與競態是指多個執行單位同時並行的被執行,而併發的執行單位對共享資源的訪問很容易導致競態 2.1 對稱多處理器 smp系統 的多個cpu 多個cpu共用同一條系統匯流排,因此可以訪問共同的外設和...
Linux 裝置驅動併發控制例項
在前面的筆記中,學習了linux裝置驅動中的併發控制,其中有中斷遮蔽 原子操作 自旋鎖 訊號量 互斥體及完成量。這幾種併發控制的技術就是解決多程序或多cpu之間對共享資源的同時訪問引起的競態問題。它們之間根據各自的性質使用在不同的場合中,這裡就不重複的介紹了,下面來分析 linux裝置驅動開發詳解 ...