Try   HackMD

Linux 核心設計 (2025): 第 6 份作業解說

read 一開始並不會到 user space 一定是在 kernel 處理,所以 user space 必須去 kernel 讀取網路封包,接著進行某些加工後,再 write 回 kernel,在透過驅動程式將封包交給 client 端(網路瀏覽器、社群軟體)。

所以這過程有大量的資料複製要處理。

r1Iqseg1gg

sendfile 合併了傳統的 read 及 write。原先的步驟是從檔案系統讀取內容傳到 user space,再將資料從 user space 傳到 kernel,合併後的優點是 user space 只要檢查執行結果就好。所以如今的網路伺服器幾乎都在 user space 實作。

io_uring 是 Linux 的明星技術,受到了廣泛的關注。

epoll 這個系統呼叫可監控多個事件。但本質上是同步的,而 io_uring 是 AIO。

Ftrace 也是動態追蹤 linux 核心的工具,在授課老師撰寫的排程器書籍第六章有相關介紹,主要用於追蹤特定時間段,kernel 某部份的程式碼執行時間

kecho

是運作在 kernel 的 server。

將專案 clone 下來後,使用 make 進行編譯。

接著要掛載 kecho.ko 檔。

$ sudo insmod kecho.ko  

接著可透過下列命令來檢查是否運作:

$ telnet localhost 12345

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

接著輸入下方命令可以離開:

^] 
telnet> q
Connection closed.

其中 ^] 並非直接輸入,而是按下 ctrl+]

專案中的 ./bench 可用來效能分析,會將結果輸出至 bench.txt

$ ./bench
$ vim bench.txt

0 31
1 35
2 38
3 41
4 48
5 48
6 51
7 57
8 55
9 68
...

user-echo-server

是運作在 user space 的 server

使用之前要先將 kecho 解除:

$ sudo rmmod kecho

再執行一次 make check

$ make check
make -C /lib/modules/6.11.0-21-generic/build M=/home/boju/Linux2025/kecho KBUILD_VERBOSE= modules
make[1]: Entering directory '/usr/src/linux-headers-6.11.0-21-generic'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
  You are using:           gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
