Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: 第四阶段总结-邵卓炜
date: 2025-06-30 22:23:12
tags:
---

### 基于Iouring的异步运行时
我首先选择完成的任务是[基于iouring的用户态异步运行时](https://github.com/Shao-ZW/kunio),支持常见的文件和网络(Tcp)io。在我的实践经历来看,构建一个高性能的用户态异步运行时,就像是在应用程序内部再造了一个微型的操作系统,它接管了传统上由内核负责的部分调度和I/O管理职责,目的是为了消除内核/用户态切换的开销,并最大化I/O吞吐量。

为了实现的简单,我选择了thread-per-core 的任务调度模型。简单来说,thread-per-core可以理解为每个CPU核心分配一个独立的执行线程,每个线程(每个核心)都拥有自己的任务队列。当一个异步任务被提交时,它会被放入相应核心的任务队列中。这种设计有几个优势,一是减少竞争,由于每个线程操作自己的队列,线程间避免了共享锁的开销;二是缓存友好:任务和数据在特定核心上处理,能更好地利用CPU缓存,减少缓存失效,提高数据访问速度;三是不需要对每个任务有Send的限制。

IO接口的异步封装,负责与将os暴露的io接口改造成rust async/await异步语法的形式。传统的I/O模型(如 select, poll, epoll)虽然是非阻塞的,但它们本质上是“事件通知”模型——通知你有事件发生了,你再去读取数据。这依然涉及用户态和内核态之间的多次上下文切换。io_uring 则是提供了更为本质的异步io接口,一种全新的提交-完成模型————队列 (SQ):用户态应用程序将各种I/O操作(如文件读写、网络套接字的发送接收等)封装成请求,批量地放入一个共享的内核提交队列中;完成队列 (CQ):内核处理完这些I/O请求后,会将结果(成功与否、处理了多少字节等)批量地放入另一个共享的内核完成队列中。应用程序只需定期检查这个完成队列,就能得知哪些I/O操作已经完成,以及它们的结果。

### 无锁ringbuffer BBQ
为了进一步优化我们比赛的OS内核的任务队列,我选择参考BBQ paper实现的[无锁ringbuffer](https://github.com/Shao-ZW/bbring),虽然最终性能并不理想,但实现该结构是一个很有趣的历程。大多数lock-free ringbuffer基于version+idx组成的 Atmoicusize 作为头尾指针,并通过loop + CAS方式更新头尾指针,version主要用于解决ABA问题;而BBQ通过将数组分块,头尾指针变为头尾块指针,并且在每个块的内部额外维护2个指针(allocated/reserved)以及2个计数(committed/consumed),一个显然的好处是头节点可以直接通过FAA指令获取分配位置。我对我实现的bbq进行了性能测试,目前实现的BBQ的性能表现非常糟糕,对比crossbeam-arrayqueue,尤其在SPMC、MPMC场景下吞吐差距在10倍以上甚至更多。并且我在实践中认为算法本身还有些边缘情形处理的问题,感兴趣的同学可移步[讨论区](https://github.com/Shao-ZW/bbring/discussions/1)。无锁的设计总是“危险”而精妙的,哪怕论文给出算法伪代码,实现的过程依然是相当曲折的,内存序的问题,aba问题,以及如何调整测试复现特定的bug,这个过程只有踩过坑才能知道痛。

### os内核赛中组件化和异步化尝试
关于我们的[比赛内核](https://github.com/greatbridf/osdev),我和我的队友在原先宏内核的基础上做了大量的改动,内容聚焦在组件化拆分以及异步化改造,前者主要集中在工作量上的庞大,如果确定好组件的依赖,如何设计出合适接口,这都需要仔细考量;异步化的改造客观来说工作量也很大,这是async传染性带来的必然,(如果重头构建一个异步内核可能相对好点),所以说目前我们为了必然大范围的传染性,会使用block_on语义的函数做一个暂时解决方案。异步os一个大的优势是不需要对每个task分配内核栈,这确实会节约相当大的内存开销,但任务异步化引入的问题之一就是内核抢占,2024届内核赛获奖内核[Phoenix](https://gitlab.eduxiji.net/educg-group-26010-2376550/T202418123993075-2940)给出的解决方案是通过设置抢占标志允许至多一次的内核抢占,这是一个不错的方案,但通用性能否做的更好一点呢?或许早先组会上听到有无栈的结合是最佳的解决方案,但由于内核比赛测试临近,最近的工作在不停的修syscall,暂时没时间研究,希望能在决赛时拿出我们认为优秀的解决方案。

相关参考资料
monoio设计介绍[https://rustmagazine.github.io/rust_magazine_2021/chapter_12/monoio.html]
iouring介绍[https://arthurchiao.art/blog/intro-to-io-uring-zh/]
BBQ论文[https://www.usenix.org/conference/atc22/presentation/wang-jiawei]
AsyncOs[https://asyncos.github.io]