国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

Linux內核分析(七)----并發與競態

2024-06-28 13:22:00
字體:
來源:轉載
供稿:網友
linux內核分析(七)----并發與競態

Linux內核分析(七)

這兩天家里的事好多,我們今天繼續接著上一次的內容學習,上次我們完善了字符設備控制方法,并深入分析了系統調用的實質,今天我們主要來了解一下并發和競態。

今天我們會分析到以下內容:

1. 并發和競態簡介

2. 競態解決辦法

3. 為我們的虛擬設備增加并發控制

在前幾次博文我們已經實現了簡單的字符設備,看似完美但我們忽視了一個很嚴重的問題,即并發問題,那么什么是并發,又如何解決并發呢,我們下面進行分析。

l 并發和競態簡介

1. 并發與競態概念

1. 何為并發:并發是指多個執行單元同時、并行被執行。

2. 何為競態:并發的執行單元對共享資源(硬件資源和軟件上的全局變量,靜態變量等)的訪問容易發生競態。

3. 我們虛擬設備的缺陷:對于我們前期的虛擬設備驅動個,假設一個執行單元A對其寫入300個字符‘a’,而另一個執行單元B對其寫入300個字符‘b’,第三個執行單元讀取所有字符。如果A、B被順序執行那么C讀出的則不會出錯,但如果A、B并發執行,那結果則是我們不可料想的。

2. 競態發生的情況

1. 對稱多處理器(SMP)的多個CPUSMP是一種緊耦合、共享存儲的系統模型,它的特點是多個CPU使用共同的系統總線,因此可以訪問共同的外設和存儲器。

2. CPU內進程與搶占它的進程:2.6的內核支持搶占調度,一個進程在內核執行的時候可能被另一高優先級進程打斷。

3. 中斷(硬中斷、軟中斷、tasklet、低半部)與進程之間:中斷可以打斷正在執行的進程,處理中斷的程序和被打斷的進程間也可能發生競態。

3. 競態的解決辦法

解決競態問題的途徑是保證對共享資源的互斥訪問。訪問共享資源的代碼區域稱為臨界區,臨界區要互斥機制保護。Linux設備驅動中常見的互斥機制有以下方式:中斷屏蔽、原子操作、自旋鎖和信號量等。

l 競態解決辦法

上面我們已經分析了競態產生的原因、發生的情況以及解決辦法,下面我們對常見的解決辦法一一分析。

1. 中斷屏蔽

1. 基本概念:在單CPU中避免競態的一種簡單方法是在進入臨界區之前屏蔽系統的中斷。由于linux的異步I/O、進程調度等很多內容都依靠中斷,所以我們應該盡快的執行完臨界區的代碼,換句話就是臨界區代碼應該盡量少。

2. 具體操作:linux內核提供了下面具體方法

Local_irq_disable();//屏蔽中斷

Local_irq_enable();//打開中斷

Local_irq_save(flags);//禁止中斷并保存當前cpu的中斷位信息

2. 原子操作

1. 基本概念:原子操作指在執行過程中不會被別的代碼中斷的操作。

2. 具體操作:linux內核提供了一系列的函數來實現內核中的原子操作,這些操作分為兩類,一類是整型原子操作,另一類是位原子操作,其都依賴底層CPU的原子操作實現,所以這些函數與CPU架構有密切關系。

1) 整型原子操作

a) 設置原子變量的值

atomic_t v = ATOMIC_INIT(0);//定義原子變量v并初始化為0

void atomic_set(atomic_t *v, int i);//設置原子變量值為i

b) 獲取原子變量的值

atomic_read(atomic_t *v);//返回原子變量v的值

c) 原子變量加、減操作

void atomic_add(int i, atomic_t *v);//原子變量v增加i

void atomic_sub(int I, atomic_t *v);//原子變量v減少i

d) 原子變量自增、自減

void atomic_inc(atomic_t *v);//原子變量v自增1

void atomic_dec(atomic_t *v);//原子變量v自減1

e) 操作并測試

int atomic_inc_and_test(atomic_t *v);

int atomic_dec_and_test(atomic_t *v);

int atomic_sub_and_test(int i,atomic_t *v);

/*上述三個函數對原子變量v自增、自減和減操作(沒有加)后測試其是否為0,如果為0返回true,否則返回false*/

f) 操作并返回

int atomic_add_return(int i,atomic_t *v);

int atomic_sub_return(int i,atomic_t *v);

int atomic_inc_return(atomic_t *v);

int atomic_dec_return(atomic_t *v);

/*上述函數對原子變量v進行自增、自減、加、減操作,并返回新的值*/

2) 位原子操作

a) 設置位

void set_bit(nr,void *addr);//設置addr地址的第nr位,即向該位寫入1。

b) 清除位

void clear_bit(nr,void *addr);//清除addr地址的第nr位,即向該位寫入0。

c) 改變位

void change_bit(nr,void *addr);//對addr地址的第nr取反

d) 測試位

int test_bit(nr,void *addr);//返回addr地址的第nr位

e) 測試并操作位

int test_and_set_bit(nr,void *addr);

int test_and_clear_bit(nr,void *addr);

