对于 kernel-travel 内核镜像,我们用 makefile 进行递归式的构建。在顶级目录的 makefile 中指定了默认的编译架构与工具链路径。若在编译阶段没有指定 ARCH 等编译参数,则会按照默认的 loongarch 进行构建。
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= loongarch
CROSS_COMPILE ?= /opt/gcc-13.2.0-loongarch64-linux-gnu/bin/loongarch64-linux-gnu-
UTS_MACHINE := $(ARCH)
SRCARCH := $(ARCH)
为了方便后期对于多架构的支持,我们规定与体系结构相关的代码都放在 arch 目录下。
并在 makefile 中提供如下规则:只有被 ARCH 指定架构目录下的代码才会被编译。
# 添加体系结构特定的Makefile,SRCARCH 由 ARCH 指定
include $(srctree)/arch/$(SRCARCH)/Makefile
kernel-travel 通过以上构建规则,完成对多架构的适配。
其中定义了如下内容:
- PECOFF 对齐
- 程序头定义
- 内存布局
最终在内核空间中形成如下内存布局:
+------------------------------------+
| .text (代码段) | <- 起始地址: VMLINUX_LOAD_ADDRESS
|------------------------------------|
| .text (代码段) | <- 可读、可写、可执行 (RWX)
|------------------------------------|
| .fixup (修复) |
| .gnu.warning (警告) |
| .altinstructions (备用指令) |
|------------------------------------|
| .got (全局偏移表) | <- 对齐到16字节
|------------------------------------|
| .plt (过程链接表) |
|------------------------------------|
| .got.plt (PLT的GOT) |
|------------------------------------|
| .init.text (初始化代码) |
|------------------------------------|
| .exit.text (退出代码) |
|------------------------------------|
| .init.data (初始化数据) |
|------------------------------------|
| .exit.data (退出数据) |
|------------------------------------|
| .sdata (静态数据) | <- 可读/可写
|------------------------------------|
| .edata (数据结束) |
|------------------------------------|
| .bss (未初始化数据) | <- 以0填充的内存区域
|------------------------------------|
| .gptab.data (GPTAB数据) |
| .gptab.sdata (GPTAB静态数据) |
| .gptab.bss (GPTAB BSS) |
| .gptab.sbss (GPTAB静态BSS) |
|------------------------------------|
| .gnu.attributes (丢弃) |
| .options (丢弃) |
| .eh_frame (丢弃) |
+------------------------------------+
在 uboot 加载为内核映像之后,第一个被执行的函数为 kernel_entry
。
该函数作为内核的入口点函数,完成了以下四项工作:
- 配置直接映射窗口
- 设置CRMD寄存器以启用分页(PG)
- 清空.bss段
- 跳转到
start_kernel
执行
进入到 start_kernel 后对 xkernel 系统环境进行初始化。
void __init __no_sanitize_address start_kernel(void)
{
char str[] = "xkernel";
int cpu = smp_processor_id();
local_irq_disable();
printk("%s %s-%d.%d.%d\n", "hello", str, 0, 0, 1);
setup_arch();//初始化体系结构
mem_init();
trap_init();
irq_init();
thread_init();
timer_init();
pci_init();
console_init();
disk_init();
console_init();
syscall_init();
char buf[512];
vfs_init();
fs_init();
early_boot_irqs_disabled = true;
int fd = sys_open("initcode",O_CREATE|O_RDWR,0);
if (fd == -1) {
printk("open failed");
}
sys_write(fd,init_code,init_code_len);
bufSync();
local_irq_enable();
while (1) {
}
}
对于内存管理的初始化主要包括以下三部分:
-
从设备树中获取 2k1000 的内存信息
void __init memblock_init(void);
-
初始化物理内存池
void __init phy_pool_init(void)
-
初始化 NUMA 架构下的 node 结构
void paging_init(void)
异常处理的初始化中包括了对于中断出处理、系统调用、tlb重填例外的初始化
void tlb_init(int cpu);
void trap_init(void)
void irq_init();
void syscall_init();
-
初始化线程运行环境
在函数
thread_init
中,对于就绪线程列表thread_ready_list
和全部线程列表thread_all_list
、pid 池,进行初始化。并启动了idle
线程。void thread_init();
-
init 进程
init进程的代码位于 command 目录之下。这段代码在编译后被写入磁盘中,以便以后的使用。
int fd = sys_open("initcode",O_CREATE|O_RDWR,0); if (fd == -1) { printk("open failed"); } sys_write(fd,init_code,init_code_len); bufSync();
在对于设备的初始化中,我们完成了对系统时钟、 pci 总线设备、磁盘控制器 SATA、通信串口 uart 的初始化。
void timer_init();
void pci_init();
void disk_init(void);
void real_serial_ns16550a_init(uint64_t base_addr, uint32_t baud_rate);
对文件系统的初始化我们包括两个部分
-
对于 vfs 层的初始化
在 vfs_init 中完成了对于 dcache 的初始化,并创建了 vfs 层的根目录。
void vfs_init(void);
-
对于磁盘文件的初始化
在全国决赛中,我们默认的磁盘文件系统格式为 ext4 ,对于磁盘文件系统的初始化也相当于对 ext4 文件系统的初始化。
void fs_init(void);