1 / 35

共時情境與相競現象

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

gauri
Télécharger la présentation

共時情境與相競現象

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

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

  3. 共時情境 • 共時情境(concurrency) 系統試著同時執行一件以上的工作。 • 早期版本的Linux核心,不支援SMP,只有在服務硬體中斷的時,才有可能引發共時情境。

  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敘述……

  5. 5.1 scull裡的陷阱 • 相競現象(race condition) 執行結果會隨事件發生順序而異的現象。 • 相競現象可能會造成失憶、系統崩潰、侵蝕資料或是引發安全漏洞。

  6. 5.2共時情境與其控管方法 • 鎖定(locking) • 排他獨佔(mutual exclusion) • 保障每次都只有一個執行緒可操作共享資源。

  7. 5.3權狀與獨佔權 • 當一個執行緒發現他必須配置記憶體的時候,在他完成配置之前,其他的執行緒都不會進行該測試。 • 設置關鍵區(critical section),就是在任何時候會多只有一個執行緒的程式段。 • 就scull的需求而言,最能夠滿足我們所需條件的機制是「權狀」(semaphore)

  8. 5.3權狀與獨佔權 • Semaphores是一個有嚴謹定義的觀念。在本質上,一個semaphore是一個整數值與一對通常稱為P與V的函式之組合。當一個行程即將進入關鍵區,必須先呼叫該關鍵區的權狀的P函式。若權狀值大於0,則將該值-1,讓行程進入關鍵區。 • 相對的,若權狀值為0(或更低),則行程必須等待其他行程釋出權狀。當行程離開關鍵區時,必須呼叫V來釋回權狀。V函式會將權狀值遞增1,並喚醒正在等待他的行程(如果有的話)。

  9. 5.3權狀與獨佔權 • 排它獨佔(mutual exclusion) 避免同時有多個行程進入關鍵區。 • 若是權狀每次只能由一個行程持有,我們稱這種模式的權狀為「獨佔權」(mutex) • Linux核心裡找到的權狀幾乎都是獨佔權

  10. Linux的權狀機制 • <asm/semaphore.h> void sema_init(struct semaphore *sem, int val); DECLARE_MUTEX(name); //1 DECLARE_MUTEX_LOCKED(name); //0 void init_MUTEX(struct semaphore *sem); void init_MUTEX_LOCKED(struct semaphore *sem);

  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);

  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 */ };

  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); }

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

  15. Reader/Writer權狀 • Writer void 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);

  16. 5.4完工通知 • 如果權狀的占用時間很長,或是相競爭取權狀的情況很嚴重,就會嚴重影響效率表現。對於需要長期等待的工作,權狀不是理想工具。 • 2.4.7版核心有一個「完工通知」(completion)介面。用途是讓一個執行緒可將完工消息告訴另一個執行緒。

  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);

  18. 5.4完工通知 • 如果使用了void complete_all()則必須以下列巨集來重新初始化 INIT_COMPLETION(struct completion c);

  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 */ }

  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 */ }

  21. 5.5迴旋鎖 • 不同於權狀,迴旋鎖可用於不得休眠的程式,像是interrrupt handlers。 • 只有“開鎖”、“閉鎖”兩種狀態。 • 相競爭取同一個迴旋鎖時,處理器就進入一個busy-wait迴圈,枯等機會進入關鍵區。

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

  23. 迴旋鎖與連動環境 • 當你的驅動程式取得了一個迴旋鎖,並進入了他的關鍵區。在離開關鍵區之前,你的驅動程式失去了處理器。(有一個較高優先度的行程把你的程式推到旁邊)於是,你的程式上了鎖,卻不會在可預見的未來開鎖,有可能導致整個系統死結的狀況。 • 因此,使用迴旋鎖有一個規則,就是持有鎖的程式段必須是連動的(atomic),不能休眠。

  24. 迴旋鎖與連動環境 • 關於kernel preemption的問題,只要迴旋鎖上了鎖,相關處理器的preemption就立即失效。 • 避免在持有鎖的狀況下陷入休眠,至今沒有有效的方法,只能把握幾個原則來判斷哪些函式有休眠的可能。

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

  26. SpinLock API Lock void 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) unLock void 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);

  27. SpinLock API tryLock int spin_trylock(spinlock_t *lock); int spin_trylock_bh(spinlock_t *lock);

  28. Reader/Writer 迴旋鎖 Read void 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);

  29. Reader/Writer 迴旋鎖 Write void 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);

  30. 5.6鎖定機制的使用心法 • 模擬兩可的規則 編寫程式的時候,必定會遇到多個函式都需要存取特定鎖定的機制所保護的資料結構。如果在一個函式上鎖的狀況下,叫了另一個也會試圖上鎖的函式,則會形成死結。

  31. 5.6鎖定機制的使用心法 • 鎖定順序 假設我持有2個鎖,lock1和lock2,啽有一段程式同時需要這兩個鎖。那麼這段程式就暗藏了死結。 解決問題的方法是,總是以相同的順序來取得保護鎖。

  32. 5.6鎖定機制的使用心法 • 細膩鎖定vs粗糙鎖定 BKL(The big kernel lock)其作用就是讓核心成為一個超大關鍵區,讓任何時刻都只有一個CPU可執行程式。

  33. 5.7鎖定技術的替代方案 • 無鎖演算法 使用環狀暫存區的資料結構(circular buffer),適合應付無保護鎖“單消費者-單製造者”互動。

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

  35. 5.7鎖定技術的替代方案

More Related