int test_and_change_bit(nr,void *addr);

/*上述函數等同于執行test_bit后,再執行xxx_bit函數*/

3. 自旋鎖

1. 基本概念:自旋鎖是一種對臨界資源進行互斥訪問的手段。

2. 工作原理:為獲得自旋鎖,在某CPU上運行的代碼需先執行一個原子操作,該操作測試并設置某個內存變量,由于其為原子操作,所以在該操作完成之前其他執行單元不可能訪問這個內存變量,如果測試結果表明已經空閑,則程序獲得這個自旋鎖并繼續執行,如果測試結果表明該鎖仍被占用,程序將在一個小的循環內重復這個“測試并設置”操作,即進行所謂的“自旋”,通俗的說就是在“原地打轉”。

3. 具體操作:linux內核中與自旋鎖相關的操作主要有:

1) 定義自旋鎖

spinlock_t lock;

2) 初始自旋鎖

spin_lock_init(lock);

3) 獲得自旋鎖

spin_lock(lock);//獲得自旋鎖lock

spin_trylock(lock);//嘗試獲取lock如果不能獲得鎖,返回假值,不在原地打轉。

4) 釋放自旋鎖

spin_unlock(lock);//釋放自旋鎖

為保證我們執行臨界區代碼的時候不被中斷等影響我們的自旋鎖又衍生了下面的內容

5) 自旋鎖衍生

spin_lock_irq() = spin_lock() + local_irq_disable()

spin_unlock_irq() = spin_unlock() + local_irq_enable()

spin_lock_irqsave() = spin_lock() + local_irq_save()

spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()

spin_lock_bh() = spin_lock() + local_bh_disable()

spin_unlock_bh() = spin_unlock() + local_bh_disable()

4. 使用注意事項:

1) 自旋鎖實質是忙等鎖,因此在占用鎖時間極短的情況下,使用鎖才是合理的,反之則會影響系統性能。

2) 自旋鎖可能導致系統死鎖。

3) 自旋鎖鎖定期間不能調用可能引起進程調度的函數。

4. 讀寫自旋鎖

1. 基本概念:為解決自旋鎖中不能允許多個單元并發讀的操作,衍生出了讀寫自旋鎖,其不允許寫操作并發,但允許讀操作并發。

2. 具體操作:linux內核中與讀寫自旋鎖相關的操作主要有:

1) 定義和初始化讀寫自旋鎖

rwlock_t my_rwlock = RW_LOCK_UNLOCKED;//靜態初始化

rwlock_t my_rwlock;

rwlock_init(&my_rwlock);//動態初始化

2) 讀鎖定

read_lock();

read_lock_irqsave();

read_lock_irq();

read_lock_bh();

3) 讀解鎖

read_unlock();

read_unlock_irqrestore();

read_unlock_irq();

read_unlock_bh();

4) 寫鎖定

write_lock();

write_lock_irqsave();

write_lock_irq();

write_lock_bh();

write_trylock();

5) 寫解鎖

write_unlock();

write_unlock_irqrestore();

write_unlock_irq();

write_unlock_bh();

5. 順序鎖

1. 基本概念:順序鎖是對讀寫鎖的一種優化,如果使用順序鎖,讀執行單元在寫執行單元對被順序鎖保護的共享資源進行寫操作時仍然可以繼續讀,不必等待寫執行單元的完成,寫執行單元也不需等待讀執行單元完成在進行寫操作。

2. 注意事項:順序鎖保護的共享資源不含有指針,因為在寫執行單元可能使得指針失效,但讀執行單元如果此時訪問該指針,將導致oops。

3. 具體操作:linux內核中與順序鎖相關的操作主要有:

1) 寫執行單元獲得順序鎖

write_seqlock();

write_tryseqlock();

write_seqlock_irqsave();

write_seqlock_irq();

write_seqlock_bh();

2) 寫執行單元釋放順序鎖

write_sequnlock();

write_sequnlock_irqrestore();

write_sequnlock_irq();

write_sequnlock_bh();

3) 讀執行單元開始

read_seqbegin();

read_seqbegin_irqsave();//local_irq_save + read_seqbegin

4) 讀執行單元重讀

read_seqretry ();

read_seqretry_irqrestore ();

6. RCU(讀拷貝更新)

1. 基本概念:RCU可以看做是讀寫鎖的高性能版本,相比讀寫鎖,RCU的優點在于即允許多個讀執行單元同時訪問被保護數據,又允許多個讀執行單元和多個寫執行單元同時訪問被保護的數據。

2. 注意事項:RCU不能代替讀寫鎖。

3. 具體操作:linux內核中與RCU相關的操作主要有:

1) 讀鎖定

rcu_read_lock ();

rcu_read_lock_bh ();

2) 讀解鎖

rcu_read_unlock ();

rcu_read_unlock_bh ();

3) 同步RCU

synchronize_rcu ();//由RCU寫執行單元調用

synchronize_sched();//可以保證中斷處理函數處理完畢,不能保證軟中斷處理結束

4) 掛接回調

call_rcu ();

call_rcu_bh ();

有關RCU的操作還有很多,大家可以參考網絡。

7. 信號量

