diff --git a/articles/20230816-riscv-klibc-linux-kernel-libc-intro.md b/articles/20230816-riscv-klibc-linux-kernel-libc-intro.md
new file mode 100644
index 0000000000000000000000000000000000000000..c80188282085180b6f139d6885ed0bb7300ceaac
--- /dev/null
+++ b/articles/20230816-riscv-klibc-linux-kernel-libc-intro.md
@@ -0,0 +1,392 @@
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc2 - [spaces toc codeinline]
+> Author:    Jingqing3948 <2351290287@qq.com>
+> Date:      2023/08/16
+> Revisor:   Falcon 
+> Project:   [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Sponsor:   PLCT Lab, ISCAS
+
+# Linux 内核库函数(kernel libc)简介
+
+## 简介
+
+本文主要介绍了实验盘中 Linux 内核源码下的 lib 库,包括其目录结构、基本功能和测试方法。为简单起见,以下简称 Linux 内核函数库为 kernel libc。请注意,这里并非用户态的 klibc 函数库。
+
+## kernel libc 是什么
+
+在此先声明一下我们指的 Linux 内核函数库,这个是 Linux kernel 源码中的 C 库。
+
+获取 kernel libc 库的两种方法:
+
+1. 实验盘文件:进入 `Linux Lab` 后,可以在 `src/linux-stable/lib` 文件夹下找到。
+2. 官网下载解压后,得到 `linux[-v]` 文件夹,然后在其下的 `lib` 文件夹中可以找到。[官网下载链接:https://www.kernel.org/][001]
+
+## kernel libc 目录结构
+
+
+
+通过 chatgpt 总结分类:
+
+1. 内核功能相关文件
+
+   - 内核自旋锁、互斥锁、读写锁等锁相关的自测文件
+     - locking-selftest-wlock-hardirq.h
+     - locking-selftest-wlock-softirq.h
+     - locking-selftest-mutex.h
+     - locking-selftest-rlock.h
+     - locking-selftest-rlock-hardirq.h
+     - locking-selftest-rlock-softirq.h
+     - locking-selftest-rsem.h
+     - locking-selftest-rtmutex.h
+     - locking-selftest-softirq.h
+     - locking-selftest-spin.h
+     - locking-selftest-spin-hardirq.h
+     - locking-selftest-spin-softirq.h
+     - locking-selftest.c
+
+   - 早期 cpio 文件支持
+
+     - earlycpio.c
+
+   - 故障注入(及其拷贝)用于模拟故障的发生
+
+     - fault-inject.c
+
+     - fault-inject-usercopy.c
+
+   - Undefined Behavior Sanitizer 相关的文件
+
+     - ubsan.c
+
+     - Kconfig.ubsan
+
+     - ubsan.h
+
+   - 用于 Kernel Address SANitizer 的文件
+
+     - kasan 相关文件
+
+   - 内核 Fence 机制相关文件
+
+     - kfence
+
+   - 内核 Concurrency Sanitizer 相关文件
+
+     - kcsan
+
+2. 文件系统和配置相关文件:
+
+   - 用于文件系统或其他模块的文件
+     - 842
+   - 用于异常处理的外部表相关文件
+     - extable.c
+   - 用于内核启动配置的文件
+     - bootconfig.c
+   - 用于处理命令行参数的文件
+     - cmdline.c
+     - cmdline_kunit.c
+   - 生成 CRC 表的文件
+     - gen_crc*.c
+   - KUnit 测试框架相关文件
+     - Kconfig
+     - kunit
+   - 用于内核后期初始化的文件
+     - late_init.c
+   - 用于内核热补丁的相关文件
+     - livepatch
+   - OID 注册表相关文件
+     - oid_registry.c
+     - build_OID_registry
+
+3. 硬件和驱动相关文件
+
+   - Flattened Device Tree (FDT) 相关文件
+     - fdt_addresses.c
+     - fdt.c
+     - fdt_empty_tree.c
+     - fdt_ro.c
+     - fdt_rw.c
+   - 用于逻辑设备的文件
+     - logic_*.c
+   - PCI I/O 映射相关文件
+     - pci_iomap.c
+   - 用于硬件或数据结构的文件
+     - bch.c
+     - bsearch.c
+     - btree.c
+
+4. 网络和协议相关文件:
+
+   - 用于审计相关功能的文件
+     - audit.c
+   - 非屏蔽中断回溯相关文件
+     - nmi_backtrace.c
+   - 用于文本搜索的文件
+     - textsearch.c
+   - Virtual Dynamic Shared Object 相关文件
+     - vdso
+
+5. 数据结构和算法相关文件
+
+   - 关联数组相关文件
+     - assoc_array.c
+   - 基数树相关文件
+     - radix-tree.c
+   - 位域测试文件
+     - bitfield_kunit.c
+   - 用于内核功能测试的文件
+     - 一些以 `test_` 开头的文件
+
+6. 其他文件:
+
+   - 密码学相关文件
+     - crypto
+   - 用于构建 ID 的文件
+     - buildid.c
+   - 数学库相关文件
+     - math
+   - 内存相关文件
+     - memcat_p.c
+   - UUID 相关文件
+     - uuid.c
+   - zlib 压缩库相关文件
+     - zlib_*
+   - 用于输出函数的文件
+     - vsprintf.c
+
+## kernel libc 配置与编译
+
+kernel libc 下包含各种库函数的实现,为了方便稍后理解如何添加新的库函数,这里简单分析一下其配置和编译管理方式。
+
+`lib/` 目录下包含几类文件,一类是源码,还有一类是 Kconfig,再有一类是 Makefile,后面两个用于配置和编译相关的控制,本节主要分析它们。
+
+### kernel libc 配置方式
+
+Kconfig 文件是一种用于配置选项的脚本文件。它定义了内核 libc 的各个配置选项,包括库函数的开启或关闭,功能的选择等。这样的配置方式可以让开发者根据需求进行灵活的定制,避免在编译时包含不必要的函数,从而减小库的体积。
+
+Kconfig 文件的组织通常是由顶层的 Kconfig 文件引用其他子目录下的 Kconfig 文件,逐层组织整个配置树。开发者通过 `make menuconfig` 命令可以进入一个文本式的配置界面,从而通过交互式的方式选择需要开启或关闭的功能,这些选择将在生成配置文件 `.config` 中体现。
+
+### kernel libc 编译方式
+
+Makefile 中配置 kernel libc 文件的编译方式,主要分为两个部分:编译选项,编译控制部分。
+
+编译选项:`obj-y` 格式设定导出文件。
+
+编译控制:通过声明一个与此模块相关联的控制变量,使得可以在 Kconfig 中通过对此变量赋值的方式控制是否编译此模块。
+
+## 如何添加新的 kernel libc 库函数
+
+从上述过程中可以分析得到,添加一个 kernel libc 文件需要的步骤如下:
+
+1. 编写 c 文件,在要导出的函数结尾加:`EXPORT_SYMBOL(xxx);` 来在内核中导出符号供其他文件可以访问;
+2. 在 Kconfig 文件中特定 menu 层级内添加该 `config XXX` 变量,设定变量类型(如常见为 bool 类型,可以在 menuconfig 中回车勾选)以及提示信息,使得这一可以在 Makefile 文件中使用 `CONFIG_XXX`;
+3. 在 Makefile 中添加 `obj-$(CONFIG_XXX) += xxx.o` 设定将 C 源文件根据配置 `CONFIG_XXX` 来选择是否编译成目标文件;
+4. 在 `make kernel-menuconfig` 配置中打开对应选项;
+5. `make kernel` 重新编译;
+6. 之后就可以运行使用该模块了。
+
+## 如何测试新的 kernel libc 库函数
+
+整体的测试流程如下:
+
+- 在 lib 库里编写相应的 test_xxx.c 文件。
+- 在 lib/makefile 里添加编译时的配置。
+- 在 lib/Kconfig 里添加配置。
+- 在 menuconfig 里启动这个 test 测试函数,使得开发板启动时自测函数会被运行。
+- 如果未全部通过则继续修改库函数,直至通过测试。
+
+下面我们以 printf 函数的测试为例展开分析。
+
+首先查看 vsprintf.c, vsprintf 函数可以把输出存到字符缓冲中:
+
+```c
+// src/linux-stable/lib/vsprintf.c:2707
+
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * ...
+ * If you're not already dealing with a va_list consider using snprintf().
+ */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
+EXPORT_SYMBOL(vsnprintf);
+```
+
+然后摘取 test_printf.c 中的一部分:test string 部分分析。首先封装了 do_test 和 test 函数:
+
+```c
+// src/linux-stable/lib/test_printf.c:41
+
+do_test(int bufsize, const char *expect, int elen,
+	const char *fmt, va_list ap)
+{
+	va_list aq;
+	int ret, written;
+
+	total_tests++;
+
+	memset(alloced_buffer, FILL_CHAR, BUF_SIZE + 2*PAD_SIZE);
+	va_copy(aq, ap);
+	ret = vsnprintf(test_buffer, bufsize, fmt, aq);
+	va_end(aq);
+
+	if (ret != elen) {// 输入长度是否和预期长度匹配
+		pr_warn("vsnprintf(buf, %d, \"%s\", ...) returned %d, expected %d\n",
+			bufsize, fmt, ret, elen);
+		return 1;
+	}
+
+	if (memchr_inv(alloced_buffer, FILL_CHAR, PAD_SIZE)) {// 写入范围是否超出了 PAD_SIZE
+		pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote before buffer\n", bufsize, fmt);
+		return 1;
+	}
+
+	if (!bufsize) {// bufsize==0 但是有输入信息
+		if (memchr_inv(test_buffer, FILL_CHAR, BUF_SIZE + PAD_SIZE)) {
+			pr_warn("vsnprintf(buf, 0, \"%s\", ...) wrote to buffer\n",
+				fmt);
+			return 1;
+		}
+		return 0;
+	}
+
+	written = min(bufsize-1, elen);
+	if (test_buffer[written]) {// 校验结尾是否是、0
+		pr_warn("vsnprintf(buf, %d, \"%s\", ...) did not nul-terminate buffer\n",
+			bufsize, fmt);
+		return 1;
+	}
+
+	if (memchr_inv(test_buffer + written + 1, FILL_CHAR, BUF_SIZE + PAD_SIZE - (written + 1))) {// 是否是在 PAD_SIZE 后面终止的
+		pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote beyond the nul-terminator\n",
+			bufsize, fmt);
+		return 1;
+	}
+
+	if (memcmp(test_buffer, expect, written)) {// 比较一下写入缓冲区的数据和预期是否一样,assert
+		pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote '%s', expected '%.*s'\n",
+			bufsize, fmt, test_buffer, written, expect);
+		return 1;
+	}
+	return 0;// 以上问题都没有碰到,成功
+}
+```
+
+```c
+// src/linux-stable/lib/test_printf.c:95
+
+static void __printf(3, 4) __init
+__test(const char *expect, int elen, const char *fmt, ...)
+{
+	va_list ap;
+	int rand;
+	char *p;
+
+	if (elen >= BUF_SIZE) {// 长度超出缓冲区,直接不存了
+		pr_err("error in test suite: expected output length %d too long. Format was '%s'.\n",
+		       elen, fmt);
+		failed_tests++;
+		return;
+	}
+
+	va_start(ap, fmt);
+
+	failed_tests += do_test(BUF_SIZE, expect, elen, fmt, ap);// 统计多少用例没有通过
+	rand = 1 + prandom_u32_max(elen+1);
+	/* Since elen < BUF_SIZE, we have 1 <= rand <= BUF_SIZE. */
+	failed_tests += do_test(rand, expect, elen, fmt, ap);// 随机缓冲区长度
+	failed_tests += do_test(0, expect, elen, fmt, ap);// 0 缓冲区长度
+
+	p = kvasprintf(GFP_KERNEL, fmt, ap);
+	if (p) {
+		total_tests++;
+		if (memcmp(p, expect, elen+1)) {
+			pr_warn("kvasprintf(..., \"%s\", ...) returned '%s', expected '%s'\n",
+				fmt, p, expect);
+			failed_tests++;
+		}
+		kfree(p);
+	}
+	va_end(ap);
+}
+```
+
+然后在具体测试用例中传入参数交给 test。比如下例是测试打印字符串。
+
+```c
+// src/linux-stable/lib/test_printf.c:183
+
+static void __init
+test_string(void)
+{
+	test("", "%s%.0s", "", "123");// 参数 1 是预期输出,参数 2 是占位符组合,后面变长参数是传入占位符的数据
+	test("ABCD|abc|123", "%s|%.3s|%.*s", "ABCD", "abcdef", 3, "123456");
+	test("1  |  2|3  |  4|5  ", "%-3s|%3s|%-*s|%*s|%*s", "1", "2", 3, "3", 3, "4", -3, "5");
+	test("1234      ", "%-10.4s", "123456");
+	test("      1234", "%10.4s", "123456");
+	test("    ", "%4.*s", -5, "123456");
+	test("123456", "%.s", "123456");
+	test("a||", "%.s|%.0s|%.*s", "a", "b", 0, "c");
+	test("a  |   |   ", "%-3.s|%-3.0s|%-3.*s", "a", "b", 0, "c");
+}
+```
+
+比如 `vsprintf.c` 中的控制变量声明:
+
+```makefile
+// src/linux-stable/lib/Makefile:83
+
+obj-$(CONFIG_TEST_PRINTF) += test_printf.o
+```
+
+根据 CONFIG_TEST_PRINTF 变量来判断是否编译出 test_printf 的目标文件。
+
+然后在 lib/KConfig.debug 里可以看到 test 文件设定在 menuconfig 的什么位置下。
+
+```shell
+menu "Kernel hacking"
+config TEST_PRINTF
+	tristate "Test printf() family of functions at runtime"
+```
+
+在 linux-lab 目录下执行 `make kernel-menuconfig`,可以看到 printf 对应的测试项:
+
+```
+kernel-hacking
+	-> Kernel Testing and Coverage
+		-> Run time Testing
+			-> Test printf() family of functions at runtime
+```
+
+根据其 menu 设定,这个测试文件模块被添加到了 `Run time Testing` 也就是运行时自检部分中,只要执行 `make boot` 运行时就会自动运行此测试文件输出测试结果。
+
+我们将其启用:
+
+
+
+也就是说我们在 menuconfig 里启用了 "Test printf() family of functions at runtime" 这一项,则相当于启用了 TEST_PRINTF 这个变量,则 makefile 中 CONFIG_TEST_PRINTF 这个变量也会被启用,然后编译链接的时候就会添加 test_printf.c 生成的目标文件,并在运行内核的时候一并运行。
+
+完成配置后,`make kernel` 重新编译内核,并 `make boot` 运行,可以看到打印的调试信息中出现了:
+
+```shell
+test_printf: loaded.
+crng possibly not yet initialized. plain 'p' buffer contains "(____ptrval____)"
+test_printf: crng possibly not yet initialized. plain 'p' buffer contains "(____ptrval____)"
+test_printf: crng possibly not yet initialized. plain 'p' buffer contains "(____ptrval____)"
+test_printf: crng possibly not yet initialized. plain 'p' buffer contains "(____ptrval____)"
+test_printf: all 416 tests passed
+```
+
+
+
+这一段说明测试模块顺利启动并通过测试。
+
+## 总结
+
+本文主要分析了 kernel libc 库的目录结构并介绍了如何添加和测试新的库函数。
+
+对于 Linux kernel 库的使用,本文只做了一个简单尝试。后续可能展开尝试 kernel libc 库的具体用法,以及继续对 kernel libc 库进行分析。
+
+## 参考资料
+
+- [https://www.kernel.org][001]
+
+[001]: https://www.kernel.org/
diff --git a/articles/images/riscv-klibc/20230816-kernel-libc-file-structure.jpg b/articles/images/riscv-klibc/20230816-kernel-libc-file-structure.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..03537663d4926422a538475b21d1ef3269d417a8
Binary files /dev/null and b/articles/images/riscv-klibc/20230816-kernel-libc-file-structure.jpg differ
diff --git a/articles/images/riscv-klibc/20230816-kernel-libc-menuconfig.jpg b/articles/images/riscv-klibc/20230816-kernel-libc-menuconfig.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..90a8d9144932c180ebfb8a62eee5d62071fa988c
Binary files /dev/null and b/articles/images/riscv-klibc/20230816-kernel-libc-menuconfig.jpg differ
diff --git a/articles/images/riscv-klibc/20230816-kernel-libc-test-vsprint-example.jpg b/articles/images/riscv-klibc/20230816-kernel-libc-test-vsprint-example.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b0b43c64607dc56d45927e8421d26d13477e58c4
Binary files /dev/null and b/articles/images/riscv-klibc/20230816-kernel-libc-test-vsprint-example.jpg differ