UNO-OS是一款RISCV平台的,宏内核操作系统,该内核部分参考了Linux和xv6的设计逻辑,同时也加入了一系列新的设计理念。
以下为Uno-OS系统的结构概览
详细架构如下
./kernel/
├── boot # 启动模块
│ ├── entry.S # 用于加载start函数
│ ├── main.c # 内核入口函数,完成必要的初始化工作
│ ├── start.c # start函数,完成从M模式向S模式的跳转
│ └── Makefile
├── dev # 设备和驱动模块
│ ├── console.c # 控制台设备
│ ├── plic.c # 处理PLIC外设中断
│ ├── timer.c # 系统时钟模块
│ ├── uart.c # 处理UART外设中断
│ ├── virtio.c # qemu虚拟IO接口
│ └── Makefile
├── fs # 文件和IO模块
│ ├── bitmap.c # 文件系统中的bitmap模块
│ ├── buf.c # IO模块的缓冲层
│ ├── dir.c # 文件目录模块
│ ├── file.c # 文件结构抽象模块
│ ├── fs.c # 文件系统的管理
│ ├── inode.c # 索引节点的管理
│ └── Makefile
├── lib # 相关的实用工具库
│ ├── print.c # 打印和调试
│ ├── sleeplock.c # 睡眠锁
│ ├── spinlock.c # 自旋锁
│ ├── str.c # 字符串操作
│ └── Makefile
├── mem # 内存管理模块
│ ├── kvm.c # 内核内存管理
│ ├── mmap.c # 内存直接映射管理
│ ├── pmem.c # 物理内存分配管理
│ ├── uvm.c # 用户页表管理
│ └── Makefile
├── proc # 进程管理模块
│ ├── cpu.c # cpu管理模块
│ ├── exec.c # elf程序执行模块
│ ├── proc.c # 进程调度模块
│ ├── swtch.S # 上下文切换
│ └── Makefile
├── syscall # 系统调用管理模块
│ ├── syscall.c # 系统调用处理
│ ├── sysfile.c # 文件相关系统调用
│ ├── sysproc.c # 进程相关系统调用
│ ├── syspower.c # 电源管理相关系统调用
│ └── Makefile
├── trap # 中断处理模块
| ├── trampoline.S # 内核态和进程态切换的处理
| ├── trap_kernel.c # M模式中断处理
| ├── trap.S # 进行上下文切换相关工作
| ├── trap_user.c # S模式中断处理
| └── Makefile
├── Makefile
└── kernel.ld
我们使用Ubuntu 22.04/Ubuntu24.04和QEMU-riscv 5.1.0开发完成
Uno-OS 系统的构建和运行相关参数写在 Makefile 中,要运行该系统,在命令行下输入:
$ make qemu即可运行系统
目前已经完成了boot模块,中断处理模块,内存管理模块,进程调度模块,文件系统模块这些操作系统所必备的模块和功能,此外,我们实现了数十条常用的系统调用。
- 中断处理模块:参考xv6中的中断处理实现,处理了时钟中断以及PLIC和CLINT中断
- 内存管理模块:实现了COW策略,设计了一个支持栈,堆和mmap的虚拟内存系统
- 进程调度模块:实现了高响应比优先(HRRN)调度算法
- 文件系统模块:实现了类似linux下ext2的文件系统,支持文件创建,删除,读写等操作
在实现上述模块的同时,为了保证系统的稳定性,我们还进行了大量的测试,包括内核态的测试和用户态的测试。
目前已实现的系统调用
int sys_exec(char* path, char** argv); // 从磁盘中加载并执行一个ELF文件
uint64 sys_brk(uint64 new_heap_top); // 扩展或收缩用户堆大小
uint64 sys_mmap(uint64 start, uint32 len); // 分配一个指定的mmap区域
uint64 sys_munmap(uint64 start, uint32 len); // 释放一个指定的mmap区域
int sys_fork(); // 完全复制当前进程的状态(除了pid)
int sys_wait(void* addr); // 进程进入休眠态,等待任一子进程的退出后将其唤醒
int sys_exit(int exit_state); // 当前进程退出,如果有子进程,则将子进程交由proczero管理
int sys_sleep(uint32 seconds); // 令当前进程睡眠指定的系统时钟刻(10个系统时钟刻约为1秒)
int sys_open(char* path, uint32 open_mode); // 打开或创建文件
int sys_close(int fd); // 关闭指定的文件
uint32 sys_read(int fd, uint32 len, void* addr); // 对于一个给定的文件,读取不超过指定的长度个字节到数组中
uint32 sys_write(int fd, uint32 len, const void* addr); // 对于一个给定的文件,写入不超过指定的长度个字节到文件中
uint32 sys_lseek(int fd, uint32 offset, int flags); // 设置文件偏移量
int sys_dup(int fd); // 复制文件描述符
int sys_fstat(int fd, fstat_t* state); // 获取文件信息
uint32 sys_getdir(int fd, dirent_t* addr, uint32 len); // 获取目录中的目录项
int sys_mkdir(char* path); // 根据指定的目录路径创建目录
int sys_chdir(char* path); // 修改当前工作目录
int sys_link(char* old_path, char* new_path); // 创建文件链接
int sys_unlink(char* path); // 删除文件链接,如果链接数为0则删除文件
int sys_pid(); // 获取当前进程的pid
int sys_ppid(); // 获取当前进程的父进程的pid
uint64 sys_time(); // 获取当前系统时间
uint64 sys_halt(); // 关机
uint64 sys_reboot(); // 重启
uint64 sys_curhide(); // 隐藏光标
uint64 sys_curshow(); // 显示光标
uint64 sys_backcolor(uint32 red,uint32 green,uint32 blue); // 设置背景颜色
uint64 sys_forecolor(uint32 red,uint32 green,uint32 blue); // 设置前景颜色
uint64 sys_clear(); // 清屏
uint64 sys_conreset(); // 重置控制台属性(颜色等)截至12月24日,该系统总共经历了100余次修改(以commit次数计算)。可以参考我们的commit记录
总的代码量约为6000行(去除注释)
以下为各个版本完成的工作:
| 版本号 | 完成内容 | 分支链接 |
|---|---|---|
| v-0.1alpha | 完成boot模块并实现必要的库 | gitlab |
| v-0.2alpha | 完成虚拟内存模块 | gitlab |
| v-0.3alpha | 完成中断处理模块 | gitlab |
| v-0.4alpha | 完成进程结构的设计 | gitlab |
| v-0.5alpha | 完成进程调度模块 | gitlab |
| v-0.6alpha | 完成磁盘底层操作接口 | gitlab |
| v-0.7alpha | 完成文件系统模块 | gitlab |
| v-0.8alpha | 完成基础的系统调用 | gitlab |
| v1.0(master) | 实现了COW和高响应比优先调度算法 | gitlab |
我们实现的创新点包括但不限于:
- 实现了 COW 策略
- 实现了类似 UNIX 下的多级索引文件结构
- 实现了高响应比优先(HRRN)算法
- 优化磁盘缓冲区算法,保证即使在最坏情况下也只需遍历一次链表
- 实现UNIX下的管道机制 (✓)
- 实现懒分配策略
- 进行更加完备的测试 (✓)
- 实现更多系统调用 (✓)
上述内容详见UNO-OS内核设计手册
