共時情境與相競現象

35
共共共共共共共共共 共共共 : 共共共

description

共時情境與相競現象. 報告者 : 吳依靜. OUTLINE. 共時情境 5.1 scull 裡的陷阱 5.2 共時情境與其控管方法 5.3 權狀與獨佔權 5.4 完工通知 5.5 迴旋鎖 5.6 鎖定機制的使用心法 5.7 鎖定技術的替代方案. 共時情境. 共時情境 (concurrency) 系統試著同時執行一件以上的工作。 早期版本的 Linux 核心,不支援 SMP ,只有在服務硬體中斷的時,才有可能引發共時情境。. 5.1 scull 裡的陷阱. 以下是 Scull 中的一段程式碼 - PowerPoint PPT Presentation

Transcript of 共時情境與相競現象

Page 1: 共時情境與相競現象

共時情境與相競現象報告者 : 吳依靜

Page 2: 共時情境與相競現象

OUTLINE

• 共時情境• 5.1 scull 裡的陷阱• 5.2 共時情境與其控管方法• 5.3 權狀與獨佔權• 5.4 完工通知• 5.5 迴旋鎖• 5.6 鎖定機制的使用心法• 5.7 鎖定技術的替代方案

Page 3: 共時情境與相競現象

共時情境• 共時情境 (concurrency) 系統試著同時執行一件以上的工作。

• 早期版本的 Linux 核心,不支援 SMP ,只有在服務硬體中斷的時,才有可能引發共時情境。

Page 4: 共時情境與相競現象

5.1 scull 裡的陷阱• 以下是 Scull 中的一段程式碼if (!dptr->data[s_pos]) {

dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (!dptr->data[s_pos])

goto out; }

• 假設有 2 個互不相干的行程 A 與 B ,恰巧試圖對同一個scull 裝置的同一個位置寫入資料。兩者同時抵達上述程式第一個 if 敘述……

Page 5: 共時情境與相競現象

5.1 scull 裡的陷阱• 相競現象 (race condition)執行結果會隨事件發生順序而異的現象。

• 相競現象可能會造成失憶、系統崩潰、侵蝕資料或是引發安全漏洞。

Page 6: 共時情境與相競現象

5.2 共時情境與其控管方法

• 鎖定 (locking)

• 排他獨佔 (mutual exclusion)

• 保障每次都只有一個執行緒可操作共享資源。

Page 7: 共時情境與相競現象

5.3 權狀與獨佔權

• 當一個執行緒發現他必須配置記憶體的時候,在他完成配置之前,其他的執行緒都不會進行該測試。

• 設置關鍵區 (critical section) ,就是在任何時候會多只有一個執行緒的程式段。

• 就 scull 的需求而言,最能夠滿足我們所需條件的機制是「權狀」(semaphore)

Page 8: 共時情境與相競現象

5.3 權狀與獨佔權

• Semaphores 是一個有嚴謹定義的觀念。在本質上,一個 semaphore 是一個整數值與一對通常稱為 P 與 V 的函式之組合。當一個行程即將進入關鍵區,必須先呼叫該關鍵區的權狀的 P 函式。若權狀值大於 0 ,則將該值 -1 ,讓行程進入關鍵區。

• 相對的,若權狀值為 0( 或更低 ) ,則行程必須等待其他行程釋出權狀。當行程離開關鍵區時,必須呼叫 V 來釋回權狀。 V 函式會將權狀值遞增 1 ,並喚醒正在等待他的行程 ( 如果有的話 ) 。

Page 9: 共時情境與相競現象

5.3 權狀與獨佔權

• 排它獨佔 (mutual exclusion) 避免同時有多個行程進入關鍵區。

• 若是權狀每次只能由一個行程持有,我們稱這種模式的權狀為「獨佔權」 (mutex)

• Linux 核心裡找到的權狀幾乎都是獨佔權

Page 10: 共時情境與相競現象

Linux 的權狀機制• <asm/semaphore.h>void sema_init(struct semaphore *sem, int val);

DECLARE_MUTEX(name); //1DECLARE_MUTEX_LOCKED(name); //0

void init_MUTEX(struct semaphore *sem);void init_MUTEX_LOCKED(struct semaphore *sem);

Page 11: 共時情境與相競現象