make[1]: Leaving directory '/usr/src/linux-headers-6.11.0-21-generic'
Preparing...
Send message via telnet
Progress : [########################################] 100%
Complete

上述進度條的相關實作在 scripts/util.sh

drop-tcp-socket
這也是一個核心模組,若 kecho 崩潰,但沒有釋放其佔用的埠,可用將指定的埠關掉。

seHTTPd

  • Ractor pattern
    大量存在於 server 中,針對大量連線的情境,並沒有對每個單一的連線請求建立獨立的 thread,其想法在於,只先準備一個 thread,再轉送給其他 handler。

將專案 clone 下來後,主要要看的程式碼在 /src 中

epoll 效率很高,為何還要 select 及 poll??

使用場景不同,如同排序演算法,

編譯完後使用下列命令執行伺服器:

$ ./sehttpd

Web server started.

開啟另一個終端機視窗,輸入下列命令可以監看伺服器的效能:

$ ab -n 10000 -c 500 -k http://127.0.0.1:8081/

這個跑超級慢。

結果如下:

Server Software:        
Server Hostname:        127.0.0.1
Server Port:            8081

Document Path:          /
Document Length:        0 bytes

Concurrency Level:      500
Time taken for tests:   874.832 seconds
Complete requests:      1747
Failed requests:        0
Keep-Alive requests:    0
Total transferred:      424764 bytes
HTML transferred:       0 bytes
Requests per second:    2.00 [#/sec] (mean)
Time per request:       250381.240 [ms] (mean)
Time per request:       500.762 [ms] (mean, across all concurrent requests)
Transfer rate:          0.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   500  501   0.2    501     501
Waiting:        0    0   0.0      0       0
Total:        500  501   0.2    501     501

Percentage of the requests served within a certain time (ms)
  50%    501
  66%    501
  75%    501
  80%    501
  90%    501
  95%    501
  98%    501
  99%    501
 100%    501 (longest request)

httperf 也可作為伺服器效能的評估工具。

須從原始程式碼編譯。

在 lab0-c 評估常數時間時,可以將 outlier 去除,但反映在網頁伺服器上,這個議題須更慎重考慮。

BPF 程式一但編譯後變成了 byte code,就會注入到 Linux Kernel。

khttpd

一樣先使用 make 編譯,編譯完成後要掛載,命令如下:

$ sudo insmod khttpd.ko

同時也可以自行指令埠要用幾號:

$ sudo insmod khttpd.ko port=1999

接著使用下列命令可以建立客戶端連線:

$ wget localhost:1999

用下方命令可以觀測 port 狀況:

$ sudo netstat -apn | grep 8081

tcp        0      0 0.0.0.0:8081            0.0.0.0:*               LISTEN      9362/./sehttpd 

我還不知道為啥是觀測 8081,因為上面指定的是 1999。

重新看了一下影片,老師有先解除掛載,然後再重新掛載一次,並且這次沒有指定 port。不過當我進行一樣的操作時,出現下方資訊:

$ sudo rmmod khttpd 
$ sudo insmod khttpd.ko

insmod: ERROR: could not insert module khttpd.ko: Address already in use

還不確定問題所在。

接著可以使用 htstress 進行壓力測試:

./htstress -n 100000 -c 1 -t 4 http://localhost:8081/

結果如下:

0 requests
10000 requests
20000 requests
30000 requests
40000 requests
50000 requests
60000 requests
70000 requests
80000 requests
90000 requests

requests:      100000
good requests: 100000 [100%]
bad requests:  0 [0%]
socket errors: 0 [0%]
seconds:       1.114
requests/sec:  89732.051

下方命令可查看與模組相關的 kernel thread:

$ ps -ef | grep khttpd                                                      0|1 err  17:04:54 
root        4149       2  0 16:40 ?        00:00:00 [khttpd]
boju      105022    3983  0 17:04 pts/0    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox --exclude-dir=.venv --exclude-dir=venv khttpd

改進網頁伺服器性能,可以透過 ftrace 來追蹤,首先要先確認目前系統是否有 ftrace:

$ cat /boot/config-`uname -r` | grep CONFIG_HAVE_FUNCTION_TRACER

CONFIG_HAVE_FUNCTION_TRACER=y

接著是結合剛剛的壓力測試去撰寫簡單的測試腳本:

#!/bin/bash
TRACE_DIR=/sys/kernel/debug/tracing

# clear
echo 0 > $TRACE_DIR/tracing_on
echo > $TRACE_DIR/set_graph_function
echo > $TRACE_DIR/set_ftrace_filter
echo nop > $TRACE_DIR/current_tracer

# setting
echo function_graph > $TRACE_DIR/current_tracer
echo 3 > $TRACE_DIR/max_graph_depth
echo http_server_worker > $TRACE_DIR/set_graph_function

# execute
echo 1 > $TRACE_DIR/tracing_on
./htstress localhost:8081 -n 2000
echo 0 > $TRACE_DIR/tracing_on

將其命名為 test.sh,可以以下列命令執行:

$ sudo bash test.sh

0 requests
200 requests
400 requests
600 requests
800 requests
1000 requests
1200 requests
1400 requests
1600 requests
1800 requests

requests:      2000
good requests: 2000 [100%]
bad requests:  0 [0%]
socket errors: 0 [0%]
seconds:       0.299
requests/sec:  6681.901

每秒可以回應將近 7000 個連線,相比剛剛少了很多。

接著去追蹤 ftrace 的結果:

$ sudo cat /sys/kernel/debug/tracing/trace

會發現花費許多時間在:

http_parser_callback_message_complete [khttpd]();

從這些花費時間較長的函式去改善,就有相當大的優化空間。

此為本作業重要的要求。

ftrace 還能追蹤函式的階層關係,我還沒詳細去操作過。