repo地址:https://github.com/KveinAxel/async-os
课题背景
RISC-V N拓展
N 扩展允许用户态程序发生中断和例外后,直接进入用户态的处理程序,不触发外层运行环境响应。用户态中断主要用于支持存在 M 模式和 U 模式的安全嵌入式系统(见第 10章) 。不过,它也能支持类 Unix 操作系统中的用户态中断。当在 Unix 环境中使用时,传统的信号处理机制依然保留,而用户态中断可以用来做未来的扩展,产生诸如垃圾回收屏障(garbage collection barriers) 、整数溢出(integer overflow)、浮点陷入(floating-point traps)等用户态事件。
——RISC-V 手册
尽管RISC-V N拓展目前只是一个草案,但他带来的用户态中断已经让内核开发者垂涎许久。不管是不用陷入内核的外部中断带来的方便,还是对传统信号处理机制的优化,RISC-V N拓展都是看上去十分有益的尝试。
软硬协同的用户态中断
该系统是贺鲲鹏、尤予阳同学基于该草案进一步完善,提出了一种符合规范的模拟器、FPGA 实现。仓库地址是:https://github.com/Gallium70/rv-n-ext-impl
我本次的异步系统调用实现便是基于此系统,此系统提供的用户态中断实现可以方便地为异步系统调用提供高效地支持。
研究现状
异步操作系统设计方案
该方案是一共经历了五版,非常详细地讨论了异步操作系统的设计方案,里面也提到了异步系统调用的设计,但该设计并未基于用户态中断机制,而提供了一种类似linux kernel中的io-uring的机制。
他的机制如下:
第一次系统调用:
- 用户:准备系统调用参数、发出系统调用请求
- 内核:映射共享内存、获取系统调用参数、发起相应服务协程的异步执行、返回共享内存中的服务响应队列信息给用户进程;
- 内核进程执行完服务协程后,在响应队列保存返回值;
第二次系统调用:
- 用户进程在请求队列准备系统调用参数;在共享内存的响应队列中查看第一次系统调用的结果;
- 内核进程在完成第一个服务协程后,在共享内存的响应队列中保存返回值,主动查询新的系统调用请求,并执行;如果没有新的请求,则让出 CPU;
研究工作
根据异步操作系统设计方案与用户态中断的实现,我设计了一个异步系统调用的方案。
用户视图
- 用户利用异步系统调用函数和 Rust 的 async/await ,生成对应的 Future 树,交由对应的 UserExecutor 进行处理。
- UserExecutor 取出一个 UserTask,判断是否已经注册到UserReactor 。对没注册的任务 poll 一次后,若为 pending则注册到 UserReactor。对于其他注册的任务,查询UserReactor,若准备就绪就 Poll 一次并更新在 UserReactor的状态。
- 对于其中的 Leaf Future ,在 UserExecutor 的执行流中,会发送系统调用,陷入内核,在内核简单注册后立即返回Pending。
- 内核完成后,会向用户发送用户态中断
- 用户态中断处理程序向 UserReactor 发送事件唤醒对应的UserTask
内核视图
- 内核陷入程序判断是 UserEnvTrap 在将寄存器参数和执行流交由内核中的 syscall 函数处理。
- 对于有异步拓展的 syscall 函数首先判断系统调用的异步参数(编码后的用户任务号)是否为 0. 0 代表是同步系统调用,非零则代表是异步系统调用
- 异步版本的系统调用会将生成的 Future 交给 KernelExecutor,并返回Future 的注册信息(成功与否) 。
- 陷入函数退出。
其他情况
- 对于单核情况。用户注册完异步系统调用并陷入内核后,由于内核优先级高,内核会不断处理 KernelTask 在处理完毕后返回用户时,直接会触发用户态中断并唤醒对应的 UserTask
- 对于多核情况。内核和用户进程位于两个硬件线程,用户进程利用异步机制,在发起系统调用后,可以避免阻塞等待,运行其他的任务。而内核也可以同时完成系统调用的内容。从而实现,用户进程和内核系统调用同时高负载运行。
最终进度
- 内核态运行时
- 用户态运行时
- 系统调用实现
- pipe
- close
- read
- write
- 测例
- 文档