diff --git a/articles/20230731-linux-kernel-lib-intro.md b/articles/20230731-linux-kernel-lib-intro.md
new file mode 100644
index 0000000000000000000000000000000000000000..9553fd7aa3d720840ea2bc2c52312f3288c40666
--- /dev/null
+++ b/articles/20230731-linux-kernel-lib-intro.md
@@ -0,0 +1,393 @@
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces urls refs]
+> Author: Jingqing3948 <2351290287@qq.com>
+> Date: 2023/07/31
+> 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. 在 Makefile 中添加 `obj-y` 设定如何将 C 源文件编译成目标文件,以及 `obj-$(CONFIG_XXX) += xxx.o` 使得这一符号可以在 Kconfig 文件中被找到;
+3. 在 Kconfig 文件中特定 menu 层级内添加该 config 变量,设定变量类型(如常见为 bool 类型,可以在 menuconfig 中回车勾选)以及提示信息 `tristate`;
+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/20230731-kernel-libc-file-structure.jpg b/articles/images/riscv-klibc/20230731-kernel-libc-file-structure.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..03537663d4926422a538475b21d1ef3269d417a8
Binary files /dev/null and b/articles/images/riscv-klibc/20230731-kernel-libc-file-structure.jpg differ
diff --git a/articles/images/riscv-klibc/20230731-kernel-libc-menuconfig.jpg b/articles/images/riscv-klibc/20230731-kernel-libc-menuconfig.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..90a8d9144932c180ebfb8a62eee5d62071fa988c
Binary files /dev/null and b/articles/images/riscv-klibc/20230731-kernel-libc-menuconfig.jpg differ
diff --git a/articles/images/riscv-klibc/20230731-kernel-libc-test-vsprint-example.jpg b/articles/images/riscv-klibc/20230731-kernel-libc-test-vsprint-example.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b0b43c64607dc56d45927e8421d26d13477e58c4
Binary files /dev/null and b/articles/images/riscv-klibc/20230731-kernel-libc-test-vsprint-example.jpg differ