《Linux核心設計與實現》學習【5】—— 核心同步
1 基本概念
臨界區:訪問和操作共享資料的程式碼段
競爭條件:多個執行執行緒處於同一臨界區中同時執行
同步:避免併發和防止競爭條件
2 造成併發執行的原因
(1)中斷:中斷隨時會發生,也就會隨時打斷當前執行的程式碼。
(2)軟中斷和tasklet:軟中斷和tasklet也會隨時被核心喚醒執行,也會像中斷一樣打斷正在執行的程式碼
(3)核心搶佔:核心具有搶佔性,發生搶佔時,如果搶佔的執行緒和被搶佔的執行緒在相同的臨界區,就產生了競爭條件
(4)睡眠及使用者空間的同步:使用者程式睡眠後,排程程式會喚醒一個新的使用者程式,新的使用者程式和睡眠的程式可能在同一個臨界區中
(5)對稱多處理:2個或多個處理器可以同時執行相同的程式碼
注意:要給資料加鎖而不是給程式碼加鎖。
3 死鎖
(1)產生原因
一個或多個執行執行緒和一個或多個資源,每個執行緒都在等待其中一個資源,但所有資源已經都被佔了。所有執行緒相互等待,但他們永遠不會釋放已經佔有的資源。
(2)避免死鎖
1)按順序加鎖,使用巢狀的鎖時,必須保證以相同的順序獲取鎖。
2)防止發生飢餓。即設定一個超時時間,防止一直等待下去。
3)不要重複請求同一個鎖。
4)設計應力求簡單。加鎖的方案越複雜就越容易出現死鎖。
4 核心同步方法
4.1 原子操作
atomic_t型別定義在<linux/types.h>
32位:atomic_t
64位:atomic64_t
4.2 自旋鎖
自旋鎖(spin lock),只能被一個執行緒持有,某個執行緒試圖獲取鎖,該執行緒會忙-旋轉-等待。
基本使用:
DEFINE_SPINLOCK(mr_lock);
spin_lock(&mr_lock);
//臨界區
spin_unlock(&mr_lock);
注意:
1)可用在中斷處理程式中
2)在獲取鎖前,首先禁止當前處理器上的本地中斷
4.3 讀寫鎖
多個讀任務可以併發地持有鎖,只能有一個任務可以持有寫鎖
使用
DEFINE_RWLOCK(mr_rwlock);
read_lock(&mr_rwlock);
/* 臨界區(只讀).... */
read_unlock(&mr_rwlock);
write_lock(&mr_lock);
/* 臨界區(讀寫)... */
write_unlock(&mr_lock);
4.4 訊號量
是一種睡眠鎖,當任務不能獲取訊號量時,睡眠。
適用於長時間持有的情況
兩種:計數訊號量、二值訊號量
/* 定義並宣告一個訊號量,名字為mr_sem,用於訊號量計數 */
static DECLARE_MUTEX(mr_sem);
/* 試圖獲取訊號量....*/
if(down_interruptible(&mr_sem)){
...
}
/* 臨界區 ... */
/* 釋放給定的訊號量 */
up(&mr_sem);
4.5 讀寫訊號量
與讀寫自旋鎖類似。
4.6 互斥體
類似二值訊號量。
相對訊號量,優先選擇互斥體。
自旋鎖和互斥量
需求 | 建議的加鎖方法 |
---|---|
低開銷加鎖 | 優先使用自旋鎖 |
短期鎖定 | 優先使用自旋鎖 |
長期加鎖 | 優先使用互斥體 |
中斷上下文中加鎖 | 使用自旋鎖 |
持有鎖需要睡眠 | 使用互斥體 |
4.7 完成變數
一個任務執行工作,另一個任務在完成變數上等待,類似訊號量。
方法
方法 | 描述 |
---|---|
init_completion(struct completion *) | 初始化指定的動態建立的完成變數 |
wait_for_completion(struct completion *) | 等待指定的完成變數接受訊號 |
complete(struct completion *) | 發訊號喚醒任何等待任務 |
4.8 BLK:大核心鎖
不鼓勵使用,部分程式碼中依然沿用。
特性:
1)持有BLK的任務可以睡眠
2)遞迴鎖
3)只可以用在程式上下文中
4.9 順序鎖
讀鎖被獲取時,寫鎖仍然可以被獲取。依靠一個序列計數器,當有疑義的資料被寫入時,序列值增加。
使用
//定義
seqlock_t mr_seq_lock = DEFINE_SEQLOCK(mr_seq_lock);
//寫
write_seqlock(&mr_seq_lock);
/*寫鎖被獲取*/
write_sequnlock(&mr_seq_lock);
//讀
unsigned long seq;
do
{
seq = read_seqbegin(&mr_seq_lock);
...
} while(read_seqretry(&mr_seq_lock, seq));
適用:資料存在多個讀者,寫者很少,希望寫優先於讀,資料簡單
4.10 禁止搶佔
對於SMP,某些情況不需要自旋鎖,但仍需要關閉核心搶佔。
preempt_disable()
/*搶佔被禁止*/
preempt_enable()
4.11 順序和屏障
目的:確保指令順序執行,不被處理器重排序
相關文章
- linux核心設計與實現Linux
- Linux核心之 核心同步Linux
- linux核心--使用核心佇列實現ringbufferLinux佇列
- Linux核心學習—— 1核心體系結構Linux
- 高效學習Linux核心——核心模組編譯Linux編譯
- Linux核心模組學習Linux
- Linux核心學習總覽Linux
- Linux核心學習筆記(5)– 程式排程概述Linux筆記
- 深入學習 Linux 核心模組Linux
- linux核心級同步機制--futexLinux
- Nginx配置和Linux核心引數的學習與驗證NginxLinux
- linux0.12 核心學習 (buffer.c)Linux
- Windows核心程式設計:第10章 同步裝置IO與非同步裝置IOWindows程式設計非同步
- Laravel核心程式碼學習--HTTP核心LaravelHTTP
- freeRTOS核心學習筆記(1)-程式設計標準筆記程式設計
- Windows核心驅動學習(六)程式碼注入與核心掛鉤Windows
- windows核心程式設計--核心物件Windows程式設計物件
- 《Linux核心完全註釋》學習筆記:2.7 Linux核心原始碼的目錄結構Linux筆記原始碼
- RedisSyncer同步引擎的設計與實現Redis
- 【Linux】核心學習筆記(一)——程序管理Linux筆記
- 【乾貨!!】十分鐘帶你搞懂 Java AQS 核心設計與實現!!!JavaAQS
- 剖析react核心設計原理--非同步執行排程React非同步
- Promise核心實現Promise
- 深度學習、強化學習核心技術實戰深度學習強化學習
- 核心實戰教程第五期 _ SQL 執行引擎的設計與實現SQL
- 《Linux網路開發必學教程》1_網路程式設計核心概念與模式Linux程式設計模式
- Redis設計與實現學習筆記(一)Redis筆記
- 深度學習DeepLearning核心技術實戰深度學習
- 《深度學習Python》核心技術實戰深度學習Python
- 認識linux核心(linux核心的作用)Linux
- linux 0.12 核心學習 (bitmap.c/ truncate.c)Linux
- 深度強化學習核心技術實戰強化學習
- 高效學習Linux核心——從原始碼中的宏下手Linux原始碼
- 精通C#學習筆記---C#核心程式設計結構C#筆記程式設計
- JVM核心學習筆記JVM筆記
- Linux系統程式設計【5】——stty的學習Linux程式設計
- Laravel核心程式碼學習 — 模型關聯底層程式碼實現Laravel模型
- Laravel核心程式碼學習 — Model增刪改查底層實現Laravel