--- title: 2020 年春季 Linux 核心設計課程作業 —— seHTTPd image: https://repository-images.githubusercontent.com/253265147/e39b3700-77a9-11ea-9676-267ecffaedd4 description: 檢驗學員對 Linux 核心 I/O multiplexor, non-blocking I/O、事件驅動,和多執行緒程式設計的認知 --- # H10: sehttpd ###### tags: `linux2020` > 主講人: [jserv](http://wiki.csie.ncku.edu.tw/User/jserv) / 課程討論區: [2020 年系統軟體課程](https://www.facebook.com/groups/system.software2020/) :mega: 返回「[Linux 核心設計](http://wiki.csie.ncku.edu.tw/linux/schedule)」課程進度表 ## :memo: 預期目標 * 學習 I/O 模型 (blocking vs. non-blocking; synchronous vs. asynchronous) 和對應的 Linux 系統呼叫; * 建構高效事件驅動的 web 伺服器,過程中深度體會 Linux 系統呼叫、行程和執行緒的運作機制; * 學習透過 eBPF 進行作業系統層級的動態分析,並針對 web 伺服器發展特定的分析工具; ## :shark: seHTTPd [seHTTPd](https://github.com/sysprog21/sehttpd) 是個高效的 web 伺服器,涵蓋並行處理、I/O 事件模型、epoll、[React pattern](http://en.wikipedia.org/wiki/Reactor_pattern),和 Web 伺服器在事件驅動架構的考量,可參見 [高效 Web 伺服器開發](https://hackmd.io/@sysprog/fast-web-server)。 預先準備的套件: (eBPF 作為後續分析使用) ```shell $ sudo apt install wget $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD $ echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list $ sudo apt-get update $ sudo apt-get install bcc-tools linux-headers-$(uname -r) $ sudo apt install apache2-utils ``` 取得程式碼和編譯: ```shell $ git clone https://github.com/sysprog21/sehttpd $ cd sehttpd $ make ``` 預期可見 `sehttpd` 這個執行檔。接著透過內建的 test suite 來測試: ```shell $ make check ``` ### 對 seHTTPd 進行壓力測試 首先,我們可用「古典」的方法,透過 [Apache Benching tool](https://httpd.apache.org/docs/current/programs/ab.html) 對 seHTTPd 進行壓力測試。在一個終端機視窗執行以下命令: ```shell $ ./sehttpd ``` 切換到網頁瀏覽器,開啟網址 `http://127.0.0.1:8081/` 應在網頁瀏覽器畫面中見到以下輸出: :::info Welcome! If you see this page, the seHTTPd web server is successfully working. ::: 然後在另一個終端機視窗執行以下命令: ```shell $ ab -n 10000 -c 500 -k http://127.0.0.1:8081/ ``` 參考程式輸出: (數值若跟你的測試結果有顯著出入,實屬正常) ```shell Server Software: seHTTPd Server Hostname: 127.0.0.1 Server Port: 8081 Document Path: / Document Length: 241 bytes Concurrency Level: 500 Time taken for tests: 0.927 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 4180000 bytes HTML transferred: 2410000 bytes Requests per second: 10784.81 [#/sec] (mean) Time per request: 46.361 [ms] (mean) Time per request: 0.093 [ms] (mean, across all concurrent requests) Transfer rate: 4402.39 [Kbytes/sec] received ``` 留意到上述幾項: * `-k` 參數: 表示 "Enable the HTTP KeepAlive feature",也就是在一個 HTTP session 中執行多筆請求 * `-c` 參數: 表示 concurrency,即同時要下達請求的數量 * `-n` 參數: 表示壓力測試過程中,期望下達的請求總量 關於輸出結果,請詳閱 [ab - Apache HTTP server benchmarking tool](https://httpd.apache.org/docs/current/programs/ab.html) 說明。 :notebook: 需要注意的是,`ab` 無法有效反映出多執行緒的特性 (`ab` 自身就消耗單核 100% 的運算量),因此我們才會在 [khttpd](https://github.com/sysprog21/khttpd) 提供 [htstress.c](https://github.com/sysprog21/khttpd/blob/master/htstress.c),後者提供 `-t` 選項,能夠依據測試環境的有效 CPU 個數進行分配。 [ab - Apache HTTP server benchmarking tool](https://httpd.apache.org/docs/current/programs/ab.html) 的實作從今日的 GNU/Linux 或 FreeBSD 來說,算是過時且未能反映系統特性,除了 `htstress`,尚可使用 [wrk](https://github.com/wg/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](https://github.com/httperf/httperf)。 ### 例外處理 倘若你將 seHTTPd 執行後,不立刻關閉,隨即較長時間的等待和重新用上述 `ab` 多次測試 (變更 `-n` 和 `-c` 參數的指定數值) 後,可能會遇到以下錯誤狀況 (部分) 1. Segmentation fault; 2. 顯示訊息 `[ERROR] (src/http.c:253: errno: Resource temporarily unavailable) rc != 0` 可用 `$ grep -r log_err` 搜尋原始程式碼,以得知現有的例外處理機制 (注意: 裡頭存在若干缺失,切勿「舉燭」)。 ## :microscope: 以 [eBPF](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter) 追蹤 HTTP 封包 首先,研讀 [Linux 核心設計: 透過 eBPF 觀察作業系統行為](https://hackmd.io/@sysprog/linux-ebpf) 以理解核心動態追蹤機制,後者允許我們使用非侵入式的方式,不用去修改我們的作業系統核心內部,不用去修改我們的應用程式,也不用去修改我們的業務程式碼或者任何系統配置,就可快速高效地精確獲取我們想要的資訊。 在 [seHTTPd](https://github.com/sysprog21/sehttpd) 原始程式碼的 [ebpf 目錄](https://github.com/sysprog21/sehttpd/tree/master/ebpf)提供簡易的 HTTP 封包分析工具,就是建構在 eBPF 的基礎之上,並透過 [IO Visor](https://github.com/iovisor) 提供的工具來運作。 概念示意圖:  使用方式: (預先在另一個終端機視窗執行 `$ ./sehttpd`) ```shell $ cd ebpf $ sudo python http-parse-sample.py ``` 注意: 這個工具預設監控 `eth0` 這個網路介面 (network interface)。倘若你的預設網路介面不是 `eth0`,你需要依據 `ip` 工具的輸出,決定監控哪個網路介面。舉例來說,在某台 GNU/Linux 機器上執行以下命令: ```shell $ ip link ``` 你會見到若干輸出,如果你的環境裡頭已執行 [Docker](https://www.docker.com/),輸出數量會很可觀,但不用擔心,排除 `lo`, `tun`, `virbr`, `docker`, `br-`, `veth` 開頭的輸出,然後就剩下 `enp5s0` 這樣的網路介面 (端視你的網路硬體而有不同),於是可將上述命令改為: ```shell $ sudo python http-parse-sample.py -i enp5s0 ``` 然後打開網頁瀏覽器,多次存取和刷新 `http://127.0.0.1:8081/` 網址,然後你應可在上述執行 Python 程式的終端機見到類似以下的輸出: ``` TCP src port '51670' TCP dst port '8081' ¢GET / HTTP/1.1 IP hdr length '20' IP src '192.168.50.97' IP dst '61.70.212.51' TCP src port '8081' TCP dst port '51670' ÌHTTP/1.1 304 Not Modified ``` 關於上述程式運作的概況,可參考 [Appendix C](https://hackmd.io/@0xff07/r1f4B8aGI)。 透過 eBPF 追蹤 [fibdrv](https://github.com/sysprog21/fibdrv) 核心模組的運作機制,可參見 [0xff07 的共筆](https://hackmd.io/@0xff07/S1vfNHWB8) * [對應的程式碼](https://github.com/0xff07/fibdrv/) ## :runner: [Thread Pool](https://en.wikipedia.org/wiki/Thread_pool) 和並行議題 現行 [seHTTPd](https://github.com/sysprog21/sehttpd) 運用 non-blocking I/O 和 I/O multiplexor 機制,在單執行緒仍可有效能表現,但若我們想更充分運用硬體特性,勢必要透過多執行緒和克服相關並行議題。請事先研讀以下材料: * [Toward Concurrency](https://hackmd.io/@sysprog/Skh_AaVix) * [server-framework](https://hackmd.io/@ktvexe/B1s8hX1yg) 引入 [thread pool](https://en.wikipedia.org/wiki/Thread_pool) 的設計,需要非常小心,否則容易致使反效果 —— 分配更多的執行緒,但最終的伺服器效能還不如單執行緒為基礎的實作。應充分研讀 [An Introduction to Lock-Free Programming](https://preshing.com/20120612/an-introduction-to-lock-free-programming/),以掌握 lockless/lock-free 程式設計背景知識。 多執行緒的程式不容易除錯,可善用 [ThreadSanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual) 這項已整合到 gcc/clang。修改 `Makefile`,在 `LDFLAGS =` 後方新增下列: ```shell # ThreadSanitizer CFLAGS += -fsanitize=thread LDFLAGS += -fsanitize=thread ``` 之後 `$ make clean all` 即可編譯出支援 [ThreadSanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual) 的 seHTTPd 執行檔。 ## :checkered_flag: 自我檢查清單 - [ ] 在 [高效 Web 伺服器開發](https://hackmd.io/@sysprog/fast-web-server) 提到 epoll 的兩種工作模式 (level trigger vs. edge trigger),對照 [seHTTPd](https://github.com/sysprog21/sehttpd) 原始程式碼,解釋 epoll 工作模式的設定和在 web 伺服器實作的考量點 $\to$ 搭配程式碼實驗並說明 > 提示: 參考實驗程式碼: [test_epoll_lt_and_et](https://github.com/Manistein/test_epoll_lt_and_et) - [ ] 在 [sehttpd/src/http_parser.c](https://github.com/sysprog21/sehttpd/blob/master/src/http_parser.c) 裡頭維護著 HTTP 解析所用的狀態機 (state machine),能否推敲出其考量點呢?能否搭配 [第 8 週測驗題](https://hackmd.io/@sysprog/linux2020-quiz8) 提出縮減 branch/dispatch 成本的實作呢? - [ ] [seHTTPd](https://github.com/sysprog21/sehttpd) 內部為何有 timer,考量點和具體作用為何?timer 為何用到 priority queue 呢?能否在 Linux 核心原始程式碼找到類似的用法? - [ ] [Toward Concurrency](https://hackmd.io/@sysprog/Skh_AaVix) 提及 thread pool 的實作,lock-free/lockless 的 thread pool 的效益為何?在高並行的應用場域 (如 web 伺服器),可以如何發揮 thread pool 的效益呢? - [ ] 解釋前述 `http-parse-sample.py` 運作機制,以及 eBPF 程式在 Linux 核心內部分析封包的優勢為何? ## :penguin: 作業要求 * 回答上述「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼,以第一手材料 (包含自己設計的實驗) 為佳 :::warning :warning: 如果你在 2020 年 4 月 10 日前,已從 GitHub [sysprog21/sehttpd](https://github.com/sysprog21/sehttpd) 進行 fork,請依據 [Alternatives to forking into the same account](https://github.community/t5/Support-Protips/Alternatives-to-forking-into-the-same-account/ba-p/7428) 一文,對舊的 repository 做對應處置,然後重新 fork ::: * 在 GitHub 上 fork [sehttpd](https://github.com/sysprog21/sehttpd),目標是修正 seHTTPd 的執行時期的缺失,提升效能和穩健度 (robustness),需要充分落實以下: * 修改 [htstress.c](https://github.com/sysprog21/khttpd/blob/master/htstress.c) 並放入 git repository,學習 [wrk](https://github.com/wg/wrk) 和 [httperf](https://github.com/httperf/httperf) 的部分特徵,強化對 seHTTPd 伺服器的測試 (除了效能,也該涵蓋正確性測試,包含檔案存取),特別是 concurrency 和 multi-threading 等議題; * 強化 seHTTPd 實作的例外處理,得以長時間持續運作,應修正各式程式邏輯、記憶體管理,和 I/O 事件模型處理相關的缺失; * 參考上述 eBPF 範例程式,實作專門動態分析 seHTTPd 執行過程中 Linux 核心內部事件、系統呼叫,關鍵操作耗費的時間等等的工具; * 引入 [thread pool](https://en.wikipedia.org/wiki/Thread_pool) 到 seHTTPd 探討對 web 伺服器的效能的影響,過程中應該充分量化各因素,尤其是在多執行緒的環境中 non-blocking I/O 搭配事件驅動程式設計的實作; * 以 [sendfile](http://man7.org/linux/man-pages/man2/sendfile.2.html) 系統呼叫改寫 `src/http.c` 裡頭傳遞檔案內容的實作 (對應到 `serve_static` 函式),思考在高並行的環境中,檔案系統 I/O 的開銷及改進空間; * 用 `$ grep -r TODO *` 找出 seHTTPd 的待做事項,予以列出並充分闡述具體執行方法,例如涉及動態記憶體管理、長期閒置連線的處理機制等等。 > ==[mimalloc](https://github.com/microsoft/mimalloc)== 的運用是個值得留意的改進方向,可參考共筆 [mimalloc 實作機制和改善](https://hackmd.io/@jserv/BynrpV0eB) 和 [mimalloc 共筆](https://hackmd.io/@hPMCWajOS-ORQdEEAQ04-w/SkJltC61H) * 複習 [lab0](https://hackmd.io/@sysprog/linux2020-lab0) 裡頭針對 [Valgrind](https://valgrind.org/) 和建構其上的 [Massif](https://valgrind.org/docs/manual/ms-manual.html) 工具,用以分析 seHTTPd 的記憶體開銷,設計實驗讓 seHTTPd 得以長時間運作,並搭配各式工作負載 (work load)。 * 在你的開發環境中安裝 [Nginx](https://nginx.org/en/) 伺服器並比較由你強化後的 seHTTPd 和 [Nginx](https://nginx.org/en/) 的效能差異,試圖解釋針對 Linux 的改進空間 ## 繳交方式 編輯 [Homework6 作業區共筆](https://hackmd.io/@sysprog/linux2020-homework6),將你的觀察、上述要求的解說、應用場合探討,以及各式效能改善過程,善用 gnuplot 製圖,紀錄於新建立的共筆 ## 截止日期 * May 9, 2020 (含) 之前 > 越早在 GitHub 上有動態、越早接受 code review,評分越高
×
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
.