第20回 アジア競技大会 開催構想01 招致の意義/大会コンセプト 4 競技関係 02 開催期間及び実施競技 5 03 競技会場 6 04 選手村 34 05 気象データ
共時情境與相競現象
description
Transcript of 共時情境與相競現象
共時情境與相競現象報告者 : 吳依靜
OUTLINE
• 共時情境• 5.1 scull 裡的陷阱• 5.2 共時情境與其控管方法• 5.3 權狀與獨佔權• 5.4 完工通知• 5.5 迴旋鎖• 5.6 鎖定機制的使用心法• 5.7 鎖定技術的替代方案
共時情境• 共時情境 (concurrency) 系統試著同時執行一件以上的工作。
• 早期版本的 Linux 核心,不支援 SMP ,只有在服務硬體中斷的時,才有可能引發共時情境。
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.1 scull 裡的陷阱• 相競現象 (race condition)執行結果會隨事件發生順序而異的現象。
• 相競現象可能會造成失憶、系統崩潰、侵蝕資料或是引發安全漏洞。
5.2 共時情境與其控管方法
• 鎖定 (locking)
• 排他獨佔 (mutual exclusion)
• 保障每次都只有一個執行緒可操作共享資源。
5.3 權狀與獨佔權
• 當一個執行緒發現他必須配置記憶體的時候,在他完成配置之前,其他的執行緒都不會進行該測試。
• 設置關鍵區 (critical section) ,就是在任何時候會多只有一個執行緒的程式段。
• 就 scull 的需求而言,最能夠滿足我們所需條件的機制是「權狀」(semaphore)
5.3 權狀與獨佔權
• Semaphores 是一個有嚴謹定義的觀念。在本質上,一個 semaphore 是一個整數值與一對通常稱為 P 與 V 的函式之組合。當一個行程即將進入關鍵區,必須先呼叫該關鍵區的權狀的 P 函式。若權狀值大於 0 ,則將該值 -1 ,讓行程進入關鍵區。
• 相對的,若權狀值為 0( 或更低 ) ,則行程必須等待其他行程釋出權狀。當行程離開關鍵區時,必須呼叫 V 來釋回權狀。 V 函式會將權狀值遞增 1 ,並喚醒正在等待他的行程 ( 如果有的話 ) 。
5.3 權狀與獨佔權
• 排它獨佔 (mutual exclusion) 避免同時有多個行程進入關鍵區。
• 若是權狀每次只能由一個行程持有,我們稱這種模式的權狀為「獨佔權」 (mutex)
• Linux 核心裡找到的權狀幾乎都是獨佔權
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);
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);
在 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 */ };
在 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); }
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);
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);
5.4 完工通知• 如果權狀的占用時間很長,或是相競爭
取權狀的情況很嚴重,就會嚴重影響效率表現。對於需要長期等待的工作,權狀不是理想工具。
• 2.4.7 版核心有一個「完工通知」(completion)介面。用途是讓一個執行緒可將完工消息告訴另一個執行緒。
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);
5.4 完工通知• 如果使用了 void complete_all() 則必須以下列巨集來重新初
始化 INIT_COMPLETION(struct completion c);
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 */ }
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 */ }
5.5 迴旋鎖
• 不同於權狀,迴旋鎖可用於不得休眠的程式,像是 interrrupt handlers 。
• 只有“開鎖”、“閉鎖”兩種狀態。
• 相競爭取同一個迴旋鎖時,處理器就進入一個 busy-wait 迴圈,枯等機會進入關鍵區。
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);
迴旋鎖與連動環境
• 當你的驅動程式取得了一個迴旋鎖,並進入了他的關鍵區。在離開關鍵區之前,你的驅動程式失去了處理器。 ( 有一個較高優先度的行程把你的程式推到旁邊 ) 於是,你的程式上了鎖,卻不會在可預見的未來開鎖,有可能導致整個系統死結的狀況。
• 因此,使用迴旋鎖有一個規則,就是持有鎖的程式段必須是連動的 (atomic) ,不能休眠。
迴旋鎖與連動環境
• 關於 kernel preemption 的問題,只要迴旋鎖上了鎖,相關處理器的 preemption 就立即失效。
• 避免在持有鎖的狀況下陷入休眠,至今沒有有效的方法,只能把握幾個原則來判斷哪些函式有休眠的可能。
迴旋鎖與連動環境• 你的裝置受迴旋鎖保護。假設我的驅動程
式正在執行中,且已經取得代表裝置控制權的迴旋鎖。在上鎖的期間,裝置發出了中斷要求,觸發了中斷服務程式(ISR) 。 ISR 同樣必須先取得迴旋鎖。然而,若 ISR 與原本持有的迴旋鎖但被打岔的程式段是在同一個處理器上運作。 Interrrupt handler 會不停空轉,而有會釋出迴旋鎖的程式段會沒機會執行。
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);
SpinLock API
tryLockint spin_trylock(spinlock_t *lock); int spin_trylock_bh(spinlock_t *lock);
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);
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);
5.6 鎖定機制的使用心法• 模擬兩可的規則編寫程式的時候,必定會遇到多個函式都
需要存取特定鎖定的機制所保護的資料結構。如果在一個函式上鎖的狀況下,叫了另一個也會試圖上鎖的函式,則會形成死結。
5.6 鎖定機制的使用心法• 鎖定順序假設我持有 2 個鎖, lock1 和 lock2 ,啽
有一段程式同時需要這兩個鎖。那麼這段程式就暗藏了死結。解決問題的方法是,總是以相同的順序來
取得保護鎖。
5.6 鎖定機制的使用心法• 細膩鎖定 vs粗糙鎖定BKL(The big kernel lock) 其作用就是讓核
心成為一個超大關鍵區,讓任何時刻都只有一個 CPU 可執行程式。
5.7 鎖定技術的替代方案• 無鎖演算法使用環狀暫存區的資料結構 (circular buffer) ,
適合應付無保護鎖“單消費者 -單製造者”互動。
5.7 鎖定技術的替代方案• 製造者負責將資料填寫在陣列的某一端,消費者從陣列的另外一端取走資料。當製造者走到陣列的末端,就繞回到陣列的頂端。所以一個環狀暫存區需要一個陣列與兩個索引指標,分別指向新資料的存放點( 寫入點 ) ,以及下一筆要被取出的舊資料。
5.7 鎖定技術的替代方案