# Linux 核心實作上課筆記與疑惑 ## 第二週 ### [C 語言: 數值系統](https://hackmd.io/@sysprog/c-numerics) **運用 bit-wise operator** - `x & (x - 1) == 0` 在 C 語言中表示: - power of two e.g. $4_{10} = 1\ 0\ 0_2$ , $3_{10} = 0\ 1\ 1_2$ , $1\ 0\ 0_2$ & $0\ 1\ 1_2 = 0\ 0\ 0_2$ - signed v.s. unsigned - 將字元轉成小寫: 免除使用分支 - ' '(空格)的 [ASCII碼](https://zh.wikipedia.org/zh-tw/ASCII) 為 $32_{10} = 1\ 0\ 0\ 0\ 0\ 0_2$ - A = $0\ 1\ 0\ 0\ 0\ 0\ 0\ 1_2$ , a = $0\ 1\ 1\ 0\ 0\ 0\ 0\ 1_2$ - 英文大寫和小寫的 ASCII 剛好差 32 ,因此利用`\ ' '` 即可讓代表 32 的位元設為 1 ,達到大寫轉小寫的作用。 - 同理,也可利用 `& '_'` ( _ = $0\ 1\ 0\ 1\ 1\ 1\ 1\ 1\ 1_2$), `^ ' '` 來分別達到將字元轉為大寫和大小寫互轉 ```c ('a' | ' ') // 得到 'a' ('A' | ' ') // 得到 'a' ``` - Count Leading Zero 疑問 ::: info 不明白為何 lookup table 的內容是這樣設計,也不了解為何是乘上0x07C4ACDD 並向右位移 27 位元,想請老師幫忙解惑 ::: ``` c const int tab32[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; int log2_32 (uint32_t value) { value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; return tab32[(uint32_t)(value*0x07C4ACDD) >> 27]; } ``` ### [Linux 核心的 hash table 實作](https://hackmd.io/@sysprog/linux-hashtable) **常見雜湊函數作法**: 1. Division method 2. Mid-square 3. Folding addition 4. Multiplication Method :::warning TODO 想比照比較同頁面下方 "golden ratio 與非 golden ratio" 的實驗方法,觀察這四值種 hash function 在輸入為亂數時發生碰撞的情形 ::: --- ## 第三週 ### [你所不知道的 C 語言:遞迴呼叫篇](https://hackmd.io/@sysprog/c-recursion#Fibonacci-sequence) - C 語言命名慣例 - 大寫結構體: 和作業系統、所屬環境有關 - 小寫結構體: 在文件中有定義的話就可以將它的成員取出來做特定操作 ### [你所不知道的 C 語言:技巧篇](https://hackmd.io/@sysprog/c-trick) - C 語言中會將程式模組化,將程式分為 .h 檔 (header file) 和 .c 檔 - 編譯器一次只編譯一個檔案,每個檔案都是一個編譯單元,會有個別對應的 scope ,若 function 前面加上 `static` 表示該 function 的有效範圍僅在同一個檔案內,避免到時候 linking 的時候有多個檔案擁有相同的函式名稱,造成重複定義。 ## 第七週 ### [Linux 核心設計: 不僅是個執行單元的 Process](https://hackmd.io/@sysprog/linux-process) Threads - 建立 threads 的成本比建立 process 更低 - multitasking - 切換成本較 process 低 - Linux Thread State Transition ![image](https://hackmd.io/_uploads/HJsjxqcxR.png) ### [Linux 核心設計: 淺談同步機制](https://hackmd.io/@sysprog/linux-sync) - mutex 和 semophore 的比較 - mutex 確保數個 process 在一個時間點上,只能有一個 process 存取單項資源 - semaphore 讓數個 producer 與數個 consumer 在計數器的基礎上進行合作 | | mutex | semophore | | -------- | -------- | -------- | | 出現時間 | 較晚 (2.6.16版) | 較早 | 解鎖 | 只能由上鎖的 thread 解鎖 | 可由原本的 thread 或是另外一個 thread 解開 | | 可進入 critical section 之 thread 數量 | 1個 | 多個 | 應用場景 | 常使用在 critical section 只能有一個 thread 進入,而且要避免 priority inversion 的時候 | 常用在同步兩個 thread 或功能上面,因為 Semaphore 實際上使用的是 signal 的 up 與 down,讓 Semaphore 可以變成是一種 notification 的作用,例如 A thread 執行到某個地方時 B thread 才能繼續下去 | priority inversion | 無 | 有 ### [並行和多執行緒程式設計-POSIX Thread](https://hackmd.io/@sysprog/concurrency/%2F%40sysprog%2Fposix-threads) - thread-local storage (tls): 給 thread 一個專屬的空間 (不和其他 thread 共享) - 例如可記錄 errno (error number) - 硬體協助 - 無硬體協助 - Native POSIX Thread Library (NPTL): 一對一 - semophore - sysv semaphore - posix semaphore (現在系統, sem_ 開頭) - 用 semaphore 控制總量、 mutex 控制 mutual exclusion - 利用 Atomic Compare and Exchange 指令來實作 semaphore 和 mutex - atomic functions 分為 strong 和 weak : strong guarantee the success or failure while weak may fail - [condition variables 範例](https://github.com/angrave/SystemProgramming/wiki/Synchronization%2C-Part-5%3A-Condition-Variables) - Threads sleeping inside a condition variable are woken up by calling `pthread_cond_broadcast` (wake up all) or `pthread_cond_signal` (wake up one) - **Condition variables are always used with a mutex lock.** - Before calling wait, the mutex lock must be locked and wait must be wrapped with a loop. Tips: 如果 `N` 為二的冪, `i % N` 可以改成 `(i++) & (N-1)` bitwise 操作 ### [2024q1 第 9 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz9) #### 測驗 1 如何確保 fork 程式有東西可以合併? mmap: 作業系統中讓多個行程存取同一個記憶體空間 ```c int main(int argc, char **argv) { rand_context_t r; rand_init(&r, (uintptr_t) &main ^ getpid()); /* shared by forked processes */ int *arr = mmap(NULL, N_ITEMS * sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); for (int i = 0; i < N_ITEMS; ++i) arr[i] = iabs((int) rand_next(&r)); merge_sort(arr, N_ITEMS); for (int i = 1; i < N_ITEMS; ++i) { if (arr[i] < arr[i - 1]) { fprintf(stderr, "Ascending order is expected.\n"); exit(1); } } printf("OK!\n"); return 0; } ``` - sudo sysctl -w kernel.sched_child_runs_first=1,以便要求 Linux 排程器 (CFS) 讓 child process 優先於 parent process 執行 - 遞迴時呼叫 fork 會更耗費成本 - 如何改進此程式? - 避免使用遞迴,降低 fork 成本 - 預先 fork (pre-forking) - copy_form_user 會有 cpu mode switch #### 測驗 2 - ioctl : 和核心內裝置溝通 - if page 太大, page fault 的成本會太大 (可參考 CS:APP ch9) ### 第十二周 5/9 多核處理器 - 手機開機只先開一核作為管理者 - WFI、WFE指令 - work load 計算,會有 decay,因為最重要的資訊是當下資訊,越久之前的資料重要性越低 - CPU Isolation: 開機時指定某一核不主動參與排程,除非使用 set affinity 將任務指定給該核 Pthread barrier: 讓快的 thread 等慢的 thread time stamp counter (TSC): 可接露 CPU 週期數,只要 CPU 開著,周期數必為單調遞增 ### 第 13 週 增加取餘數效率的小技巧: 如果是對 2 的冪**取餘數**, **等於對 2 的冪 - 1 做 &** - 如: X % 16 = X & (16 - 1) - 所以可以將 ring buffer 的大小設定為 2 的冪 [zero-copy](https://hackmd.io/@sysprog/linux2020-zerocopy) [alignof c11](https://en.cppreference.com/w/c/language/_Alignof) page alignment: 因為 page 是記憶體操作的最小單元,必須保證有 align,才能避免非預期結果 FUTEX_WAIT FUTEX_WAKE 第十六周 在現代查表可能比 CPU 運算還慢約 50 倍,因此要避免查表 要學會懷疑、推理並做實驗驗證 - [展頻](https://zh.wikipedia.org/zh-tw/%E6%89%A9%E9%A2%91) - [調變 (modulation)](https://zh.wikipedia.org/zh-tw/%E8%AA%BF%E8%AE%8A) - [OFDM](https://zh.wikipedia.org/zh-tw/%E6%AD%A3%E4%BA%A4%E9%A0%BB%E5%88%86%E5%A4%8D%E7%94%A8) 面試要展現對公司的利益! 為何需要 boot loader? 因為 Linux kernel 太大,需要先初始化 DRAM。所以需要在 ROM 中初始化 SRAM,之後才能初始化 DRAM ![image](https://hackmd.io/_uploads/ByQVQ4kB0.png) > [millaker](https://hackmd.io/@sysprog/HyjiP43N0) [SD/MMC](https://wiki.csie.ncku.edu.tw/embedded/SDIO) # 6/13 - mmap 和 mmap2其實功能差不多,只是 mmap2是給系統實作用 - 編譯器最佳化 - constant propagation: 傳遞常數 - constant folding: 先計算常數再展開 - Static Single Assignment (SSA): 靜態單賦值,讓每個變數都只有出現過一次 ![image](https://hackmd.io/_uploads/rkeJsduHA.png) - bitwise AND 和 logical AND比較 logical AND 左側的 operator 是 false,右側的 operator 不會 evaluate (left-to-right evaluation) biteise AND 則會左、右側 operator都會 evaluate - Redis - 適合寫入少但讀取多的情境 - in-memory cache - RCU - 讀多寫少的情境,可以做到 lock-free (只要執行足夠長的時間,至少會有一個執行緒會有進展)