LOADING

加载过慢请开启缓存 浏览器默认开启

系统能力大赛操作系统内核赛赛后总结

2024/9/10

2024全国大学生系统能力大赛操作系统设计赛内核实现赛道(LoongArch芯片)喜提国三,但实际上我们的作品仍然有许多不足。回望半年来的开发,非常想做一个总结。也希望给那些手搓OS内核的人提供一些经验借鉴。有可能之后还会发布专栏讲解一些OS内核开发需要注意的东西。

作品概览?

当前的作品在gitlab希冀上已经开源,上面也有提供相应的文档(可能写的比较粗糙)。仓库地址:XN6 OS

开发进度?

当前 XN6 OS 已经支持进程管理、内存管理、设备管理、文件系统等基本模块,系统调用兼容Linux(或POSIX标准),可以运行BusyBox并进入Ash或Hush。
当前其中还有不少bug没有修正,比赛已经结束啦不想管啦哈哈哈哈哈

壹. 从XV6到C++重写并支持模块化,再到架构重构

1. 项目的起源

最初我们的项目起源于深大的操作系统实验,那个实验是将XV6从RISCV迁移到了LoongArch上进行的一系列OS内核实验。那个实验使用的qemu是龙芯3A系列的,但是比赛使用的是2K1000,差距还是比较大的,一个用于高性能计算场景,另一个用于嵌入式工控场景,而且XV6那种设计模式并不适合用于比赛——设计太偏向于教学了,很难在这基础之上进行重型的开发。于是,我在小队里面发起了使用C++重写的提议。

2. 为什么用C++?

嗯,C++写的OS内核可以说非常少见了。C++作为系统编程语言与C语言兼容,但是却不适合作为OS内核这种对性能及其敏感的系统软件开发。这一点我也早就想到了,但是对于不熟悉内核实现的我们来说,C++有它独具的优势:面向对象带来的良好可读性与随着标准不断更新带来的对系统软件的支撑库越来越多。这一点对我们十分重要,良好的可读性让我们写出更可靠的代码,一些在freestanding开发中也可使用的库让代码质量更高。

当然性能缺陷是明显的,在开发的后期,我已经能在debug的过程中看到许多问题:比如说大量使用Lambda和functional库使得函数调用栈变得十分长,一次系统调用就可能需要三十几个函数的调用。可以考虑一下,一次函数调用通常要将返回地址和帧指针(帧指针是可以优化掉的,但是会导致无法栈展开也无法在调试中看到调用栈)给压栈,在调用完毕后还要恢复,更糟糕的情况三如果代码组织不好,大量long-call函数更是会导致cache命中率大打折扣,使得系统调用的开销变得巨大。
但是收益是也是明显的。对于不熟悉内核实现的我们而言,我们将一个STL库移植了过来,使得我们的内核中也能使用map,vector,queue,stack等模板类型,带来了良好的类型系统设计,可读性十分可观,并且也对调试十分有利。

可以说,C++避免不了性能损失,但是对于初学者而言,却具有相当的友好性。我知道肯定有人会说如果代码写的好,C++也可以做到性能很高,至少像Python是不能比的,而且同等情况下Java也没法比。但是我要说的是,这里必须理解到C++的一个重要内存模型:虚函数表结构。我相信使用C++肯定是想用到C++的虚类,它使得多态变得十分灵活,但是实际上因为C++内存模型的原因,访问虚函数需要至少两次访存,C语言使用函数指针只要一次访存够了。别杠,OS内核的优化就是这么细,甚至是指令级别的优化;如果有读过Linux内核源码就知道,Linux内核甚至还会充分发挥硬件加速,在if语句中使用分支预测以降低跳转指令带来的惩罚。

虽然我们使用了C++进行内核开发,但是我想说,任何一个OS内核开发人员需要放在首位的需求一定是:性能!性能!性能!

赛后我又去学习了rust,逐渐也明白了rust为什么能挑战C语言在系统软件开发方面的地位。我不得不说,rust作为一门年轻的编程语言,吸纳了很多语言的优点,也提出了许多新的概念,是真的很值得学习。

3. 架构的演变