1. 基本概念:信號量用于保護臨界區的常用方法與自旋鎖類似,但不同的是當獲取不到信號量時,進程不會原地打轉而是進入休眠等待狀態。

2. 具體操作:linux內核中與信號量相關的操作主要有:

1) 定義信號量

Struct semaphore sem;

2) 初始化信號量

void sema_init(struct semaphore *sem, int val);//初始化sem為val,當然還有系統定義的其他宏初始化,這里不列舉

3) 獲得信號量

void down(struct semaphore *sem);//獲得信號量sem,其會導致睡眠,并不能被信號打斷

int down_interruptible(struct semaphore *sem);//進入睡眠可以被信號打斷

int down_trylock(struct semaphore *sem);//不會睡眠

4) 釋放信號量

  void up(struct semaphore *sem);//釋放信號量,喚醒等待進程

注:當信號量被初始為0時,其可以用于同步。

8. Completion用于同步

1. 基本概念:linux中的同步機制。

2. 具體操作:linux內核中與Completion相關的操作主要有:

1) 定義Completion

struct completion *my_completion;

2) 初始化Completion

void init_completion(struct completion *x);

3) 等待Completion

void wait_for_completion(struct completion *);

4) 喚醒Completion

  void complete(struct completion *);//喚醒一個

  void complete_all(struct completion *);//喚醒該Completion的所有執行單元

9. 讀寫信號量

1. 基本概念:與自旋鎖和讀寫自旋鎖的關系類似

2. 具體操作:linux內核中與讀寫信號量相關的操作主要有:

1) 定義和初始化讀寫自旋鎖

struct rw_semaphore sem;

init_rwsem(&sem);

2) 讀信號量獲取

down_read ();

down_read_trylock();

3) 讀信號量釋放

up_read ();

4) 寫信號量獲取

down_write ();

down_write_trylock ();

5) 寫信號量釋放

up_write();

10. 互斥體

1. 基本概念:用來實現互斥操作

2. 具體操作:linux內核中與互斥體相關的操作主要有:

1) 定義和初始化互斥體

struct mutex lock;

mutex_init(&lock);

2) 獲取互斥體

void mutex_lock(struct mutex *lock);

int mutex_lock_interruptible(struct mutex *lock);

int mutex_lock_killable(struct mutex *lock);

3) 釋放互斥體

void mutex_unlock(struct mutex *lock);

上面我們介紹了linux內核中為了解決競態所提供的方法,我們下面使用信號量為我們的虛擬設備增加并發控制。

l 為我們的虛擬設備增加并發控制

我們增加了并發控制后的代碼如下,詳細代碼參考https://github.com/wrjvszq/myblongs

 1 struct mem_dev{ 2     struct cdev cdev; 3     int mem[MEM_SIZE];//全局內存4k 4     dev_t devno; 5     struct semaphore sem;//并發控制所使用的信號量 6 }; 7 static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos){ 8     unsigned long p = *ppos; 9     unsigned int count = size;10     int ret = 0;11     int *pbase = filp -> PRivate_data;12 13     if(p >= MEM_SIZE)14         return 0;15     if(count > MEM_SIZE - p)16         count = MEM_SIZE - p;17 18     if(down_interruptible(&my_dev.sem))//獲取信號量19         return - ERESTARTSYS;20 21     if(copy_from_user(pbase + p,buf,count)){22        ret = - EFAULT;23     }else{24         *ppos += count;25         ret = count;26     }27 28     up(&my_dev.sem);//釋放信號量29 30     return ret;31 }32 static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){33     int * pbase = filp -> private_data;/*獲取數據地址*/34     unsigned long p = *ppos;/*讀的偏移*/35     unsigned int count = size;/*讀數據的大小*/36     int ret = 0;37 38     if(p >= MEM_SIZE)/*合法性判斷*/39         return 0;40     if(count > MEM_SIZE - p)/*讀取大小修正*/41         count = MEM_SIZE - p;42 43     if(down_interruptible(&my_dev.sem))//獲取信號量44         return - ERESTARTSYS;45 46     if(copy_to_user(buf,pbase + p,size)){47        ret = - EFAULT;48     }else{49         *ppos += count;50         ret = count;51     }52     53     up(&my_dev.sem);//釋放信號量54 55     return ret;56 }

至此我們今天的工作完成,快過年了家里好多事,沒有太多時間,還請大家見諒,提前祝大家新年快樂。

作者:wrjvsz 來源于:http://www.CUOXin.com/wrjvszq/,轉載請注明出處。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 大洼县| 高安市| 蓝山县| 航空| 尚义县| 铁岭县| 景德镇市| 博湖县| 枣庄市| 安陆市| 喀喇| 闽清县| 罗源县| 曲阳县| 泽州县| 中卫市| 安龙县| 富宁县| 桓台县| 大竹县| 阜新市| 瑞金市| 仙居县| 布拖县| 抚顺市| 丰顺县| 靖远县| 克什克腾旗| 定襄县| 武宁县| 文登市| 长武县| 富平县| 唐河县| 长宁区| 榆林市| 南投市| 根河市| 保德县| 大洼县| 西吉县|