PA1
朋友供稿,是南京大学计算机系统基础的小型项目(PA)第一部分,也就是PA1。
RTFSC
框架初探
NEMU由四部分构成:monitor, CPU, memory, 设备
monitor负责和Linux交互,调试的功能
kconfig
这一块是简单讲解 kconfig 的作用。
在大型项目中,有很多可配置选项,配置选项之间会有依赖和关联。比如我们玩的游戏设置:
- 图形质量:(高 / 低)
- 光线追踪:(开 / 关) —— 这个选项只有在“高”画质时才能开启。
而这些设置,在代码里面通常要用宏来实现,比如:
1 | // config.h |
如果手动配置这些配置文件宏,会非常麻烦且容易出错。因此引入新的软件 kconfig,具体描述见讲义。
nemu的步骤 make menuconfig :
-
定义 (Kconfig)
- 开发者在
nemu/Kconfig文件中定义了所有可配置的选项。 - 包含了各种选项和各种依赖。
- 开发者在
-
交互 (
mconf)- 运行
make menuconfig,make会启动mconf程序。 mconf读取nemu/Kconfig,生成交互式菜单。
- 运行
-
保存 (
.config)- 在菜单中进行交互设置(启用
CONFIG_DEBUG,关闭CONFIG_CACHE等)。 - 退出保存时,
mconf将选择结果写入nemu/.config文件。
- 在菜单中进行交互设置(启用
-
翻译 (
conf)make继续执行,自动运行conf程序。conf读取nemu/Kconfig(为了了解所有选项的默认值和类型)和nemu/.config(为了了解您的选择)。conf翻译这些设置,生成两个核心文件。
-
生成 (autoconf.h / auto.conf)
conf生成nemu/include/generated/autoconf.h,里面是给 C 代码用的宏。conf生成nemu/include/config/auto.conf,里面是给 Makefile 用的变量。
例如:
-
nemu/Kconfig:1
2
3
4
5
6
7
8
9
10
11
12# 定义一个布尔型(bool)选项,叫 DEBUG
config DEBUG
bool "Enable debugging features"
default n # 默认不开启
help
This enables extra printf messages for debugging.
# 定义一个字符串(string)选项,叫 WELCOME_MSG
config WELCOME_MSG
string "The welcome message to show on boot"
depends on DEBUG # 关键:这个选项只在 DEBUG=y 时才出现!
default "Hello"-
config DEBUG: 定义了一个叫DEBUG的选项,它是个开关(bool)。 -
config WELCOME_MSG: 定义了一个叫WELCOME_MSG的选项,它是个字符串(string)。 -
depends on DEBUG: 建立依赖关系。这说明:只有当DEBUG被选中后,WELCOME_MSG这个选项才允许被设置。
-
-
mconf:- 读取
nemu/Kconfig,显示一个菜单:
1
[ ] Enable debugging features
- 选中:
1
[*] Enable debugging features
- 因为
depends on DEBUG被满足了,mconf显示了新选项:
1
2[*] Enable debugging features
("Hello") The welcome message to show on boot- 修改了欢迎语为
"Welcome to NEMU!"。 - 保存并退出,把结果写入
nemu/.config文件。
- 读取
-
nemu/.config(mconf 的输出):1
2
3
4
5#
# Automatically generated file; DO NOT EDIT.
#
CONFIG_DEBUG=y
CONFIG_WELCOME_MSG="Welcome to NEMU!" -
conf程序(翻译.config):- 生成给 C 代码的
autoconf.h
1
2
3
4
5
6
7
8
9// nemu/include/generated/autoconf.h
/*
* Automatically generated C config header
* DO NOT EDIT
*/
/* (注意:bool 类型的 'y' 被翻译成了 C 语言的 '1') */- 生成给 Makefile 的
auto.conf
1
2
3
4
5
6
7
8# nemu/include/config/auto.conf
#
# Automatically generated make config: DO NOT EDIT
#
CONFIG_DEBUG=y
CONFIG_WELCOME_MSG="Welcome to NEMU!"
/* (注意:这里的内容和 .config 基本一样,格式是 make 变量) */ - 生成给 C 代码的
-
项目文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. 包含 Kconfig 生成的 C 语言宏
int main() {
// 2. 使用布尔型配置 (DEBUG) 来控制代码
// 这部分代码只有在 `make menuconfig` 中
// 勾选了 DEBUG 时才会被编译进去
printf("Debug mode is ON.\n");
// 3. 使用字符串配置 (WELCOME_MSG)
printf("Message: %s\n", CONFIG_WELCOME_MSG);
// 如果没勾选 DEBUG,则只编译这部分
printf("Release mode.\n");
return 0;
}
游戏的配置
实际上游戏是运行时 (Run-Time) 配置,一些软件比如OS内核是编译时 (Compile-Time) 配置:
1 | // (游戏启动时从 settings.ini 读取设置) |
内核内的软件需要性能、体积、稳定,如果采用上述方法,会带来几个问题:
if指令本身会消耗 CPU 周期,会污染 CPU 缓存。当这个检查每秒发生一亿次时,累积的开销是巨大的。- 编译之后体积巨大,比如在服务器上的系统完全不需要蓝牙功能,编译进去造成冗余。
Makefile
filelist
用来管理需要编译的源文件,使用方法如下:
- 在 kconfig 步骤生成 Make 变量,例如:
- 如果选中了
TARGET_AM,它会生成:CONFIG_TARGET_AM=y - 如果没选中,它会生成:
CONFIG_TARGET_AM=n(或者干脆不定义)
- 如果选中了
- 在
filelist.mk中维护四个变量:SRCS-y- 参与编译的源文件的候选集合SRCS-BLACKLIST-y- 不参与编译的源文件的黑名单集合DIRS-y- 参与编译的目录集合, 该目录下的所有文件都会被加入到SRCS-y中DIRS-BLACKLIST-y- 不参与编译的目录集合, 该目录下的所有文件都会被加入到SRCS-BLACKLIST-y中
- 实际使用过程,定义的变量为
DIRS-BLACKLIST-$(CONFIG_TARGET_AM),make 程序读取过程中自动展开。
编译和链接
Makefile 基本规则:
1 | 目标 (Target): 依赖 (Prerequisites) |
- 目标: 想要生成的文件,比如可执行文件
program或目标文件main.o。 - 依赖: 生成这个“目标”所需要的文件。
- 命令: 从“依赖”生成“目标”的具体步骤。
- 变量: 通常在文件顶部用大写字母定义,使用
$(VAR)来引用。(类似于c语言的宏定义,直接字符替换)
详细拆解编译规则:
1 | $(OBJ_DIR)/%.o: %.c |
-
规则:
$(OBJ_DIR)/%.o: %.c$(OBJ_DIR)在初始化已经自动设定- 需要生成
$(OBJ_DIR)/%.o文件(例如build/src/main.o),需要同名的.c文件例如 (src/main.c)。 %是一个通配符,它会匹配两个文件名中相同的部分(既src/main)。
-
自动变量:
$@:目标 - Target$<:第一个依赖 - First Prerequisite
-
命令1:
@echo + CC $<@(静音符):只执行不打印- 如果没有
@,make会先打印echo + CC src/utils/timer.c,然后再执行它,导致输出+ CC src/utils/timer.c,非常啰嗦。 + CC $<(打印的内容):+ CC是一个固定的字符串,让输出更美观。$<是一个第一个依赖(例如在构建timer.o时,$<的值就是src/utils/timer.c)。
- 如果没有
-
命令2:
@mkdir -p $(dir $@)$(dir $@)(要创建的目录):$@是目标名,例如build/obj/src/utils/timer.o。$(dir ...)是一个make内置函数,用于提取路径中的目录部分。例如$(dir build/obj/src/utils/timer.o)的计算结果是build/obj/src/utils/。
-
命令3:
@$(CC) $(CFLAGS) -c -o $@ $<$(CC):代表 C 编译器,一般是gcc。$(CFLAGS):代表所有编译选项(C Flags),例如-O2 -Wall -Werror -I...等。-c:编译器选项,告诉gcc:“只编译,不链接”。即,把.c文件编译成.o目标文件。-o $@:-o是编译器的输出文件选项。$@在这里是输出文件名,例如-o build/obj/src/utils/timer.o
$<:在这里是输入源文件,例如src/utils/timer.c
-
命令4:
$(call call_fixdep, $(@:.o=.d), $@)- 修复优化依赖关系