Linux 的權狀機制Down() 會遞減權狀值,並持續等到適當的時機void down(struct semaphore *sem);int down_interruptible(struct semaphore *sem); int down_trylock(struct semaphore *sem);

Up() 返回的那一瞬間,呼叫者就不再持有權狀,任何取得權狀的執行緒,都必須呼叫一次 up() 來釋放權狀。void up(struct semaphore *sem);

Page 12: 共時情境與相競現象

在 scull 裡使用權狀• Scull_dev 的結構

struct scull_dev { struct scull_qset *data; /* Pointer to first quantum set */ int quantum; /* the current quantum size */ int qset; /* the current array size */ unsigned long size; /* amount of data stored here */ unsigned int access_key; /* used by sculluid and scullpriv */ struct semaphore sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ };

Page 13: 共時情境與相競現象

在 scull 裡使用權狀• Scull 初始化

for (i = 0; i < scull_nr_devs; i++) { scull_devices[i].quantum = scull_quantum; scull_devices[i].qset = scull_qset; sema_init(&scull_devices[i].sem, 1); scull_setup_cdev(&scull_devices[i], i); }

Page 14: 共時情境與相競現象

Reader/Writer 權狀• <linux/rwsem.h>void init_rwsem(struct rw_semaphore *sem);

• Readervoid down_read(struct rw_semaphore *sem);int down_read_trylock(struct rw_semaphore *sem); void up_read(struct rw_semaphore *sem);

Page 15: 共時情境與相競現象

Reader/Writer 權狀• Writervoid down_write(struct rw_semaphore *sem); int down_write_trylock(struct rw_semaphore *sem); void up_write(struct rw_semaphore *sem); void downgrade_write(struct rw_semaphore *sem);

Page 16: 共時情境與相競現象

5.4 完工通知• 如果權狀的占用時間很長,或是相競爭

取權狀的情況很嚴重,就會嚴重影響效率表現。對於需要長期等待的工作,權狀不是理想工具。

• 2.4.7 版核心有一個「完工通知」(completion)介面。用途是讓一個執行緒可將完工消息告訴另一個執行緒。

Page 17: 共時情境與相競現象

5.4 完工通知• <linux/completion.h>DECLARE_COMPLETION(my_completion);

• 等候完成void wait_for_completion(struct completion *c);*wait_for_completion() 是不可中斷的,這表示若是呼叫了他卻沒送出完工通知,結果將造成一個殺不死的行程。

• 用於等待完工的通知void complete(struct completion *c);void complete_all(struct completion *c);

Page 18: 共時情境與相競現象

5.4 完工通知• 如果使用了 void complete_all() 則必須以下列巨集來重新初

始化 INIT_COMPLETION(struct completion c);

Page 19: 共時情境與相競現象

5.4 完工通知ssize_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t *pos){ printk(KERN_DEBUG "process %i (%s) going to sleep\n",current->pid, current->comm); wait_for_completion(&comp); printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); return 0; /* EOF */ }

Page 20: 共時情境與相競現象

