# 2020q1 Homework8 (khttpd) contributed by < `IepIweidieng` > ## 完成清單 - [ ] [自我檢查清單](https://hackmd.io/@sysprog/linux2020-khttpd#-%E8%87%AA%E6%88%91%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE) - [x] 參照 [fibdrv 作業說明](https://hackmd.io/@sysprog/linux2020-fibdrv) 裡頭的「Linux 核心模組掛載機制」一節,解釋 `$ sudo insmod khttpd.ko port=1999` 這命令是如何讓 `port=1999` 傳遞到核心,作為核心模組初始化的參數呢? - `insmod` 會讓 kernel 執行函式 `finit_module`,而該函式有個參數 `uargs`:([kernel/module.c](<https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c>)) ```c=3855 SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) { [...] pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); [...] return load_module(&info, uargs, flags); } ``` - - - `load_module`: ```c=3654 - /* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */ static int load_module(struct load_info *info, const char __user *uargs, int flags) { ``` - - - 而 `uargs` 只出現一次: ```c=3735 /* Now copy in args */ mod->args = strndup_user(uargs, ~0UL >> 1); if (IS_ERR(mod->args)) { err = PTR_ERR(mod->args); goto free_arch_cleanup; } ``` - - - 接下來則使用到 `mod->args`: ```c=3756 /* Module is ready to execute: parsing args may do that. */ after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, -32768, 32767, mod, unknown_module_param_cb); if (IS_ERR(after_dashes)) { err = PTR_ERR(after_dashes); goto coming_cleanup; } else if (after_dashes) { pr_warn("%s: parameters '%s' after `--' ignored\n", mod->name, after_dashes); } ``` - - - `parse_args`: ([kernel/params.c](<https://elixir.bootlin.com/linux/v4.18/source/kernel/params.c>)) ```c=163 /* Args looks like "foo=bar,bar2 baz=fuz wiz". */ char *parse_args(const char *doing, char *args, const struct kernel_param *params, unsigned num, s16 min_level, s16 max_level, void *arg, int (*unknown)(char *param, char *val, const char *doing, void *arg)) { ``` - - - `params` 的指向型別上的 `const` 不是說不能改變 `params` 所有直接或間接指向的內容,因為裡面指向非 `const` 型別的指標成員可以透過 dereference 修改其指向內容,只是不能更改指標指向的位置而已。 `struct kernel_param`: ([include/linux/moduleparam.h](<https://elixir.bootlin.com/linux/v4.18/source/include/linux/moduleparam.h>)) ```c=71 struct kernel_param { const char *name; struct module *mod; const struct kernel_param_ops *ops; const u16 perm; s8 level; u8 flags; union { void *arg; const struct kparam_string *str; const struct kparam_array *arr; }; }; ``` - - [ ] 參照 [CS:APP 第 11 章](https://hackmd.io/s/ByPlLNaTG),給定的 kHTTPd 和書中的 web 伺服器有哪些流程是一致?又有什麼是你認為 kHTTPd 可改進的部分? - [ ] `htstress.c` 用到 [epoll](http://man7.org/linux/man-pages/man7/epoll.7.html) 系統呼叫,其作用為何?這樣的 HTTP 效能分析工具原理為何? - [ ] 在 GitHub 上 fork [khttpd](https://github.com/sysprog21/khttpd),目標是將 fibdrv 作業的成果整合進 kHTTPd。過程中應一併完成以下: 1. [ ] 當來自 web 客戶端 (即網頁瀏覽器或類似的軟體) 請求的網址為 `/fib/N` 時 ($N$ 是自然數,最大可到 $2^{31} - 1$),例如 `$ wget http://localhost:8081/fib/10` 應該要讓 web 客戶端得到 $Fibonacci(10)$ 即 `55` 這個字串,這個實作需要考慮到大數運算 $\to$ 不要小看這個需求,由於大數運算無可避免會用到更大的記憶體空間和更長的執行時間,你要考慮手動安插 `reschedule()`, `preempt_enable()`, `preempt_disable()` 2. [ ] 自 [Facebook 討論串](https://www.facebook.com/groups/system.software2020/permalink/345124729755066/) 找出合適的 Fibonacci 運算檢驗程式,用以比對上述 (1) 的運算結果,並且要能詳實記錄和分析處理時間 (從 HTTP 請求到 kHTTPd 回應) 3. [ ] 指出 kHTTPd 實作的缺失 (特別是安全疑慮) 並予以改正 4. [ ] 引入 [Concurrency Managed Workqueue](https://www.kernel.org/doc/html/v4.15/core-api/workqueue.html) (cmwq),改寫 kHTTPd,分析效能表現和提出改進方案,可參考 [kecho](https://github.com/sysprog21/kecho) 5. [ ] 參照 [in-kernel HTTP server](https://hackmd.io/@sysprog/kernel-web-server),學習其中分析和實作手法,從而改進 kHTTPd - [ ] 彙整同學們的共筆材料,並依據作業要求予以補強 ## 環境準備 先準備支援 Linux kernel module 的環境,而 WSL 不符條件。 但 WSL 2 支援。 ![](https://i.imgur.com/O9tBd4L.png) ```cmd Microsoft Windows [版本 10.0.19041.330] (c) 2020 Microsoft Corporation。著作權所有,並保留一切權利。 C:\Users\user>wsl --list --verbose NAME STATE VERSION * Ubuntu Running 1 C:\Users\user>wsl --set-version Utuntu 2 沒有所提供名稱的發佈。 C:\Users\user>wsl --set-version Ubuntu 2 正在進行轉換,這可能需要幾分鐘的時間... 有關 WSL 2 的主要差異詳細資訊,請瀏覽 https://aka.ms/wsl2 轉換完成。 C:\Users\user> ``` ### 系統資訊 ```shell $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic $ uname -a Linux IID-PC-acer-N17Q3 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release No LSB modules are available. $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 2 On-line CPU(s) list: 0,1 Thread(s) per core: 1 Core(s) per socket: 2 Socket(s): 1 Vendor ID: AuthenticAMD CPU family: 21 Model: 112 Model name: AMD A9-9420 RADEON R5, 5 COMPUTE CORES 2C+3G Stepping: 0 CPU MHz: 2994.380 BogoMIPS: 5988.76 Hypervisor vendor: Microsoft Virtualization type: full L1d cache: 32K L1i cache: 96K L2 cache: 1024K Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw xop fma4 topoext ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 xsaveopt virt_ssbd arat $ ``` ### 準備 kernel module 所需環境 #### 檢查核心版本 ```shell $ uname -r 4.19.104-microsoft-standard ``` ```shell $ sudo aptitude install linux-headers-$(uname -r) [sudo] password for iid: Couldn't find any package whose name or description matched "linux-headers-4.19.104-microsoft-standard" Unable to apply some actions, aborting ``` 沒有現成套件。 #### 手動設定 kernel module 環境 Clone WSL 2 所使用的 kernel 的 repository。只擷取需要的 history。 ```shell $ git clone --depth=1 --branch=4.19.104-microsoft-standard https://github.com/microsoft/WSL2-Linux-Kernel.git Cloning into 'WSL2-Linux-Kernel'... remote: Enumerating objects: 65600, done. remote: Counting objects: 100% (65600/65600), done. remote: Compressing objects: 100% (60527/60527), done. remote: Total 65600 (delta 5197), reused 31211 (delta 4157), pack-reused 0 Receiving objects: 100% (65600/65600), 177.72 MiB | 1.31 MiB/s, done. Resolving deltas: 100% (5197/5197), done. Note: checking out '506f7fa21ff5fe49bc99e173f59c965bbc7ed0c7'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b <new-branch-name> Checking out files: 100% (61781/61781), done. $ cd WSL2-Linux-Kernel/ ``` 安裝編譯 kernel 所需要的套件。 ```shell WSL2-Linux-Kernel$ sudo aptitude install build-essential flex bison libssl-dev libelf-dev [...] ``` 複製系統的 kernel config 過來。 ```shell WSL2-Linux-Kernel$ zcat /proc/config.gz > .config ``` 然後只編譯及安裝 `modules` 相關程式。 ```shell WSL2-Linux-Kernel$ make modules Makefile:590: include/config/auto.conf: No such file or directory Makefile:621: include/config/auto.conf.cmd: No such file or directory HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o YACC scripts/kconfig/zconf.tab.c LEX scripts/kconfig/zconf.lex.c HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --syncconfig Kconfig SYSTBL arch/x86/include/generated/asm/syscalls_32.h SYSHDR arch/x86/include/generated/asm/unistd_32_ia32.h SYSHDR arch/x86/include/generated/asm/unistd_64_x32.h SYSTBL arch/x86/include/generated/asm/syscalls_64.h SYSHDR arch/x86/include/generated/uapi/asm/unistd_32.h SYSHDR arch/x86/include/generated/uapi/asm/unistd_64.h SYSHDR arch/x86/include/generated/uapi/asm/unistd_x32.h HOSTCC arch/x86/tools/relocs_32.o HOSTCC arch/x86/tools/relocs_64.o HOSTCC arch/x86/tools/relocs_common.o HOSTLD arch/x86/tools/relocs UPD include/config/kernel.release WRAP arch/x86/include/generated/uapi/asm/bpf_perf_event.h WRAP arch/x86/include/generated/uapi/asm/poll.h WRAP arch/x86/include/generated/asm/dma-contiguous.h WRAP arch/x86/include/generated/asm/early_ioremap.h WRAP arch/x86/include/generated/asm/export.h WRAP arch/x86/include/generated/asm/mcs_spinlock.h WRAP arch/x86/include/generated/asm/mm-arch-hooks.h UPD include/generated/uapi/linux/version.h UPD include/generated/utsrelease.h CC kernel/bounds.s UPD include/generated/bounds.h UPD include/generated/timeconst.h CC arch/x86/kernel/asm-offsets.s UPD include/generated/asm-offsets.h CALL scripts/checksyscalls.sh DESCEND objtool HOSTCC /home/bbs/WSL2-Linux-Kernel/tools/objtool/fixdep.o HOSTLD /home/bbs/WSL2-Linux-Kernel/tools/objtool/fixdep-in.o LINK /home/bbs/WSL2-Linux-Kernel/tools/objtool/fixdep CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/exec-cmd.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/help.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/pager.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/parse-options.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/run-command.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/sigchain.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/subcmd-config.o LD /home/bbs/WSL2-Linux-Kernel/tools/objtool/libsubcmd-in.o AR /home/bbs/WSL2-Linux-Kernel/tools/objtool/libsubcmd.a GEN /home/bbs/WSL2-Linux-Kernel/tools/objtool/arch/x86/lib/inat-tables.c CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/arch/x86/decode.o LD /home/bbs/WSL2-Linux-Kernel/tools/objtool/arch/x86/objtool-in.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/builtin-check.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/builtin-orc.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/check.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/orc_gen.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/orc_dump.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/elf.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/special.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/objtool.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/libstring.o CC /home/bbs/WSL2-Linux-Kernel/tools/objtool/str_error_r.o LD /home/bbs/WSL2-Linux-Kernel/tools/objtool/objtool-in.o LINK /home/bbs/WSL2-Linux-Kernel/tools/objtool/objtool HOSTCC scripts/genksyms/genksyms.o YACC scripts/genksyms/parse.tab.c HOSTCC scripts/genksyms/parse.tab.o LEX scripts/genksyms/lex.lex.c YACC scripts/genksyms/parse.tab.h HOSTCC scripts/genksyms/lex.lex.o HOSTLD scripts/genksyms/genksyms CC scripts/mod/empty.o HOSTCC scripts/mod/mk_elfconfig MKELF scripts/mod/elfconfig.h HOSTCC scripts/mod/modpost.o CC scripts/mod/devicetable-offsets.s UPD scripts/mod/devicetable-offsets.h HOSTCC scripts/mod/file2alias.o HOSTCC scripts/mod/sumversion.o HOSTLD scripts/mod/modpost HOSTCC scripts/bin2c HOSTCC scripts/kallsyms HOSTCC scripts/conmakehash HOSTCC scripts/recordmcount HOSTCC scripts/sortextable HOSTCC scripts/asn1_compiler HOSTCC scripts/extract-cert Building modules, stage 2. MODPOST 0 modules WSL2-Linux-Kernel$ sudo make modules_install DEPMOD 4.19.104-microsoft-standard+ Warning: modules_install: missing 'System.map' file. Skipping depmod. ``` 最後將剛才得到的目錄設定為 kernel modules 的路徑 (WSL 2 下原無此目錄)。 ```shell WSL2-Linux-Kernel$ sudo ln -s /lib/modules/4.19.104-microsoft-standard+ /lib/modules/$(uname -r) ``` ### 測試 `khttpd` ```shell khttpd$ make cc -std=gnu11 -Wall -Wextra -Werror -o htstress htstress.c -lpthread make -C /lib/modules/4.19.104-microsoft-standard/build M=/home/iid/Linux-Kernel-Design-2020/khttpd modules make[1]: Entering directory '/home/bbs/WSL2-Linux-Kernel' CC [M] /home/iid/Linux-Kernel-Design-2020/khttpd/http_parser.o CC [M] /home/iid/Linux-Kernel-Design-2020/khttpd/http_server.o CC [M] /home/iid/Linux-Kernel-Design-2020/khttpd/main.o LD [M] /home/iid/Linux-Kernel-Design-2020/khttpd/khttpd.o Building modules, stage 2. MODPOST 1 modules CC /home/iid/Linux-Kernel-Design-2020/khttpd/khttpd.mod.o LD [M] /home/iid/Linux-Kernel-Design-2020/khttpd/khttpd.ko make[1]: Leaving directory '/home/bbs/WSL2-Linux-Kernel' khttpd$ make check make -C /lib/modules/4.19.104-microsoft-standard/build M=/home/iid/Linux-Kernel-Design-2020/khttpd modules make[1]: Entering directory '/home/bbs/WSL2-Linux-Kernel' Building modules, stage 2. MODPOST 1 modules make[1]: Leaving directory '/home/bbs/WSL2-Linux-Kernel' 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%] socker errors: 0 [0%] seconds: 20.734 requests/sec: 4822.889 Complete khttpd$ ``` 把 Google Chrome 關掉試試…… ```shell khttpd$ make check [...] requests: 100000 good requests: 100000 [100%] bad requests: 0 [0%] socker errors: 0 [0%] seconds: 19.614 requests/sec: 5098.430 Complete khttpd$ ``` 效能差異不大。 檢視 `khttpd.ko` 的資訊: ```shell khttpd$ modinfo khttpd.ko filename: /home/iid/Linux-Kernel-Design-2020/khttpd/khttpd.ko version: 0.1 description: in-kernel HTTP daemon author: National Cheng Kung University, Taiwan license: Dual MIT/GPL srcversion: 557851B109241B26D04B2D2 depends: retpoline: Y name: khttpd vermagic: 4.19.104-microsoft-standard+ SMP mod_unload modversions parm: port:ushort parm: backlog:ushort khttpd$ lsmod Module Size Used by khttpd 40960 0 khttpd$ ``` Benchmark ```shell khttpd$ ./htstress -n 1000 -c 1 -t 4 http://www.google.com/ 0 requests 100 requests 200 requests 300 requests 400 requests 500 requests 600 requests 700 requests 800 requests 900 requests requests: 1000 good requests: 1000 [100%] bad requests: 0 [0%] socker errors: 0 [0%] seconds: 42.700 requests/sec: 23.419 ``` ```shell khttpd$ sudo insmod khttpd.ko port=8081 khttpd$ wget localhost:8081 --2020-07-01 11:59:42-- http://localhost:8081/ Resolving localhost (localhost)... 127.0.0.1 Connecting to localhost (localhost)|127.0.0.1|:8081... connected. HTTP request sent, awaiting response... 200 OK Length: 12 [text/plain] Saving to: ‘index.html’ index.html 100%[===============================>] 12 --.-KB/s in 0s 2020-07-01 11:59:42 (168 KB/s) - ‘index.html’ saved [12/12] khttpd$ cat index.html Hello World!khttpd$ ``` ## 處理 `/fib/N` 這個字串是在哪裡產生的? ```shell khttpd$ rg "Hello World!" http_server.c 16: "Connection: Close" CRLF CRLF "Hello World!" CRLF 21: "Connection: Keep-Alive" CRLF CRLF "Hello World!" CRLF ``` ```c=12 #define HTTP_RESPONSE_200_DUMMY \ "" \ "HTTP/1.1 200 OK" CRLF "Server: " KBUILD_MODNAME CRLF \ "Content-Type: text/plain" CRLF "Content-Length: 12" CRLF \ "Connection: Close" CRLF CRLF "Hello World!" CRLF ``` ```c=76 static int http_server_response(struct http_request *request, int keep_alive) { char *response; pr_info("requested_url = %s\n", request->request_url); if (request->method != HTTP_GET) response = keep_alive ? HTTP_RESPONSE_501_KEEPALIVE : HTTP_RESPONSE_501; else response = keep_alive ? HTTP_RESPONSE_200_KEEPALIVE_DUMMY : HTTP_RESPONSE_200_DUMMY; http_server_send(request->socket, response, strlen(response)); return 0; } ``` `request->request_url` 應是所要的 url。驗證如下: ```shell khttpd$ wget localhost:8081/fib/42 --2020-07-01 13:22:45-- http://localhost:8081/fib/42 Resolving localhost (localhost)... 127.0.0.1 Connecting to localhost (localhost)|127.0.0.1|:8081... connected. HTTP request sent, awaiting response... 200 OK Length: 12 [text/plain] Saving to: ‘42.1’ 42.1 100%[===============================>] 12 --.-KB/s in 0s 2020-07-01 13:22:45 (945 KB/s) - ‘42.1’ saved [12/12] khttpd$ dmesg -k | tail [703899.551653] khttpd: requested_url = / [703899.554815] khttpd: recv error: -104 [703900.925947] khttpd: requested_url = / [703900.927536] khttpd: recv error: -104 [703901.353702] khttpd: requested_url = / [703901.355572] khttpd: recv error: -104 [703901.726378] khttpd: requested_url = / [703901.727973] khttpd: recv error: -104 [703904.739368] khttpd: requested_url = /fib/42 [703904.740990] khttpd: recv error: -104 khttpd$ ```