2022q1 Homework6 (ktcp)

contributed by < SmallHanley >

作業說明
GitHub: kecho
CS:APP CH11 Note

開發環境

$ cat /proc/version
Linux version 5.13.0-40-generic (buildd@ubuntu)
(gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34)
#45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022

$ lscpu
Architecture:                    x86_64
CPU op-mode(s):                  32-bit, 64-bit
Byte Order:                      Little Endian
Address sizes:                   39 bits physical, 48 bits virtual
CPU(s):                          8
On-line CPU(s) list:             0-7
Thread(s) per core:              2
Core(s) per socket:              4
Socket(s):                       1
NUMA node(s):                    1
Vendor ID:                       GenuineIntel
CPU family:                      6
Model:                           142
Model name:                      Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
Stepping:                        10
CPU MHz:                         1800.000
CPU max MHz:                     3400.0000
CPU min MHz:                     400.0000
BogoMIPS:                        3600.00
Virtualisation:                  VT-x
L1d cache:                       128 KiB
L1i cache:                       128 KiB
L2 cache:                        1 MiB
L3 cache:                        6 MiB
NUMA node0 CPU(s):               0-7

自我檢查清單

Q1

參照 Linux 核心模組掛載機制,解釋 $ sudo insmod khttpd.ko port=1999 這命令是如何讓 port=1999 傳遞到核心,作為核心模組初始化的參數呢?

過程中也會參照到 你所不知道的 C 語言:連結器和執行檔資訊

參考 lkmpg 4.5 Passing Command Line Arguments to a Module,我們可以從 command line 傳入參數到核心模組。將欲接收傳入值的變數宣告成全域變數,並使用 module_param() macro (另外還有 module_param_array()module_param_string())。而 module_param() macro 是宣告在 include/linux/moduleparam.h

#define module_param(name, type, perm)				\
	module_param_named(name, name, type, perm)

#define module_param_named(name, value, type, perm)			   \
	param_check_##type(name, &(value));				   \
	module_param_cb(name, &param_ops_##type, &value, perm);		   \
	__MODULE_PARM_TYPE(name, #type)

其中 param_check_##type() 會根據 type 展開成以下獨一無二名字的函式:

/* All the helper functions */
/* The macros to do compile-time type checking stolen from Jakub
   Jelinek, who IIRC came up with this idea for the 2.4 module init code. */
#define __param_check(name, p, type) \
	static inline type __always_unused *__check_##name(void) { return(p); }

__always_unused include 至 include/linux/compiler_attributes.h,展開後是 __attribute__((__unused__)),參考 Common-Function-Attributesunused attribute。

This attribute, attached to a function, means that the function is meant to be possibly unused. GCC does not produce a warning for this function.

使得程式可以在編譯時期檢查 type 是否為規範的 type。

接著,以下是 module_param_cb() 的展開:

/**
 * module_param_cb - general callback for a module/cmdline parameter
 * @name: a valid C identifier which is the parameter name.
 * @ops: the set & get operations for this parameter.
 * @arg: args for @ops
 * @perm: visibility in sysfs.
 *
 * The ops can have NULL set or get functions.
 */
#define module_param_cb(name, ops, arg, perm)				      \
	__module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)

/* This is the fundamental function for registering boot/module
   parameters. */
#define __module_param_call(prefix, name, ops, arg, perm, level, flags)	\
	/* Default value instead of permissions? */			\
	static const char __param_str_##name[] = prefix #name;		\
	static struct kernel_param __moduleparam_const __param_##name	\
	__used __section("__param")					\
	__aligned(__alignof__(struct kernel_param))			\
	= { __param_str_##name, THIS_MODULE, ops,			\
	    VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }

如果要方便觀察展開內容,可以使用 gcc toolchain 讓 gcc 命令執行到前置處理器結束,尚未進入編譯階段 (使用 -E flag):

$ TREE=/usr/src/linux-headers-`uname -r`
$ gcc -E test.c \
-I$TREE/include \
-I$TREE/arch/x86/include \
-I$TREE/arch/x86/include/generated \
-I$TREE/include/uapi \
> tmp

TODO

參考 Linux 核心模組掛載機制,使用 strace 追蹤 insmod 的系統呼叫:

$ sudo strace -o tmp insmod kecho.ko port=1999
$ grep '1999' tmp
execve("/usr/sbin/insmod", ["insmod", "kecho.ko", "port=1999"], 0x7ffd2fb17f10 /* 26 vars */) = 0
finit_module(3, "port=1999", 0)         = 0

若是傳入多個參數:

$ sudo strace insmod kecho.ko port=12345 backlog=128 bench=0
...
finit_module(3, "port=12345 backlog=128 bench=0", 0) = 0
...

Q2

參照 CS:APP 第 11 章,給定的 kHTTPd 和書中的 web 伺服器有哪些流程是一致?又有什麼是你認為 kHTTPd 可改進的部分?

Q3

htstress.c 用到 epoll 系統呼叫,其作用為何?這樣的 HTTP 效能分析工具原理為何?

Q4

給定的 kecho 已使用 CMWQ,請陳述其優勢和用法

Q5

核心文件 Concurrency Managed Workqueue (cmwq) 提到 "The original create_*workqueue() functions are deprecated and scheduled for removal",請參閱 Linux 核心的 git log (不要用 Google 搜尋!),揣摩 Linux 核心開發者的考量

Q6

解釋 user-echo-server 運作原理,特別是 epoll 系統呼叫的使用

Q7

是否理解 bench 原理,能否比較 kechouser-echo-server 表現?佐以製圖

Q8

解釋 drop-tcp-socket 核心模組運作原理。TIME-WAIT sockets 又是什麼?