5.4 完工通知ssize_t complete_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos) { printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", current->pid, current->comm); complete(&comp); return count; /* succeed, to avoid retrial */ }

Page 21: 共時情境與相競現象

5.5 迴旋鎖

• 不同於權狀,迴旋鎖可用於不得休眠的程式,像是 interrrupt handlers 。

• 只有“開鎖”、“閉鎖”兩種狀態。

• 相競爭取同一個迴旋鎖時,處理器就進入一個 busy-wait 迴圈,枯等機會進入關鍵區。

Page 22: 共時情境與相競現象

Spinlocks API 簡介<linux/spinlock.h>spinlock_t my_lock = SPIN_LOCK_UNLOCKED;void spin_lock_init(spinlock_t *lock);

Lockvoid spin_lock(spinlock_t *lock);

unLockvoid spin_unlock(spinlock_t *lock);

Page 23: 共時情境與相競現象

迴旋鎖與連動環境

• 當你的驅動程式取得了一個迴旋鎖,並進入了他的關鍵區。在離開關鍵區之前,你的驅動程式失去了處理器。 ( 有一個較高優先度的行程把你的程式推到旁邊 ) 於是,你的程式上了鎖,卻不會在可預見的未來開鎖,有可能導致整個系統死結的狀況。

• 因此,使用迴旋鎖有一個規則,就是持有鎖的程式段必須是連動的 (atomic) ,不能休眠。

Page 24: 共時情境與相競現象

迴旋鎖與連動環境

• 關於 kernel preemption 的問題,只要迴旋鎖上了鎖,相關處理器的 preemption 就立即失效。

• 避免在持有鎖的狀況下陷入休眠,至今沒有有效的方法,只能把握幾個原則來判斷哪些函式有休眠的可能。

Page 25: 共時情境與相競現象

迴旋鎖與連動環境• 你的裝置受迴旋鎖保護。假設我的驅動程

式正在執行中,且已經取得代表裝置控制權的迴旋鎖。在上鎖的期間,裝置發出了中斷要求,觸發了中斷服務程式(ISR) 。 ISR 同樣必須先取得迴旋鎖。然而,若 ISR 與原本持有的迴旋鎖但被打岔的程式段是在同一個處理器上運作。 Interrrupt handler 會不停空轉,而有會釋出迴旋鎖的程式段會沒機會執行。

Page 26: 共時情境與相競現象

SpinLock API

Lockvoid spin_lock(spinlock_t *lock); void spin_lock_irqsave(spinlock_t *lock, unsigned long flags); void spin_lock_irq(spinlock_t *lock); void spin_lock_bh(spinlock_t *lock)unLockvoid spin_unlock(spinlock_t *lock); void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags); void spin_unlock_irq(spinlock_t *lock); void spin_unlock_bh(spinlock_t *lock);

Page 27: 共時情境與相競現象

SpinLock API

tryLockint spin_trylock(spinlock_t *lock); int spin_trylock_bh(spinlock_t *lock);

Page 28: 共時情境與相競現象

Reader/Writer 迴旋鎖Readvoid read_lock(rwlock_t *lock);void read_lock_irqsave(rwlock_t *lock, unsigned long flags);void read_lock_irq(rwlock_t *lock);void read_lock_bh(rwlock_t *lock);

void read_unlock(rwlock_t *lock);void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);void read_unlock_irq(rwlock_t *lock);void read_unlock_bh(rwlock_t *lock);

Page 29: 共時情境與相競現象

Reader/Writer 迴旋鎖Writevoid write_lock(rwlock_t *lock); void write_lock_irqsave(rwlock_t *lock, unsigned long flags); void write_lock_irq(rwlock_t *lock); void write_lock_bh(rwlock_t *lock); int write_trylock(rwlock_t *lock);

void write_unlock(rwlock_t *lock); void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags); void write_unlock_irq(rwlock_t *lock); void write_unlock_bh(rwlock_t *lock);

Page 30: 共時情境與相競現象

5.6 鎖定機制的使用心法• 模擬兩可的規則編寫程式的時候,必定會遇到多個函式都

需要存取特定鎖定的機制所保護的資料結構。如果在一個函式上鎖的狀況下,叫了另一個也會試圖上鎖的函式,則會形成死結。

Page 31: 共時情境與相競現象

5.6 鎖定機制的使用心法• 鎖定順序假設我持有 2 個鎖, lock1 和 lock2 ,啽

有一段程式同時需要這兩個鎖。那麼這段程式就暗藏了死結。解決問題的方法是,總是以相同的順序來

取得保護鎖。

Page 32: 共時情境與相競現象

5.6 鎖定機制的使用心法• 細膩鎖定 vs粗糙鎖定BKL(The big kernel lock) 其作用就是讓核

心成為一個超大關鍵區,讓任何時刻都只有一個 CPU 可執行程式。

Page 33: 共時情境與相競現象

5.7 鎖定技術的替代方案• 無鎖演算法使用環狀暫存區的資料結構 (circular buffer) ,

適合應付無保護鎖“單消費者 -單製造者”互動。

Page 34: 共時情境與相競現象

5.7 鎖定技術的替代方案• 製造者負責將資料填寫在陣列的某一端,消費者從陣列的另外一端取走資料。當製造者走到陣列的末端,就繞回到陣列的頂端。所以一個環狀暫存區需要一個陣列與兩個索引指標,分別指向新資料的存放點( 寫入點 ) ,以及下一筆要被取出的舊資料。

Page 35: 共時情境與相競現象

5.7 鎖定技術的替代方案