# 看習五 OS 影片 > [影片](https://www.youtube.com/playlist?list=PLMWkAn-aOA0a2QhzfwsmXpa4BZrniWI2M) > [教材](https://www.cs.ccu.edu.tw/~shiwulo/course/2021-osdi/) ## 同步機制 在 gdb 下達 `disassemble /m <function name>` 進行反組譯 critical section 的解決要件: - mutual exclusion - progress - bounded waiting 適用長度: - spinlock :短區塊 - semaphore :長區塊 - mutex : adaptive ### Peterson's Solution ### atomic and fence `atomic_thread_fence()` 可防止指令重排的越界 x86 instructions : - `lfence` fence for load - `sfence` fence for store ### Semaphore - acquire 失敗幾乎附帶 context switch ### Spinlock - acquire 失敗會進入 loop 不斷嘗試 ### Mutex - Optional feature - ownership - priority inheritance - nested lock - adaptive #### Adaptive mutex sun solaris 依持鎖人當下的情況來判定 - Q 在 waiting queue : P sleep and kernel context switch - Q 執行中: spinlock - P, Q 在同一 processor 上: P sleep glibc 根據之前 lock 等待的時間,決定 spinlock 的 loop 次數 ### Linux futex > man futex glibc 沒有對此 system call 的包裝,需要直接使用 `syscall()` :::info TODO 看 jserv 版本的解說交互參照 ::: ### SPSC single producer single cosumer problem an example solution - mutex - full - empty ### Readers-Writers problem constrain: - readers 和 writer 不可以同時存取同個資料結構 - 不同 writer 不可同時存取同個資料結構 ```c /* reader */ do { wait(mutex); readcount++; if (readcount == 1) wait(wrt); signal(mutex); // reading here wait(mutex); readcount--; if (readcount == 0) signal(wrt); signal(mutex); } while (1); ``` pthread 有提供 read write lock 供同時讀寫用 `pthread_rwlock_wrlock()` ### 哲學家用餐問題 - 所有人都先拿某一邊 $\implies$ deadlock - 依奇偶數決定拿取順序 $\implies$ 可能違反 bounded waiting - 輪流獲得高優先權 $\implies$ 可能造成效能瓶頸 每次使用 semaphore 都會進入 kernel mode,因此過多的 semaphore 會對系統帶來效能負擔。 ### Cache block - CHA : cashing and home agent - SF : snoop filter - LLC : last level cache #### Cache coherent 算法 - snoop:使用 broadcast - dictionary:使用額外空間來紀錄分散的情況,可以做點對點傳輸 目前的 CPU 通常是混用兩種方法 cache coherent 不保證 atomic operation ### atomic 傳統上有 instruction: `test_n_set` 和 `swap` `test_n_set` 相當於 C 程式碼: #### test_n_set ```c int test_n_set(int *value) { register tmp = value; *value = 1; return tmp; } ``` 使用範例: > https://en.wikipedia.org/wiki/Test-and-set ```c volatile int lock = 0; void foo() { while (test_n_set(&lock) == 1) ; // critical section lock = 0; } ``` C11 有 `atomic_exchange()` 類似於 `test_n_set` #### swap `swap` 相當於 C 程式碼: ```c void swap(int *a, int *b) { register tmp; tmp = *a; *a = *b; *b = tmp; } ``` 此二方法一直更新記憶體,表示一直觸發 cache coherence,造成效率負擔。 因此以 compare exchange 來改善,compare exchange 只會讀取值做比較,通常不需要額外 cache coherence。 #### compare exchange ```c bool atomic_compare_exchange_strong(volatile atomic_int *obj, int *expected, int desired) { if (obj == expected) { *obj = desired; return true; } else { expected = obj; // 失敗時只有讀 obj return false; } } ``` `expected` 是 local variable 不需要做 coherent 使用範例: ```c void foo() { int expected; while (1) { while (!atomic_compare_exchange_weak(&current, &expected, 1)) expected = 0; // critical section current = 0; } } ``` RISC 系列常使用 load-link (LL) 或 store-condition (SC) 實現 ```c /* example of doing value + 1 */ do { tmp = LL(&value); tmp += 1; } while (SC(&value, tmp) == false); ``` ### Spinlock **檢查 - 進入**會出錯,因為中間的過程可能導致很多 thread 都進到 critical section。正確的方法是**改變狀態 - 檢查 - 確認是否成功 lock**,或是使用 atomic 方式**檢查 - 進入 (上鎖)**。 Use `atomic_int` rather than `volatile int`. > The volatile types do not provide inter-thread synchronization, memory ordering, or atomicity. > https://en.cppreference.com/w/c/language/atomic ### Sequential lock ### Ticket lock FIFO 有一個最大門票數值 $N$ ,writer 每次要拿到 $N$ 張票才能進入, reader 只需要 $1$ 張票就能進入。 busy waiting 可以改用 inline assembly `asm("pause")` 來省電 ### memory order C11 memory order ```c #include <stdatomic.h> typedef enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_reder_seq_cst } memory_order; ``` e.g. ```c atomic_load_explicit(const volatile int *obj, memory_order_release); ``` - relaxed:不保證任何 order,只保證 atomic,適用於統計次數 - consume:與該變數相關的 instruction 不可以往前跨越 - acquire:之後的 instruction 都不可以往前跨越,適用於 lock - release:之前的 instruction 都不可以往後跨越,適用於 unlock - acq_rel:前後都不可跨越,適用於同時有 read, modify, write 的資料結構 - seq_cst:滿足 program order 和 write atomicity,所有 thread 看到的順序都一樣 seq_cst 等同在硬體上一個大鎖,大幅影響多核效能