目的: 檢驗學員對並行和多執行緒程式設計的認知
作答表單 (針對 Linux 核心「設計」課程)
作答表單 (針對 Linux 核心「實作」課程)
1
考慮一個 lock-free stack 的實作。使用者可推入 void *
到堆疊中,檢查堆疊的大小,並從堆疊中彈出 void *
。除了初始化和銷毀,這些操作在多個執行緒中都是安全的。當彈出時,兩個不同的執行緒永遠不會獲得相同的元素。如果兩個執行緒同時嘗試推入元素,也不會有元素丟失。最重要的是,當存取堆疊時,執行緒永遠不會在 lock 上阻塞。C 語言標準函式庫無法保證 malloc()
是不依賴 lock,於是 lock-free 實作意味著不呼叫 malloc()
。預先配置堆疊記憶體的另一個重要好處是,該實作不需要使用 hazard pointers,後者比堆疊本身要複雜得多。
最大堆疊空間應該實際上是所需的最大空間加上存取堆疊的執行緒數量。這是因為一個執行緒可能從堆疊中移除一個節點,在該節點被釋放以供重用之前,另一個執行緒嘗試推入。這個執行緒可能找不到任何可用的節點,導致它放棄,儘管堆疊實際上「未滿」。
lstack_init()
和 lstack_push()
的整數返回值是用來表示錯誤代碼,成功時返回 0
。這些操作唯一可能失敗的情況是記憶體不足。這無論是否使用 lock 都是一個問題:系統可能會用完記憶體。在推入的情況下,這意味著堆疊已滿。
參考執行輸出:
注意: 無法通過 ThreadSanitizer
2
(mutex) lock 可能會導致 deadlock,但若始終按照一致的順序去 acquire lock,即可避免該情況。以下程式碼提供追蹤功能,確保使用者採取正確的順序。程式會跟蹤目前執行緒持有的 lock,每當 acquire 獲取新 lock 時,就會從最後一個 lock 到新 lock 建立一個依賴關係,後者構成一個 graph,只要該 graph 不包含任何循環,就不會發生 deadlock。
考慮以下的程式碼:
參考執行輸出: (ok 那行可能不出現)
Linux 核心也有 lock 相依性的偵測機制,參見 Runtime locking correctness validator,對應的程式碼:
補完程式碼,使 lockdep 運作符合預期。
作答規範:
true
或 false