--- tags: linux2025 --- # [2025q1](https://wiki.csie.ncku.edu.tw/linux/schedule) 第 8 週測驗題 :::info 目的: 檢驗學員對 LKMPG 及 CPU 排程器的認知 ::: ==[作答表單: 測驗 `1`](https://docs.google.com/forms/d/e/1FAIpQLScs0R8eQr-YTrSjj8cV956Ese2OMA2wAZWjw4zuHEmJMq5LoA/viewform?usp=dialog)== ### 測驗 `1` [第一份作業](https://hackmd.io/@sysprog/linux2025-lab0)提及[修改過的 `tiny-web-server`](https://hackmd.io/@sysprog/linux2025-lab0/%2F%40sysprog%2Flinux2025-lab0-c),讓學員及早理解電腦網路通訊、[客戶—伺服器通訊模型](https://en.wikipedia.org/wiki/Client%E2%80%93server_model),還有相關的 socket 程式設計 (對照 [CS:APP 第 11 章](https://hackmd.io/@sysprog/CSAPP-ch11))。為了檢驗學員對於 [LKMPG](https://sysprog21.github.io/lkmpg/) 的認知,我們開發一套 Linux 核心模式的網頁伺服器 (in-kernel web server),命名為 `kweb`,展示如何在 Linux 核心內部直接建立 TCP 伺服器。 `kweb` 在指定的埠號(port number,預設 `9999`)進行監聽,並在接收到連線後提供簡易的 HTTP 回應。其主要流程如下: 1. 建立與監聽 socket - 使用 `__sock_create()`(或 `sock_create_kern()`)在指定網路名稱空間(net namespace)中建立 TCP socket - 綁定(bind)到指定的 IP 和埠號,並呼叫 `kernel_listen()` 進行監聽 - 於 socket 收到新連線時,由 `sk_data_ready` 函式觸發 [work item](https://docs.kernel.org/core-api/workqueue.html),負責呼叫 `kernel_accept()` 來接受連線 2. 接受新連線 - 藉由 `kernel_accept()` 取得新的 `client_sock`,並將其包裝在一個工作結構(`struct client_work_queue`)中 - 使用 `schedule_work()` 將實際處理客戶端的流程排入系統工作佇列,使每個連線皆可在獨立的 work context 處理 3. 回應客戶端 - 在 `client_handler()` 中,`kweb` 使用 `sock_sendmsg()` 或 `kernel_sendpage()` 傳送固定的 HTTP 回應給客戶端 - 該回應包含簡單的標頭(`HTTP/1.1 200 OK`)以及測試訊息;另外也加入了 `Content-Length` 等標頭,使其符合標準的 HTTP 格式 4. 狀態控制 (藉由 sysfs) - `kweb` 會在 `/sys/kernel/webserver/net/data/status` 建立一個屬性檔 (attribute),並以讀寫檔案的方式來啟用或停用伺服器。 - 停用伺服器時,會先取消(cancel)與清空(flush)正在等待的接受連線工作,以避免 race condition;然後才真正釋放監聽中的 socket 5. 多網路[名稱空間](https://man7.org/linux/man-pages/man7/namespaces.7.html) (netns) 支援 - 以 `netns_subsys_ops` 配合 Linux 的 per-net 機制,在不同的網路名稱空間中維持獨立的伺服器實例 (instance) - 從使用者端看來,可以藉由 `unshare -t net` 建立新 netns,並在該空間中掛載 sysfs 後,使用同樣的啟停方式來管理伺服器。 以下為使用 kweb 的範例操作方式,並以繁體中文進行說明。此範例以預設的埠號 9999 舉例,您可在載入模組時透過參數改成其他埠號。 考慮以下 `Makefile` 內容: (注意 tab) ``` MODNAME := kweb obj-m += $(MODNAME).o KVER ?= $(shell uname -r) KSRC ?= /lib/modules/$(KVER)/build all: make -C $(KSRC) M=$(PWD) modules clean: make -C $(KSRC) M=$(PWD) clean ``` 編譯核心模組: `make`,完成後會產生一個名為 `kweb.ko` 的可載入模組檔 (LKM)。隨後使用以下命令將 `kweb` 模組載入核心: ```shell $ sudo insmod kweb.ko [host=0.0.0.0] [port=9999] ``` - `host`:要綁定的 IP 位址(若不指定,預設為 `0.0.0.0`,表示在所有網卡監聽) - `port`:要監聽的 TCP 埠號(若不指定,預設為 9999) 載入後,`kweb` 會在 `/sys/kernel/webserver/net/data` 中建立名為 `status` 的屬性檔案,用來控制伺服器的啟用與停用。 * 啟用伺服器: `echo 1 | sudo tee /sys/kernel/webserver/net/data/status` 一旦啟用,kweb 會在指定(或預設)的 IP:9999 上開始監聽。 * 測試連線: 可用 `nc`(netcat)或 `curl` 進行測試。例如 `nc 127.0.0.1 9999`。若成功連線,應可看到包含 `HTTP/1.1 200 OK` 與訊息內容的回應 * 停用伺服器: `echo 0 | sudo tee /sys/kernel/webserver/net/data/status` Namespace 使用方式: 1. 建立與進入新網路名稱空間。利用以下命令產生並進入一個新的 netns(只影響該 shell): ``` $ sudo unshare -t net sh ``` 進入後,可以執行 `ip a` 查看新 netns 裡可用的網路介面。如果還沒啟用 lo(127.0.0.1),可以: ``` $ ip addr add 127.0.0.1/8 dev lo $ ip link set lo up ``` 2. 在新 netns 中掛載 sysfs。為了操作 kweb 的 sysfs 介面,需要在新 netns 中掛載 sysfs: ``` mkdir /mnt/sysfs mount -t sysfs sysfs /mnt/sysfs ``` 之後即可在 `/mnt/sysfs/kernel/webserver/net/data/status` 找到相同的 `status` 屬性檔。 3. 啟用伺服器(新 netns 與前述方式相同: ``` echo 1 | sudo tee /mnt/sysfs/kernel/webserver/net/data/status ``` 此時只會在新 netns 中開啟一個獨立的伺服器埠(同一個 IP:Port),與原本的 netns 互不影響。 4. 回到舊 netns - 離開該 shell 即可回到原先 netns(或在另一個終端視窗操作原本的 netns) - 新 netns 內啟用的伺服器依然存在,直到您將模組移除或在該 netns 中將其停用 當您不再需要此網頁伺服器時,可用以下命令將模組從核心卸載,並同時停止所有網路名稱空間中的 `kweb` 伺服器: ```shell $ sudo rmmod kweb ``` 此動作也會移除 `/sys/kernel/webserver` 相關路徑。 `kweb.c` 的主要程式碼可見 [kweb.c](https://gist.github.com/jserv/8f6b00ef84b72a6f17a8bbf0827e24c9)。作答規範: * `AAAA`, `BBBB`, `CCCC`, `DDDD` 皆為有效的 C 語言表示式,且該依循一致的程式碼風格 (注意空白字元!) * `AAAA` (第 158 行) 和 `DDDD` (第 208 行) 包含 `container_of` 巨集 * `BBBB` 和 `CCCC` 該使用 Linux [workqueue](https://docs.kernel.org/core-api/workqueue.html) 提供的函式呼叫 * 均不包含分號 (即 `;`) 並以最精簡的形式書寫 :::success 延伸問題: 1. 解釋上述程式碼運作原理 2. 以上述 kweb 為基礎,借助[第 6 份作業](https://hackmd.io/@sysprog/linux2025-ktcp)的基礎,改善程式碼的效能和並行能力 :::
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.