---
# System prepended metadata

title: 2024q1 Homework5 (assessment)
tags: [Linux Kernel]

---

# 2024q1 Homework5 (assessment)
contributed by < [`jimmylu890303`](https://github.com/jimmylu890303) >

## 閱讀〈[因為自動飲料機而延畢的那一年](https://blog.opasschang.com/the-story-of-auto-beverage-machine-1/)〉的啟發

### 閱讀心得
人生就是一連串的選擇。每一次的選擇都像是一場賭注，而在每個選擇中，應當付出百分之百的努力，才能取得成功。如果因為害怕失敗而不敢踏出第一步，就永遠無法品嘗成功的滋味。即使遇到失敗，也能從中獲得寶貴的經驗。最重要的是，在過程中能夠領悟到其中的意義。
> 人不付出犧牲，就得不到任何回報。如果要得到什麼，就必須付出同等的代價，這就是鍊金術的基本原則，等價交換。當時我們深信著，這就是這世界的真理。------《鋼之鍊金術師》

與作者一樣，我們都是平凡的人，並非天才。然而，與我最大的區別在於他敢於付出。他為了一個突發奇想的想法，願意花費長達14個月的時間去實現它。雖然我們可能不懂硬體也不懂機械，但只要我們願意坦誠面對困難並積極解決，就能克服一切障礙。因此，我認為持之以恆、堅持不懈的心態是最重要的。只要擁有這樣的心態，所有的困難都可以迎刃而解。

> 我們將秤冰塊的機構固定好後，將其傾斜，希望落入的冰塊會從尾端開始堆積。另外擋板可以避免冰塊超出支點，接下來，由我負責檢驗該設計下冰塊和荷重元產生的訊號是否有正比關係，誤差是否在可接受的範圍內。
於是我便重複「加冰塊、倒冰塊、測量訊號、紀錄」，做了超過480次

在實踐的過程中，我不斷嘗試，就像學習一樣。一開始可能會感到困惑，但經過多次的嘗試，總能找到解決的辦法。我希望能學習像作者一樣的心態，專注投入地完成每一件事情。

>你不能現在就放棄，要是現在就放棄的話，你這輩子日後遇到這種等級的困難，就只會想逃避而已。

最後，我對上述的話感觸很深。我在遇到困難時常常感到想要逃避，而不是勇敢地正視它。因此，我希望能夠將這句話銘記在心裡。

### 前期作業改進
1. [第二週測驗二](https://hackmd.io/XE_4cW4XQiiWDnE4ILsQqg?view#%E6%B8%AC%E9%A9%97%E4%BA%8C1)
2. [第三週測驗五](https://hackmd.io/RBGUOyPpRZGVjB-Fr5Reng#%E6%B8%AC%E9%A9%97%E4%BA%94)
### 課堂感受
在這幾周學習 Linux 核心實作的感受是我一直努力跟上每周的課程進度和作業，並從教材中學到了許多知識。雖然其中很多的部分屬於基礎知識，但或許是因為大學時基礎不夠扎實，所以整體閱讀教材時感到有些吃力。雖然進度稍微落後，但我覺得自己獲益良多。

在作業方面，我經常在趕作業的截止期限，交出去的作業品質都不夠好，雖然都能夠運作，但明顯缺乏細節。尤其是看到課堂上許多同學的作業表現，感覺他們非常厲害，與我有著天壤之別。他們甚至能夠在極短的時間內交付一個品質很好的作品。但透過觀察這些同學，我也希望有朝一日能夠追上他們的腳步。

整體來說，修這堂課非常的挑戰性，我需要補足的知識也非常地多，但是就如上述所說 `人不付出犧牲，就得不到任何回報。如果要得到什麼，就必須付出同等的代價`，所以即使很困難，也不要放棄，堅持下去後我相信我也會獲益良多。

### 研讀 課程教材 和 CS:APP 3/e
> [部分課程教材筆記](https://hackmd.io/Z1AhpuP-RF-XRKm-8QrE7A#%E4%BD%A0%E6%89%80%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84-C-%E8%AA%9E%E8%A8%80%E6%95%99%E6%9D%90%E9%96%B1%E8%AE%80)

在閱讀 CS:APP 第二章時，講述著數值系統的重要性，裡面介紹各個型態的轉換，資安漏洞就是常常會由不同型態的數值的轉換導致。

我以為數值轉換及運算是一個很微小簡單的事情，但是魔鬼確會藏在這些小細節之中。

書中提及一個知名的例子， `maxlen` 被定義成 int 型態，而 `KSIZE` 為 `unsigned` 的型態，當在執行 `KSIZE < maxlen` 的操作時， maxlen 會被轉換成 `unsigned` ，而這個隱性轉換大多數的工程師都不會注意到細節卻可以被駭客當作一個入侵的入口。

當今天有心人士將 `maxlen = 負數` 傳入這個函數時， mxalen 會被轉換成 `unsigned` (一個非常大的無號數)， 透過 `memcpy(user_dest, kbuf, len)` 就能夠存取到一些非法區域(如 kernel 中的敏感資訊)。
```c
/*
2 * Illustration of code vulnerability similar to that found in
3 * FreeBSD’s implementation of getpeername()
4 */

/* Declaration of library function memcpy */
void *memcpy(void *dest, void *src, size_t n);
/* Kernel memory region holding user-accessible data */

#define KSIZE 1024
char kbuf[KSIZE];

/* Copy at most maxlen bytes from kernel region to user buffer */
int copy_from_kernel(void *user_dest, int maxlen) {
    /* Byte count len is minimum of buffer size and maxlen */
    int len = KSIZE < maxlen ? KSIZE : maxlen;
    memcpy(user_dest, kbuf, len);
    return len;
}
```

並且因為底層系統使用 Binary 運作，所以書中也有提及一些編譯器的優化。

以乘以常數為例 : $x \cdot 14$
原先乘法可以被 `(x<<3) + (x<<2) + (x<<1)` 取代，並且乘法及除法所耗費的成本也比較多，所以使用位移指令可以達到增加程式的效率。

還有一個比較特別的技巧是 Dividing by a Powers of 2 (Rounding up)
`(x + (1 << k) - 1) >> k`

起初看到這個表達式會想說為何要加上 `(1 << k) - 1` 這個bias?
後來看著它的推導後才比較明白

考慮 $\lceil x/y \rceil = \lfloor (x+y-1)/y \rfloor$
假設 $x=qy+r$ where $0\leq r<y$ 
$\lceil x/y \rceil = \lfloor (x+y-1)/y \rfloor \\
    = \lfloor (qy+r+y-1)/y \rfloor \\
    = \lfloor q+(r+y-1)/y \rfloor \\
    = q+ \lfloor (r+y-1)/y \rfloor$
當 r=0 時， $\lfloor (r+y-1)/y \rfloor = 0$，當 r > 0 時，$\lfloor (r+y-1)/y \rfloor = 1$，所以這樣就可以進行向上取整得操作。

所以考慮 $y=2^k$ 情況，則為 `(x + (1 << k) - 1) >> k`

### Signal
#### 問題一
信號是在軟體層次上對中斷機制的一種模擬，在原理上，一個進程收到一個信號與處理器收到一個插斷要求可以說是一樣的。信號是非同步的，一個進程不必通過任何操作來等待信號的到達，事實上，進程也不知道信號到底什麼時候到達。

#### 信號種類
```
$ kill -l

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
```

<s>
##### 可靠信號與不可靠信號
- 不可靠信號 : 
Linux信號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的信號機制比較簡單和原始，後來在實踐中暴露出一些問題，因此，把那些建立在早期機制上的信號叫做 `不可靠信號`。 (1-31)
> Linux 中最主要的問題是信號可能丟失
- 可靠信號 : 
隨著時間的發展，實踐證明了有必要對信號的原始機制加以改進和擴充。所以，後來出現的各種Unix版本分別在這方面進行了研究，力圖實現`可靠信號`。由於原來定義的信號已有許多應用，不好再做改動，最終只好又新增加了一些信號，並在一開始就把它們定義為可靠信號，這些==信號支援排隊==，不會丟失。 

|            | 不可靠信號 | 可靠信號 |
| ---------- |:----------:|:--------:|
| 支援排隊   |     No     |   Yes    |
| 可附加資訊 |     No     |   Yes    |

##### 即時信號和非即時信號
- 非即時信號 : 不可靠信號都為非即時信號
- 即時信號 : 可靠信號為即時信號
</s>

> https://man7.org/linux/man-pages/man7/signal.7.html
> https://man7.org/linux/man-pages/man2/sigaction.2.html (注意 process vs. thread)

:::info
為何後續 linux 的維護者不將`不可靠信號`改成`可靠信號`?
額外分成非即時信號及即時信號的必要性?
:::
> 非即時信號及即時信號是網路上中文來源不明的教材，在參造 [signal](https://man7.org/linux/man-pages/man7/signal.7.html) 的文件中，沒提及到不可靠信號及非即時信號的內容。

#### 進程對信號的回應
- 忽略信號 : 對信號不做任何處理，其中，有兩個信號不能忽略： `SIGKILL` 及 `SIGSTOP`
- 捕捉信號 : 定義信號處理函數，當信號發生時，執行相應的處理函數
- 執行預設信號操作

### 課堂教材問題
#### 問題二
參閱老師的 [coro.c](https://github.com/sysprog21/concurrent-programs/tree/master/coro) 課程教材，可以發現在作任務的切換是使用 `setjmp` 和 `longjmp` 去做跳躍的動作( nonlocal goto )
:::info
```c=
jmp_buf Buf;
void B(){
    longjmp(Buf,1);
};

void A(){
    if(setjmp(Buf) == 0){
        printf("A setjmp address\n");
        B();
    }
    else{
        printf("Come back from longjmp");
    }
};
```

當在一開始 A 呼叫 `setjmp` 時， `setjmp` 會將 stack pointer, instruction pointer, the values of other registers, the signal mask 存在 jmp_buf 的 buffer 內 `longjmp` 作使用。但在執行 `setjmp` 時， pc 會指向 line 8 的位置。
當 B 使用 `longjmp` 時會恢復 Buf 內的資訊，那應該會執行`printf("A setjmp address\n");` 一次，但是真實的作用是會觸發 `setjmp(Buf) != 0` 行為則執行 else 內的行為。
:::
> ip 會儲存在 line 7 的位置，所以在 B 使用`longjmp` 時會恢復 Buf 內的資訊，觸發 `setjmp(Buf) != 0` 行為則執行 else 內的行為。
#### 問題三
:::info
在 /dev/ 下的裝置有 major number 和 minor number，在對 /dev/下的裝置作操作時， linux kernel 會透過 major number 去找到對應的 module ， 但是若有多個 device 對應到同個 major number，也是會找相同的 module，但是要如何透過不同的 minor number 去控制不同的驅動行為 ? 
:::
>多個裝置對應相同的 major number: 如果有多個裝置使用相同的 major number（例如同一類型的硬體裝置），每個裝置會有不同的 minor number。這些 minor number 可以在同一驅動程式模組中被不同的函式或處理邏輯所區分和處理。

### 期末專題
1. vwifi 虛擬無線網路裝置驅動程式和實驗環境 : [link](https://github.com/sysprog21/vwifi)
這是一個有關虛擬無線網路裝置驅動程式和實驗環境的專案，我希望透過這個專案的開發來學習韌體驅動相關技術，並且能夠利用這些驅動程式與其他裝置進行溝通。
2. 打造 Linux 虛擬攝影機裝置驅動程式 : [link](https://github.com/sysprog21/vcam)
這個專案可以透過編寫驅動程式來實現 Linux 核心與周邊設備之間的交互溝通，對於理解和應用韌體驅動相關技術將會非常有幫助。
3. RISC-V System Emulator + Linux : [link](https://github.com/sysprog21/semu)
在 [semu](https://github.com/sysprog21/semu) 上執行 Linux 是一個優秀的嵌入式系統開發專案。在計算機結構課程中接觸到 [FemtoRV](https://github.com/BrunoLevy/learn-fpga/blob/master/FemtoRV/README.md)，但當時未能成功將 RTOS 移植到 FemtoRV 上。因此，我希望在這個專案中能夠成功將 Linux 移植到 SEMU 平台上。

TODO: 將 semu 的部分 VirtIO 成果整合到 [kvm-host](https://github.com/sysprog21/kvm-host)
* semu 已有 virtio-net 和 [virtio-gpu](https://github.com/sysprog21/semu/pull/34)，而這些成果若可整合到系統模擬效率更高的 [kvm-host](https://github.com/sysprog21/kvm-host)，可顯著提高 Linux 系統模擬的可用性
    > [實作 virtio-net](https://hackmd.io/@fewletter/kvm-host)

TODO: 研讀 [KVM: Linux 虛擬化基礎建設](https://hackmd.io/@sysprog/linux-kvm), [打造以 KVM 為基礎的精簡虛擬機器管理程式](https://pretalx.coscup.org/coscup-2023/talk/JSGKMQ/), [2022 年報告](https://hackmd.io/@ray90514/kvm-host) 和 [2023 年報告](https://hackmd.io/@sysprog/rkro_FeSh)，以理解 KVM 和 VirtIO 原理，針對電腦網路和 2D 裝置模擬提出方案