分享到plurk 分享到twitter 分享到facebook

版本 d184f474b9fc8ff52918e2cb0492fc3824eb720e

User/Xalestar

Changes from d184f474b9fc8ff52918e2cb0492fc3824eb720e to 34a4300630cde3dd9fb16e7ab34fa0cc30501981

# 簡介
國立成功大學 資訊工程學系 115級

Github: [Xalestar](https://github.com/Xalestar)

# 2026 年 Linux 核心設計課程自我評量
## 成果發表與貢獻
5 分。

修改教材語句、錯字:

- [並行程式設計: 概念](https://hackmd.io/@sysprog/concurrency/%2F%40sysprog%2Fconcurrency-concepts)
    - 修改語句,使其通順:
    `2. process 使用 semaphore 時,通常扮演單一角色固定扮演一種角色:總是發出信號 (signal),或者總是接收信號 (wait) —— 。同一個 process 不會先後對同一個 semaphore 進行 signal 與 wait。換言之,process 要不擔任 producer,要不充當 consumer 的角色,不能同時兼任兩者兩者都是。semaphore 是為了保護 process 之間的執行同步正確。`
    - 修改標點符號,從 `,` 改為 `,`:
    `* Free (performance) lunch 指的是程式設計的效能可以透過 CPU 時脈的進步而得到改善。會說 over 是因為 CPU 的時脈因耗電和散熱的問題,難以樂觀地持續提升,所以程式設計師必須要修改程式才能改善效能`
    - 移除多餘的 `是`:
    `一個可再進入 ([reentrancy](https://en.wikipedia.org/wiki/Reentrancy_(computing))) 的函式是可被多個工作同時呼叫,而不會有資料不一致的問題。簡單來說,一個可再進入的函式,會避免在函式中使用任何共享記憶區 (global memory),所有的變數與資料均存在呼叫者的資料區或函式本身的堆疊區 (stack memory)。對常見的 C 編譯器來說,被呼叫 (callee) 之函式在返回之前,不會更動到呼叫者 (caller) 端的堆疊區。因此,即使該函式被不同的工作同時呼叫,由於在不同的堆疊區執行,互相之間是完全獨立的。`
    - 移除重複的 `執行` 二字:
    `每個執行單元都有自己的 stack , 但跟本來的執行單元共用 text, heap 與 data section。`
    - 修改標點符號,從 `,` 改為 `,`: 
    `* 並行性是種「架構程式」的概念。寫下一段程式之前,, 思考問題架構時就決定好的。`
- [Linux 核心設計: 不只是執行單元的 process](https://hackmd.io/@sysprog/linux-process)
    - 共 4 處修改,但查看 “versions” ,修改處卻沒有標示出來,故用截圖形式呈現修改
    ![Screenshot 2026-07-13 at 9.30.26 PM](https://hackmd.io/_uploads/ByetuDf4fl.png)
- [Linux 核心設計: 透過 eBPF 觀察作業系統行為
](https://hackmd.io/CJb5S2gxTCiX-__krLUFpg)
    - 修改語句,使其通順、用詞精確:
    `2. 載入核心驗證器: 核心驗證器會檢驗程式是否安全,若不安全則有權拒絕該 BPF bytecode;若通過驗證,它可以掛載(attach)至以下四種事件類型之一進行追蹤:`
    - 修改專有名詞:
    `* ARP (Address Resolution Protocol): 一種位址解析協定:`
    - 修改語句、用詞、標點符號:
        ```
        1. 每台主機都會在 ARP 快取 (ARP cache) 中建立一個 ARP 表格,用來記錄 IP 位址和實體位址的對應關係。這個 Table 的每一筆資料會根據自身的存活時間遞減而最終消失,以確保資料的即時性;
        2. 當發送主機有一個封包要傳送給目的主機﹐且已知目的主機的 IP 位址時,發送主機會先檢查自己的 ARP 表格中是否有該 IP 位址的實體位址對應。如果有,就直接使用此位址來傳送訊框(frame);如果沒有,則向網路廣播一個 ARP Request 訊框﹐查詢目的主機的實體位址。此訊框會包含發送端的 IP 位址和實體位址;
        3. 這時﹐網路上所有的主機都會收到這個廣播訊框﹐會檢查其中的 IP 欄位是否和自己的 IP 位址一致。如果不是則忽略﹔如果是則會先將發送端的實體位址和 IP 資料更新到自己的 ARP 表格去﹐如果已經有該 IP 的對應,則用新資料覆蓋原來的;然後再回應一個 ARP Reply 訊框給對方﹐告知發送主機關於自己的實體位址;
        4. 當發送端接到 ARP Reply 訊框之後﹐也會更新自己的 ARP 表格,然後就可以用此紀錄進行傳送了;
        5. 如果發送端沒有得到 ARP Reply,則宣告查詢失敗。
        ```

## 作業與隨堂測驗
6 分。

在作業中我學習到:
- 用字要精確,避免造成溝通上的誤會
- 要了解程式行為,需翻閱規格書,而非僅依執行結果妄下定論
- 數學在電腦科學中的重要性
    
我自認並沒有花費足夠的心力在作業上,許多問題也並沒有作答,直到後來一對一討論、期末專題、課堂問答才花費較多心力理解與實驗,不過有作答的部分都有查證並且確保自己有釐清細節與理解問題。

## 期末專題
9 分。

[Linux 核心設計專題:縮減系統啟動時間和客製化](https://hackmd.io/@sysprog/ByXHinRAZe)

### 專題目標

本專題以 BeagleBone Green Wireless 為平台,使用 Linux 7.0.5 與 Buildroot 建構可由 microSD 啟動的無線基地台。系統包含 BusyBox shell、hostapd、dnsmasq、nftables 與 BusyBox httpd Web UI,可讓外部裝置連線、取得 DHCP 位址,並查看 AP、用戶端及系統資訊。

主要目標是縮短系統從 `U-Boot SPL` 輸出至 hostapd 顯示 `AP-ENABLED` 的時間,同時保留可重現的 Buildroot external tree、核心設定、rootfs overlay、啟動腳本與 kernel patch。

### 系統建構過程

開發初期先使用 U-Boot、TFTP boot 與 NFS root 建立快速測試環境,避免每次修改 kernel 或 rootfs 都需重燒 SD 卡。功能穩定後,再以 Buildroot 統一產生 U-Boot、kernel、DTB 與 rootfs,建立可重現的建構流程。

系統功能依序完成如下:

- 建立 UART console 與 U-Boot 網路環境。
- 使用 `wpa_supplicant` 驗證 wl18xx 韌體、驅動與無線網路。
- 建立 hostapd AP、dnsmasq DHCP/DNS、nftables NAT 與 IP forwarding。
- 以 BusyBox httpd 製作 Web UI。
- 將設定檔、overlay 與啟動腳本整合至 Buildroot external tree。

初始版本使用 systemd,希望透過平行啟動縮短時間;但實測發現 AP 啟動具有「驅動載入 → `wlan0` 建立 → 介面啟用 → hostapd → DHCP/DNS」的相依關係,平行化效果有限。systemd 亦占用約 9.8 MB 儲存空間,PID 1 常駐記憶體約 8.4 MB,因此改用 BusyBox init,並自行撰寫 AP 啟動腳本。

此階段同時解決 `wlan0` 尚未建立便執行命令的競態、nftables 規則重複載入,以及前景執行 hostapd 阻塞 init script 等問題。rootfs 也改為唯讀,降低重複測試時檔案系統 recovery 造成的時間波動。

### 啟動時間改善歷程

改善工作分別涵蓋 toolchain、userspace、啟動腳本、kernel、U-Boot 與 DTS:

| 階段 | 主要改善 | 平均時間 |
|---|---|---:|
| 初始版本 | BusyBox AP 基準系統 | 18.827 s |
| Userspace 精簡 | musl、靜態連結、移除 dbus、調整啟動腳本 | 8.508 s |
| 服務與核心調整 | 啟用 TRNG、移除非必要服務、加入 `quiet` | 5.623 s |
| 驅動與 DTS 精簡 | 停用未使用 eMMC、降低 probe 與 tracing 成本 | 4.510 s |
| 最終版本 | 移除非必要 driver、檔案與核心功能 | **4.059 s** |

整體啟動時間由 18.827 秒降至 4.059 秒,共縮短 14.768 秒,改善約 **78.4%**,速度約為原始版本的 **4.6 倍**。

### 主要問題與解決方法

1. **`regulatory.db` 造成 hostapd timeout**  
   `cfg80211` 為 built-in driver 時,會在 rootfs 掛載前讀取 `regulatory.db`,導致讀取失敗。hostapd 設定 `country_code=TW` 後會等待更新,額外耗費約 5 秒。最後使用 `CONFIG_EXTRA_FIRMWARE` 將資料庫內建至核心,成功消除 timeout。

2. **隨機數不足阻塞 hostapd**  
   精簡背景服務後,早期中斷來源減少,CRNG 無法及時 ready,使 hostapd 在產生 WPA2 隨機數時被阻塞。啟用 AM335x 硬體 TRNG 後即可穩定啟動。

3. **未使用裝置增加 probe 時間**  
   系統由 microSD 啟動,但 DTS 仍啟用 eMMC,產生額外 MMC 探測與 deferred probe 成本。透過 kernel patch 停用 eMMC 後,啟動時間進一步下降。

4. **UART 輸出影響量測結果**  
   kernel 將 log 同步輸出至低速 UART 時,曾造成約 0.9 秒阻塞,因此加入 `quiet`,並以 kernel timestamp 校正外部 serial 時間。

5. **initramfs 不一定較快**  
   將 rootfs 打入 initramfs 後,zImage 增至約 22 MB,SD 讀取與解壓縮成本反而增加超過 1 秒,因此最終採用精簡的外部 rootfs。

### 量測方法與結果

量測使用 Host UART 搭配 `grabserial`,以 `U-Boot SPL` 為起點、`wlan0: AP-ENABLED` 為終點,並搭配 `initcall_debug`、bootgraph、bootchart、`strace` 與自訂 marker 分析各階段。

| 版本 | 次數 | 平均 | 標準差 | 最小值 | 最大值 |
|---|---:|---:|---:|---:|---:|
| 初始版本 | 3 | 18.826569 s | 0.077415 s | 18.740395 s | 18.890237 s |
| 最終版本 | 5 | 4.058861 s | 0.004074 s | 4.052172 s | 4.062844 s |

初始與最終版本相差約 14.77 秒,明顯大於量測變異,足以支持整體改善有效。不過,本量測未包含上電後 ROM 至 SPL 輸出前的時間,且各階段可能同時修改多項設定,因此結果應視為累積改善;若要確認單一修改的因果效果,仍需進行控制其他條件的 A/B 交錯測試。

## 與授課教師的互動
9 分。

### 一對一討論

分別於 4 月 29 日及 5 月 8 日與授課教師進行兩次討論,內容包含 IEEE 754 位元操作,以及嵌入式 Linux 專題的系統規劃與量測方法。

#### 1. IEEE 754 位元操作
討論以不使用 FPU 實作單精度浮點數 `fdiv8()` 為例。由於除以 8 等同於將二進位指數減 3,初期構想是直接修改 exponent 欄位,但原始實作使用 `(int)x` 取得浮點數內容,實際上只會進行數值轉型,無法取得 IEEE 754 的原始位元表示。修正後改以 `memcpy()` 在 `float` 與 `uint32_t` 之間複製位元。

討論中也發現,直接執行 `bits -= 3u << 23` 僅適用於有限且 exponent 大於 3 的 normal number。若輸入為 subnormal、±0、NaN、±∞,或位於 normal 與 subnormal 的邊界,可能造成借位、欄位破壞或錯誤結果。若要完整支援 IEEE 754,還必須處理 rounding、NaN payload 與例外旗標。

此外,單精度 exponent 採用 bias 127,使 normal number 的實際指數範圍為 −126 至 +127,並保留欄位 0 與 255 分別表示 zero/subnormal 及 infinity/NaN。此設計能銜接 subnormal 範圍,也有利於硬體進行比較與例外判斷。

#### 2. 嵌入式 Linux 專題規劃
討論後決定使用 Buildroot 統一管理 toolchain、BusyBox、kernel、U-Boot、rootfs 與套件設定,而非分別手動建構各元件。系統預計支援 `wpa_supplicant` station mode、hostapd AP、WPA2 與相關無線功能,並以手機能否看到 SSID 作為使用者可感知的完成指標。

同時比較 BusyBox init 與 systemd。systemd 可依 unit 相依關係平行啟動服務,但 AP 流程包含驅動載入、介面建立及 hostapd 啟動等先後關係,平行化效果可能有限;BusyBox init 則具有體積小、依賴少的優勢。教師建議以實際量測結果判斷,而非只根據架構特性選擇。

因此,後續實作確立以下方向:

- 使用 bootchart 等工具分析 kernel、rootfs、init script、韌體與 driver probe。
- 重複量測並呈現平均值、標準差、最小值及最大值。
- 說明 UART、SD 卡、檔案系統狀態與無線初始化等誤差來源。
- 使用誤差棒比較不同版本,避免以單次結果推論改善效果。

### 課堂問答軌跡
筆記:[2026-05-26/06-02 問答簡記](https://hackmd.io/CZEAQrigR0KL2MHBbK8qnw?view)

5 月 28 日參與課堂問答,並於 6 月 2 日接續與授課教師討論 orphan 與 zombie process。為釐清兩者差異,我撰寫 `fork()` 測試程式,搭配 `ps` 與 bpftrace 觀察核心事件。

#### 1. Orphan process 實驗
實驗讓 parent 建立 child 後立即退出,child 則繼續執行。初期看到 PID 變動時,曾誤以為 parent 的 PID 發生改變;進一步追蹤 `sched_process_fork`、`sched_process_exit` 與 `forget_original_parent` 後,確認實際流程為:

1. parent 建立 child。
2. parent 結束。
3. kernel 執行 reparenting。
4. child 的 PPID 變為 1,由 systemd 收養。

收養機制可確保 child 日後結束時,仍有新的 parent 接收 `SIGCHLD` 並回收其資源。

#### 2. Zombie process 實驗
另一個實驗讓 child 立即結束,而 parent 持續執行且不呼叫 `wait()`。此時 `ps` 顯示 child 為 `<defunct>`,狀態為 `Z`。由於 zombie 已無執行內容,使用 `kill -9` 也無法移除;必須由 parent 呼叫 `wait()`/`waitpid()`,或在 parent 結束後交由 PID 1 收養並回收。

透過 bpftrace 追蹤 `release_task` 與 `sched_process_free`,確認「child 結束 → 成為 zombie → 等待回收 → task 資源釋放」的完整流程。Zombie 雖不占用 CPU 排程時間,仍保留 PID 與結束狀態;若長期大量累積,可能耗盡系統資源並影響新 process 或 thread 的建立。

## 所見所聞所感
10 分。

關於〈因為自動飲料機而延畢的那一年〉,目前在網路上已找不到完整的系列文章,只能找到前四集。讀完現存的內容及其他人的節錄後,令我印象最深、也最受觸動的是作者的這段話。雖然它並未出現在前四集之中,但在[一位先前修課學長撰寫的心得](https://hackmd.io/@yozz/2023-hw5)裡有所節錄:
> 「飲料機的程式愷宏是寫不出來的,之所以能在一個月內完成,是因為我在大學期間就寫過好幾個網站了。愷宏能和工廠溝通、設計出可用的零件,是因為他在大一就在跑工廠做東西了。紘銘能輕易的設計出飲料機的電路,是因為他曾花了很多時間在電子電路課上頭,做了很多習題,纏著教授把每個疑惑都搞懂才罷休。」

這段話之所以觸動我,是因為它讓我想起第一次與老師一對一討論時,我在最後問了老師一個問題:「老師都是怎麼管理時間的?畢竟我們學生需要處理的事情應該比老師少很多,但我卻常常覺得時間不夠用,想做的事情很多,卻總是做不完;老師卻能同時處理學生、工作及自己的專案等各種事務。」

老師以自己大學時與同學開發遊戲的經歷為例,告訴我:「當你曾經深入鑽研一件事情,投入大量心力將它徹底弄懂之後,往往會發現自己的時間逐漸變多了——即使日後面對的問題未必與過去鑽研的內容直接相關,當時培養出的思考能力與經驗依然能派上用場。此外,與他人溝通也會變得更加順利;因為你很清楚自己在說什麼、想表達什麼,自然能減少耗費在溝通上的心力。」

這段話也完全呼應了我自身的問題。過去的我經常花費大量時間搜尋資料,卻只用相對少的時間深入思考;接收了許多資訊,卻鮮少進一步追問:「為什麼?其中有沒有矛盾?這個做法真的比較好嗎?」我的思考方式似乎早已習慣圍繞著「如何拿高分、如何應付考試」等僵化的問題打轉,而這些事情實際上對於培養自己的思考深度,乃至未來的成長,都幾乎沒有幫助。

在這門課中,最讓我感受到腦中思考開始「活化」的時刻,是一次課堂問答。當時老師問了關於 process 中 orphan 與 zombie 的問題。或許是因為緊張,我甚至將「行程」說成了「進程」,回答自然也不盡理想。然而,在老師與其他同學問答的同時,我抓緊空檔重新做了一次最原始的實驗,並有了新的發現:PID 會在某個時刻突然發生變化。後來,我立刻提出自己的觀察,老師也進一步指引我使用 bpftrace 進行實驗。

在下一次上課前,我完成了實驗,也進一步釐清其中的細節與相關設計緣由。到了下次上課,我再次與老師進行問答。這一次,在問答的過程中,我終於有了一種「自己好像真的準備好了」的感覺。

從一對一討論、課堂問答到期末專題,這些經驗使我明白,其實自己是做得到的——前提是,我必須以同樣認真的心態,面對並解決每一個值得在意的細節。

## 分數計算
| 項次 | 名稱 | 分數 |
| ---- | ---- | ---- |
| 1    |   成果發表與貢獻   |    5  |
| 2    |  作業與隨堂測驗    |    6  |
| 3    |   期末專題   |   9   |
| 4    |   與授課教師的互動   |   9   |
| 5    |   所見所聞所感   |   10   |

---
Plan B:
$$ 1 + floor(GEOMEAN) = 1 + floor[(5 \times 6 \times 9 \times 9 \times 10)^{1/5}] = 1 + 7 = 8 $$