一、项目背景
StarryOS是基于ArceOS模块基础的Linux兼容OS。实现了绝大多数的linux syscall,并可以运行很多linux用户软件。本次训练营项目阶段,我通过添加测试,下载使用真实linux应用,与linux kernel对比等等方式,丰富和完善了starry的功能。
二、主要任务
本次训练营,我的工作主要分为了三个阶段来进行。
2.1 阶段一:socket数据面(Data Plane)syscall测例与语义完善
2.1.1 syscall簇功能介绍
此syscall簇共有四个子syscall。
sendto- 向指定地址发送数据报。主要用于udp协议recvfrom- 接收数据报并获取源地址sendmsg- 发送高级消息(Scatter/Gather + 控制信息)recvmsg- 接收高级消息(状态回填 + 截断检测)
2.1.2 测试的添加过程
我与大模型合作,阅读了linux接口文档,根据所有的输出语义,生成了syscall的测试。结果76个测试中,错误了34个。大部分的socket处理不符合语义。详细阅读了源码,我发现了几个重大的问题,并且逐个进行修复。
2.1.3 测试过程中,进行的源码修复
(1) axerrno模块增强
- 文件:
components/axerrno/src/lib.rs - 新增错误类型:
DestAddrRequired(EDESTADDRREQ) - 目标地址必需MessageTooLong(EMSGSIZE) - 消息太长
- 用途: 支持更精确的socket错误返回
(2) sendto系统调用修复
- 文件:
os/StarryOS/kernel/src/syscall/net/io.rs - 修复内容:
- 修复地址参数处理逻辑:
addr=NULL时返回None(不管addrlen),addrlen=0时返回EINVAL - 简化send flags处理:使用
SendFlags::from_bits_retain(flags)直接解析所有标志位 - 支持更多MSG_*标志(MSG_MORE, MSG_OOB, MSG_DONTROUTE等)
- 修复地址参数处理逻辑:
(3) recvmsg截断状态跟踪
- 文件:
os/StarryOS/kernel/src/syscall/net/io.rs - 新增功能:
- 添加
truncated_out参数跟踪消息截断状态 - 在recvmsg成功后正确设置
msg.msg_flags(包含MSG_TRUNC标志)
- 添加
(4) UDP模块 MSG_MORE Corking
- 文件:
os/arceos/modules/axnet-ng/src/udp.rs - 实现功能:
- 支持MSG_MORE标志:缓冲多个sendto调用的数据
- 在不带MSG_MORE的调用时一次性发送合并的数据
- Cork状态捕获首次MSG_MORE调用的端点地址(Linux语义)
- 限制cork缓冲区大小(UDP_TX_BUF_LEN)防止内存溢出
(5) UDP recv改进
- 修复: 移除未绑定socket的NotConnected检查(允许bind后立即recv)
- 功能: 支持truncated状态跟踪
(6) Socket标志位扩展
- 文件:
os/arceos/modules/axnet-ng/src/socket.rs - 新增SendFlags:
OOB(0x01) - 带外数据DONTROUTE(0x04) - 不经过网关EOR(0x80) - 记录终止CONFIRM(0x800) - 链路层邻居确认NOSIGNAL(0x4000) - 抑制SIGPIPEMORE(0x8000) - cork/合并发送
- 新增RecvOptions字段:
truncated- 跟踪消息截断状态
(7) Raw Socket修复
- 文件:
os/arceos/modules/axnet-ng/src/raw.rs - 修复内容:
- 添加对MSG_OOB标志的检查(返回OperationNotSupported)
- 添加对MSG_DONTROUTE的TODO注释
- 添加对truncated状态的TODO注释
修复了这些问题后,绝大多数的返回值全部符合linux语义。可以说大部分跟tcp和udp协议相关的传输问题都根本上解决了。
2.2 阶段二:一些小中型应用的测例添加和内核修复
这里我也学习了很久,好在有其他同学的帮助,并且可以参考别人的测试架构。我也构建了smoke normal stress三层次的测试架构。逐步测试和完善应该有的功能。
2.2.1 pip(python包管理器)
测试了pip的各项功能,创建venv环境的能力,以及下载更新等管理py包的能力。
2.2.2 mosquitto(MQTT协议的物联网设备通信软件)
MQTT协议用于低带宽物联网设备通信。这里测试了日常使用的各项功能。
可能存在问题:当前容器内测试没法走网络模拟两个机子的通信。自己创建了一个server和client测试,识别到在一个ip就会直接loopback。需要补充真实板测来模拟结果。
2.2.3 ffmpeg(音视频处理软件)
用于测试starryos的大规模mmap,流媒体处理,音视频解码能力。使用python自建server来抓取流媒体信息来测试一些操作
2.2.4 app的适配过程中的内核修复
我通过apk包管理器从仓库下载并且运行,然后捕捉错误。同时借助本机linux环境验证和寻找错误。最后锁定了几个内核bug。
通过修复了以下几个重要内核错误,最终实现了三个app的支持。
(1) rsext4 delete_dir 递归删除
文件: components/rsext4/src/file/delete.rs
问题: rmdir会直接递归删除所有的文件。根源是系统会调用delete_dir函数,这个函数是递归删除文件树。(等价rm -rf)这是linux绝不允许的。(linux的标准应该是内核不能有递归删除功能,所有递归删除应该在用户态。因为直接删除所有文件还没提醒非常不安全)
修复内容:
- 新增
is_dir_empty()函数,检查目录是否为空 - 在
delete_dir函数中添加空目录检查,非空目录返回ENOTEMPTY - 添加
Ext4Error::not_empty()构造函数
相关文件:
components/rsext4/src/error/mod.rs- 添加错误类型components/rsext4/src/file/mod.rs- 导出is_dir_emptycomponents/rsext4/src/lib.rs- 导出is_dir_emptyos/arceos/modules/axfs-ng/.../inode.rs- VFS层添加检查os/arceos/modules/axfs/src/fs/ext4fs.rs- 旧VFS层添加检查
测试: 新增bug-ext4-dir-ops测试套件,包含87个断言,覆盖4种架构
(2) xattr 系统调用缺失
文件: os/StarryOS/kernel/src/syscall/fs/ctl.rs
问题: pip会将要删除的文件从用户文件夹移动到/tmp文件夹,作为”临时回收站”。而他使用了rename来实现移动。但是,/root文件夹是rsext4系统,而/tmp是tmpfs系统,存在跨文件系统移动文件夹,于是返回EXDEV。从而使用复制黏贴到目标文件夹,然后再删除源文件夹这种手段。但是复制的时候,会复制文件的拓展属性。这里就调用了xattr族(这里是listxattr,列出附加属性)。而且必须且无法避免。
修复内容:
- 新增12个xattr系统调用存根函数
listxattr系列:返回0(空列表)getxattr系列:返回ENODATAsetxattr/removexattr系列:返回EOPNOTSUPP
相关文件:
os/StarryOS/kernel/src/syscall/mod.rs- 添加12个系统调用分发表项- 覆盖4种架构:x86_64, aarch64, riscv64, loongarch64
测试: 新增test-xattr测试套件,包含10个测试检查点
(3) socket option 缺失
文件: os/arceos/modules/axnet-ng/src/general.rs
问题: Python 3.12的ssl.SSLSocket._create()调用getsockopt(SO_TYPE)来验证socket类型。StarryOS没有实现这个选项,导致TLS握手失败,阻塞了pip install。
修复内容:
- 在
GeneralOptions结构体中添加domain和protocol字段 - 修改构造函数为
GeneralOptions::new(socket_type: i32, domain: i32, protocol: i32) getsockopt返回存储的值;setsockopt返回ENOPROTOOPT(只读,符合Linux语义)
相关文件:
os/arceos/modules/axnet-ng/src/options.rs- 添加SocketType、SocketProtocol、SocketDomain枚举变体os/arceos/modules/axnet-ng/src/opt.rs- 添加SO_TYPE/SO_PROTOCOL/SO_DOMAIN映射os/arceos/modules/axnet-ng/src/tcp.rs、udp.rs、raw.rs- 更新构造调用os/arceos/modules/axnet-ng/src/unix/dgram.rs、unix/stream.rs、vsock/stream.rs- 传递正确的三元组
测试: 新增test-sockopt测试套件,包含9个测试用例,覆盖TCP和UDP socket
(4) rename缓存失效导致uninstall失败
文件: components/axfs-ng-vfs/src/node/dir.rs
问题: 当DirNode::rename通过mem::take转移子目录缓存时,每个子DirEntry的内部Reference.parent强引用仍然指向旧的父目录的DirEntry对象。后续通过新路径进行的unlink等操作会解析到错误的父目录,导致ENOENT错误。
修复内容:
- 移除
rename函数中的子目录缓存转移逻辑 - 保留挂载点转移逻辑
- 子目录条目现在通过延迟查找从磁盘加载,确保
Reference.parent指向正确的父目录
相关文件:
components/axfs-ng-vfs/src/node/dir.rs- 修改DirNode::rename函数
测试: 新增7个测试用例,包括test_rename_then_unlink、test_rename_file_then_delete_old_path等,模拟pip卸载模式
(5) 删除文件时索引偏移导致删除出错
文件: components/rsext4/src/file/delete.rs、components/rsext4/src/file/inode.rs
问题: read_dir使用逻辑索引(只计算未删除的条目),当rm -rf在连续的getdents64调用之间删除条目时,逻辑索引发生偏移,导致后续条目被跳过。
修复内容:
delete.rs: 修改remove_dentry_in_dir_block函数,只清零被删除条目的inode字段,保留rec_len不变inode.rs: 修改read_dir函数,手动遍历原始目录块数据,包括已删除的条目(inode==0),累积物理字节偏移
相关文件:
components/rsext4/src/file/delete.rs- 修改删除逻辑components/rsext4/src/file/inode.rs- 修改目录读取逻辑
测试: 新增2个测试用例:test_readdir_offset_after_delete和test_rm_rf_pattern,使用原始SYS_getdents64系统调用
2.3 阶段三:基于Wayland协议的图形界面的完善和应用测例的添加
在我进入这个阶段时,wayland已经初步支持好了。可以显示样例程序。我随即想要适配一下实际应用。这里遇到很大困难,全网关于此内容的学习资料实在太少。只有官方文档,少数中文博客。视频仅有b站plct实验室有相关内容。在适配的过程中,一半时间大概都是理论学习还有与ai对接内容。这段时间我的学习能力真的遇到极大挑战
2.3.1 ffplay(ffmgeg的播放器,基于sdl2)
视频播放器的适配。主要用于验证sdl2。同时sdl2必须适配opengl组件(它需要gl一个返回值才能正常工作),所以我一并适配了mesa GL库,用llvmpipe模式来进行cpu模拟渲染。
效果:可以以每秒5到10帧播放视频
2.3.2 doomgeneric(doom1993的开源实现,基于sdl2)
doom是非常著名的游戏,开发者非常为之着迷。有句话:有cpu有屏幕就能跑doom。
我基于sdl2进行了一个移植。一并修复和验证了鼠标键盘输入的问题。
效果:可以利用键盘鼠标进行游戏。
2.3.3 qt-calc(计算器应用,基于qt6)
以上两个都是基于sdl2库,我选择qt作为下一个测试的库。
alpain仓库里qt应用不多,我选择了qt-calc来适配。验证qt应用能否正常运行,同时也验证了desktop-shell的运行(堆叠桌面)
效果:可以点击计算结果,并且应用是窗口化的,可以随意移动窗口
2.3.4 app的适配过程中的内核修复
(1) 修复构建系统axbuild的软连接复制问题
文件: scripts/axbuild/src/rootfs/inject.rs
问题: 原先的build阶段,没法把软连接文件(xxxx.so.1等等)复制进starry。我修复了更改逻辑。现在可以成功复制,避免出现缺少文件的情况
修复内容:
- 新增
is_symlink()分支,检测符号链接并生成debugfs symlink命令 - 采用两遍遍历策略:第一遍处理目录和常规文件,第二遍创建符号链接
- 修复
debugfs symlink参数顺序(v1.47.0实际语法与man page不一致) - 相对符号链接目标转换为绝对客户机路径
相关文件:
scripts/axbuild/src/rootfs/inject.rs- 修改collect_overlay_debugfs_commands函数
测试: 新增symlinks_are_emitted_after_regular_files测试,覆盖ldconfig风格的符号链接链
(2) 实现真正的PRIME dma-buf
文件: os/StarryOS/kernel/src/pseudofs/dev/card0.rs
问题: 原先的实现,PRIME_HANDLE_TO_FD是身份映射,在真实场景会导致为定义行为,这也是gl库段错误的根本原因。现在实现注册真正的fd
修复内容:
- 新增
DmaBufGem结构体,包含物理地址范围、引用计数页面和大小 - 修改
handle_prime_handle_to_fd函数,查找dumb表中的句柄,构造DmaBufGem,注册真正的内核fd对象 - 修改
handle_prime_fd_to_handle函数,使用downcast_ref::<DmaBufGem>()进行类型安全导入 - 在
DmaBufGem上实现device_mmap方法,添加边界检查
相关文件:
os/StarryOS/kernel/src/pseudofs/dev/drm.rs- 添加DRM_CAP_PRIME常量和处理函数
安全改进:
- 类型安全:
downcast_ref拒绝非dma-buf类型的fd - 命名空间隔离:fd号不再直接转换为GEM句柄
- 引用计数:
Arc::clone防止并发DESTROY_DUMB或close(fd)导致的use-after-free
(3) 修复输入事件传递
文件: os/StarryOS/kernel/src/pseudofs/dev/event.rs
问题: 解决鼠标/键盘事件无法从内核 evdev 到达 Weston/Qt 的问题。原先内核能接受输入事件,但是传到weston就丢失了。原来是IRQ不触发,libinput 用的是 epoll_wait,需要被唤醒才会去读。
修复内容:
- 新增
has_waiters: AtomicBool字段,跟踪是否有进程在等待 - 新增
start_polling()方法,作为后台轮询任务 - 修改
register_irq()方法,无条件调用start_polling()作为后备方案 - 修改
Pollable::register()方法,设置has_waiters = true
自适应频率:
- 有等待者时(libinput在
epoll_wait中):100Hz轮询(10ms间隔) - 无等待者时(无人读取设备):5Hz轮询(200ms间隔)
安全分析:
- 锁顺序:释放内部锁后再调用
wake(),无死锁风险 - 内存顺序:正确使用
Ordering::Acquire/Release - 竞态条件安全:与IRQ处理程序共享
Mutex<Inner>
(4) DRM stride 修复
文件: os/StarryOS/kernel/src/pseudofs/dev/card0.rs
问题: present_fb() 原先使用 flat memcpy,假设源 buffer 和 axdisplay scanout 的 stride 完全一致。当 GL 渲染器使用的 buffer stride 与 scanout 不同时,每一行偏移若干像素,产生十几段稳定的斜撕裂线(pixman 模式无此问题)。
修复内容:
- 在
Framebuffer结构体中添加stride: u32字段 - 修改
handle_addfb2函数,从dumb buffer的pitch值存储stride - 重写
present_fb函数,支持stride感知的帧缓冲区呈现:- stride匹配(快速路径):零开销的平面
memcpy - stride不匹配(安全路径):逐行复制,带防御性边界检查
- stride匹配(快速路径):零开销的平面
边界检查:
dst_limit = info.fb_size / info.stride.max(1)- 防止除零和边界溢出bytes_per_row = (src_stride as usize).min(info.stride)- 确保源端不溢出rows.min(dst_limit)- 确保复制不超过目标容量
三、达到的收获和效果
3.1 socket调用相关的语义基本对齐linux
修复前,大部分socket相关的syscall语义错误。经过修复后完善了starry的网络栈,网络能力得到了很大的加强。
3.2 多个有价值应用适配starry,并且修复了多个内核bug
修复多个bug,且适配了多个linux app。这些app都是实际生产常用的,证明了starryos实用能力。
3.3 Wayland对opengl cpu模拟模式的支持,对鼠标键盘输入的支持,对desktop-shell的支持
通过内核修复,wayland协议已经可以通过opengl库的llvmpipe(cpu模拟)来实现渲染。相比以前的pixman渲染,虽然都还是cpu渲染,但增强了拓展能力(sdl2和qt6都需要经过gl库进行检查,否则会直接崩溃)。而且理论上只要增加gpu支持,就可以使用硬件加速。因为starry可能在rk3588等有gpu的板子运行,还是很有必要的,后续也可以加入virgl的支持。键盘鼠标的事件之前无法传递到weston。修复后,点击就可以响应了。同时也支持了desptop-shell桌面环境,可以使用堆叠窗口。
总结:现在的StarryOS的桌面环境,已经很接近Linux桌面。可以使用堆叠桌面,使用键鼠交互,使用基于主流图形库的应用。
3.4 Wayland路径目前可运行大部分主流图形库
gtk4,sdl2,qt6三个重要图形库,全部经过实际应用测试。这三个库是大部分gui应用的底层库。这说明理论上很多的gui应用都可以在starry上以wayland协议启动了。
四、学习到的知识和经验
4.1 我学会了多人合作开发代码的基本流程。
在此之前,我只个人开发过一些小项目,没有多人的开发经验。现在我熟悉了基于git的多人工作的方式。这对我未来工作还是参加其他开源项目无疑是极大的帮助。同时也给了我参与开源项目的勇气。
4.2 我学会了与ai合作进行代码开发的基本工作流程。
我学会了如何使用ai加速我的开发速度。也学会了怎么借助大模型,来学习新知识和快速了解问题。在ai时代,学习使用大模型辅助自己,同时又不丢失自己学习的能力。这也是非常重要的技能。
4.3 我对操作系统理论的理解更进一步。
原先我对操作系统的了解仅限rcore这种教学项目,tgoskit更加贴近真实的操作系统内核,让我了解了更加复杂的操作系统的组成和运作方式。相信未来我着手其他更复杂的os项目也能更加得心应手。
4.4 我了解了为内核适配软件的基本方法。
开始我完全不了解怎么进行这个工作。在老师同学帮助下我渐渐熟悉了构建方法。知道了怎么通过导入包,根据各种测试等检测starry的功能,并最后修改kernel来完善功能。通过这些方式,我成功修复了很多问题,支持了数个linux软件,丰富了starry生态。
4.5 我了解了操作系统图形化界面产生的基本原理。
wayland,weston,EGL,DRM,KMS……等等一堆专业的术语,在此之前我完全看着就晕。但是经过几个月的学习,现在的我,已经了解了一个像素怎么产生,到最后怎么显示在一个屏幕上。这对我也是巨大的收获。
五、总结
本次是我第一次参加opencamp训练营。同时也是第一次进入项目阶段。本身我就对rust语言还有操作系统持有浓厚的兴趣。寒假时同学的推荐,给我打开新的大门。几个月的时间,我对os的理解从完全不懂到参与项目,并有一定自己的理解,能找到下一步学习的方向。这些都要感谢所有开源训练营的贡献者。以后我也会积极投入到开源项目的贡献中!
我也知道了ai的力量非常有限。遇到困难的问题,依然需要我去解决。或者写及其具体的提示词才可以解决。我也明白提升自己个人实力才是解决问题的关键还有ai时代要掌握的能力。
感谢opencamp让我有机会接触到这些知识,让我有这些经验。感谢各位老师和同学在这段时间给予的帮助和教导。这段时间收获镇的很多,感觉自己处理问题,学习新知识的能力提升了非常多。我很荣幸能学习这门优质的课程。
六、pr列表
- GitHub ID:
zyc107109102 - 学习进度issue: issue #577
总计13个pr提交
| 序号 | PR链接 | 说明 |
|---|---|---|
| 1 | PR #598 | |
| 2 | PR #854 | |
| 3 | PR #882 | |
| 4 | PR #884 | |
| 5 | PR #938 | |
| 6 | PR #1001 | |
| 7 | PR #1002 | |
| 8 | PR #1072 | |
| 9 | PR #1086 | |
| 10 | PR #1191 | |
| 11 | PR #1268 | |
| 12 | PR #1396 | |
| 13 | PR #1415 |