linux2022
主講人: jserv / 課程討論區: 2022 年系統軟體課程
返回「Linux 核心設計」課程進度表Image Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
seHTTPd 是個高效的 web 伺服器,涵蓋並行處理、I/O 事件模型、epoll、React pattern,和 Web 伺服器在事件驅動架構的考量,可參見 高效 Web 伺服器開發。
預先準備的套件: (eBPF 作為後續分析使用)
取得程式碼和編譯:
預期可見 sehttpd
這個執行檔。接著透過內建的 test suite 來測試:
首先,我們可用「古典」的方法,透過 Apache Benching tool 對 seHTTPd 進行壓力測試。在一個終端機視窗執行以下命令:
切換到網頁瀏覽器,開啟網址 http://127.0.0.1:8081/
應在網頁瀏覽器畫面中見到以下輸出:
Welcome!
If you see this page, the seHTTPd web server is successfully working.
然後在另一個終端機視窗執行以下命令:
參考程式輸出: (數值若跟你的測試結果有顯著出入,實屬正常)
留意到上述幾項:
-k
參數: 表示 "Enable the HTTP KeepAlive feature",也就是在一個 HTTP session 中執行多筆請求-c
參數: 表示 concurrency,即同時要下達請求的數量-n
參數: 表示壓力測試過程中,期望下達的請求總量關於輸出結果,請詳閱 ab - Apache HTTP server benchmarking tool 說明。
ab
無法有效反映出多執行緒的特性 (ab
自身就消耗單核 100% 的運算量),因此我們才會在 khttpd 提供 htstress.c,後者提供 -t
選項,能夠依據測試環境的有效 CPU 個數進行分配。ab - Apache HTTP server benchmarking tool 的實作從今日的 GNU/Linux 或 FreeBSD 來說,算是過時且未能反映系統特性,除了 htstress
,尚可使用 wrk,該專案的訴求是
wrk is a modern HTTP benchmarking tool capable of generating significant load when run on a single multi-core CPU. It combines a multithreaded design with scalable event notification systems such as epoll and kqueue.
另一個可測試 HTTP 伺服器負載的工具是 httperf。
倘若你將 seHTTPd 執行後,不立刻關閉,隨即較長時間的等待和重新用上述 ab
多次測試 (變更 -n
和 -c
參數的指定數值) 後,可能會遇到以下錯誤狀況 (部分)
[ERROR] (src/http.c:253: errno: Resource temporarily unavailable) rc != 0
可用 $ grep -r log_err
搜尋原始程式碼,以得知現有的例外處理機制 (注意: 裡頭存在若干缺失,切勿「舉燭」)。
首先,研讀 Linux 核心設計: 透過 eBPF 觀察作業系統行為 以理解核心動態追蹤機制,後者允許我們使用非侵入式的方式,不用去修改我們的作業系統核心內部,不用去修改我們的應用程式,也不用去修改我們的業務程式碼或者任何系統配置,就可快速高效地精確獲取我們想要的資訊。
在 seHTTPd 原始程式碼的 ebpf 目錄提供簡易的 HTTP 封包分析工具,就是建構在 eBPF 的基礎之上,並透過 IO Visor 提供的工具來運作。
概念示意圖:
使用方式: (預先在另一個終端機視窗執行 $ ./sehttpd
)
注意: 這個工具預設監控 eth0
這個網路介面 (network interface)。倘若你的預設網路介面不是 eth0
,你需要依據 ip
工具的輸出,決定監控哪個網路介面。舉例來說,在某台 GNU/Linux 機器上執行以下命令:
你會見到若干輸出,如果你的環境裡頭已執行 Docker,輸出數量會很可觀,但不用擔心,排除 lo
, tun
, virbr
, docker
, br-
, veth
開頭的輸出,然後就剩下 enp5s0
這樣的網路介面 (端視你的網路硬體而有不同),於是可將上述命令改為:
然後打開網頁瀏覽器,多次存取和刷新 http://127.0.0.1:8081/
網址,然後你應可在上述執行 Python 程式的終端機見到類似以下的輸出:
關於上述程式運作的概況,可參考 Appendix C。
透過 eBPF 追蹤 fibdrv 核心模組的運作機制,可參見 0xff07 的共筆
現行 seHTTPd 運用 non-blocking I/O 和 I/O multiplexor 機制,在單執行緒仍可有效能表現,但若我們想更充分運用硬體特性,勢必要透過多執行緒和克服相關並行議題。請事先研讀 並行和多執行緒程式設計
引入 thread pool 的設計,需要非常小心,否則容易致使反效果 —— 分配更多的執行緒,但最終的伺服器效能還不如單執行緒為基礎的實作。應充分研讀 An Introduction to Lock-Free Programming,以掌握 lockless/lock-free 程式設計背景知識。
多執行緒的程式不容易除錯,可善用 ThreadSanitizer 這項已整合到 gcc/clang。修改 Makefile
,在 LDFLAGS =
後方新增下列:
之後 $ make clean all
即可編譯出支援 ThreadSanitizer 的 seHTTPd 執行檔。
在 Computed goto for efficient dispatch tables 一文提到:因為 switch 還是會有判斷數值的處理,所以在執行效率上 goto
還是會比使用 switch-case
來得好,而且使用 goto
也可以避免使用 switch-case
帶來 branch miss 的損失。
搭配 GNU extension 中提供的 Labels as Values,在 http_parser.c:http_parse_request_line
中的加入 conditions
作為 label address table 讓其可用 state
來查表,並直接利用 goto
跳躍到對應的程式碼。
接著將 switch
的部分取代為 goto
接著利用 perf 觀察使用 computed goto 改寫後關於 branch prediction 的表現:
使用 switch-case 的版本:
可見 computed goto 版本的伺服器因為在 branch miss 的數量較使用 switch-case 版本少,所以執行的時間得到改善。
接著因為在宣告 state
與 conditions
時裡面的名稱有高度的重疊,所以改以 X macro 的方式簡化宣告的程式碼
利用巨集組合 enumerate 的變數與 label 並將 macro 作為變數傳到 state_code
這個 macro 中,展開後就會得到與上方宣告一樣的結果
設計 dispatch()
的 macro 來進行各種情況的程式碼跳躍。
因為將 for 迴圈的功能也實作在 dispatch
中,所以使用 computed goto 進行 dispatch
之前,也要處理字串迭代的更新。
但考慮到在進入 for 迴圈的時候並不會將 pi
遞增,所以為了讓進入迴圈前與進入迴圈後 dispatch
的行爲與原先 for 迴圈的實作一致,此處將 pi
作為巨集參數的參數帶入,讓其可在二種不同的情況做不同的處理。
提示: 參考實驗程式碼: test_epoll_lt_and_et
http-parse-sample.py
運作機制,以及 eBPF 程式在 Linux 核心內部分析封包的優勢為何?WWWROOT
,例如 httpdsrc/http.c
裡頭傳遞檔案內容的實作 (對應到 serve_static
函式),思考在高並行的環境中,檔案系統 I/O 的開銷及改進空間;io_uring
改寫 seHTTPd,並比較原本使用 epoll
的處理效率$ grep -r TODO *
找出 seHTTPd 的待做事項,予以列出並充分闡述具體執行方法,例如涉及動態記憶體管理、長期閒置連線的處理機制等等。
mimalloc 的運用是個值得留意的改進方向,可參考共筆 mimalloc 實作機制和改善 和 mimalloc 實驗
編輯 Homework6 作業區共筆,將你的觀察、上述要求的解說、應用場合探討,以及各式效能改善過程,善用 gnuplot 製圖,紀錄於新建立的共筆
越早在 GitHub 上有動態、越早接受 code review,評分越高