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

User/Xalestar

簡介

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

Github: Xalestar

2026 年 Linux 核心設計課程自我評量

成果發表與貢獻

5 分。

修改教材語句、錯字:

  • 並行程式設計: 概念
    • 修改語句,使其通順: 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
    • 共 4 處修改,但查看 “versions” ,修改處卻沒有標示出來,故用截圖形式呈現修改 Screenshot 2026-07-13 at 9.30.26 PM
  • Linux 核心設計: 透過 eBPF 觀察作業系統行為
    • 修改語句,使其通順、用詞精確: 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 核心設計專題:縮減系統啟動時間和客製化

專題目標

本專題以 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 問答簡記

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 分。

關於〈因為自動飲料機而延畢的那一年〉,目前在網路上已找不到完整的系列文章,只能找到前四集。讀完現存的內容及其他人的節錄後,令我印象最深、也最受觸動的是作者的這段話。雖然它並未出現在前四集之中,但在一位先前修課學長撰寫的心得裡有所節錄:

「飲料機的程式愷宏是寫不出來的,之所以能在一個月內完成,是因為我在大學期間就寫過好幾個網站了。愷宏能和工廠溝通、設計出可用的零件,是因為他在大一就在跑工廠做東西了。紘銘能輕易的設計出飲料機的電路,是因為他曾花了很多時間在電子電路課上頭,做了很多習題,纏著教授把每個疑惑都搞懂才罷休。」

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

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

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

在這門課中,最讓我感受到腦中思考開始「活化」的時刻,是一次課堂問答。當時老師問了關於 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 \]