--- 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
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up