一个在关键路径上面隐藏了11个月的BUG:DragonOS进程切换查错

news2024/11/15 11:18:14

前言的前面

DragonOS是一个从0开始研发内核及用户态环境的,独立自主的,面向服务器领域的开源操作系统,提供Linux兼容性。

官网:https://DragonOS.org

代码仓库:GitHub - fslongjin/DragonOS: 一个64位的操作系统。An x86_64 operating system.

前言

在写DragonOS的时候,我总是遇到一些神奇的BUG,包括但不限于:

  • 加一行printk(“”);,代码就能正常运行
  • 读写几个无关的变量,代码就能跑了
  • 加一层函数调用,把某个函数wrap一下,代码就能运行
  • 10月份的时候,我和同学调试IDR的源代码,有个单元测试用例就是无法通过。并且,出错的位置总是不相同。将测试用例的数据规模减小之后,就不会报错。
  • XHCI驱动程序在初始化的时候,随机性报错,系统重启后即有概率正常初始化。

上面这些bug,每次碰到,都摸不着头脑,觉得真的是个玄学问题,一直不知道怎么解决他们,根本找不到方向。直到最近,在使用Rust重构CFS调度器的时候,突然间意识到了,上面这些现象,都是来自于进程切换的代码,产生了错误。

先说结论,BUG的产生来自两个方面:

  • 未定义行为的内联汇编代码
  • 切换进程前,存在未完全保存执行现场的调用路径。(也就是说,有时候保存了,有时候没有保存)

我是怎么发现这个bug的?

首先,我使用Rust重构了CFS调度器,这个逻辑不复杂,很快就实现了。

由于原先的C语言版本的代码,调用了这两个宏来进行进程切换:switch_mm()和switch_proc(),分别用来切换页表以及进程上下文。

参见DragonOS-0.1.2的cfs.c的第84、86行:

http://opengrok.ringotek.cn/xref/DragonOS-0.1.2/kernel/src/sched/cfs.c#84

这两个宏主要是汇编代码,长下面这样:

http://opengrok.ringotek.cn/xref/DragonOS-0.1.2/kernel/src/process/process.h?fi=process_switch_mm#process_switch_mm

http://opengrok.ringotek.cn/xref/DragonOS-0.1.2/kernel/src/process/process.h?fi=switch_proc#switch_proc

简单介绍一下这两个宏的作用:

  • process_switch_mm这个宏,主要作用是,将下一个进程的基地址加载到页表基址寄存器CR3中。
  • switch_proc这个宏,首先保存了rbp寄存器(当前栈帧基址)和rsp寄存器(当前栈指针),把他们保存到当前进程的线程结构体中。然后切换到下一个进程的内核栈,同时获取为当前进程的设置一个返回地址(就是switch_proc_ret_addr所在的地址),存到当前进程的线程结构体内的rip成员变量中。并且,往下一个进程的内核栈内,压入下一个进程的返回地址(next->thread->rip),接着,跳转到__switch_to这个函数(注意不是call,而是jmp,因此这里是不会压栈的),进行其他的工作,当__switch_to函数返回时,处理器将会弹出63行压入的“下一个进程的RIP”,这样就完成了进程切换。

后面的实验证明,错误具有两处,其中一处正是发生在switch_proc宏的内联汇编代码之中。

回到重构CFS的话题,我想在Rust代码中,实现切换进程的动作。由于内联汇编的编写有点麻烦,那么最简单、最直接的办法,自然是在C里面加一个函数,把switch_proc和switch_mm这两个宏封装一下,接着直接在Rust里面调用这个C函数即可。

因此,我把这两个宏封装了一下,封装成这样:

http://opengrok.ringotek.cn/xref/DragonOS/kernel/src/sched/core.c?r=d4f3de93#9

注意,我为了避免歧义,在这里把原本的switch_proc()宏,改名为switch_to().在下文中,将用switch_to来代指前文的switch_proc宏。

然后,再在Rust的代码之中,调用了这个函数。本来我以为这样就万事大吉了,但是一运行,处理器就在进程调度的时候,产生了General Protection异常,并且出错的地方,是位于__switch_to函数的ret指令处。(switch to函数里面切换了fs、gs寄存器)

指向这个异常产生的原因有很多,查询Intel开发手册的Volume3A的第6.15章节中,关于General Protection的产生的原因的描述后,大概是这样:

由于文档中,大量的描述是关于那几个段选寄存器的,并且__switch_to函数里面切换了fs、gs寄存器,因此我对进程切换前后,cs、ds、es、fs、gs、ss几个段选寄存器的值,以及将要被换入的值,进行了详细的检查。发现他们的值都是正确的,权限也都是正确的。

Debug陷入了僵局。

解决BUG

我反复思考:为什么这两个宏单独使用就可以运行,独立成函数就不行了呢?是不是因为由于编译器指令重排序优化问题,或者是处理器乱序执行问题导致的?我加了内存屏障,依然无法解决。

BUG的原因之一:未完全保存指执行现场的上下文

在这个时候,我检查发现:在中断结束时调用的sched(),由于进入中断的时候,保存了上下文。除了这种情况以外,其他时候,直接调用sched(),我们并没有对进程当前的执行现场作保存!在这个时候,我联想到之前那些奇怪的BUG,就是文章开头所说的那些。我把他们结合起来思考,突然顿悟:那些玄学的bug的产生,正是因为发生进程调度,而执行现场没有被保存,在进程被重新调度时,由于执行现场的数据缺失,导致其报错!随机性出错的现象,正是因为调度时机不确定导致的!

因此,我对这个问题提出了解决方案:调度器必须在中断上下文中运行,以保证执行现场被完整保存。为了支持那些需要立即调度的场景(与时钟中断触发的调度相对应),我为DragonOS新增了一个系统调用:sys_sched().而原先的sched()函数,功能则改为“发起一个SYS_SCHED系统调用”。这个系统调用就是利用了进入系统调用之前,会由中断处理机制先把执行现场保存了的特点,从而解决了进程的执行现场没有被保存的问题。

具体的代码如图所示:

http://opengrok.ringotek.cn/xref/DragonOS/kernel/src/arch/x86_64/sched.rs?r=d4f3de93#6

http://opengrok.ringotek.cn/xref/DragonOS/kernel/src/sched/core.rs?r=d4f3de93#78

经过上面的修改,所有的能够运行进程调度器,切换进程的路径,都保存了进程的上下文。我想着,这样问题应该就解决了吧?结果一运行,仍然是报错的,还是那个熟悉的General Protection异常。

这个时候,我重新审视了一下上面的代码,经过一个小时的思考,我确认我上面找的确实就是一个BUG,仍然报错肯定是因为还有未发现的bug。

BUG的原因之二:switch_to宏的内联汇编,是未定义行为的代码

我重新思考了很久,我坚信问题一定存在于switch_to和__switch_to这两个地方。但是,在进入这两个地方的前后,寄存器值,以及即将换入的值,我都没有发现异常。我盯着switch_to()宏的代码看了很久,发现它就是有点不对劲!

http://opengrok.ringotek.cn/xref/DragonOS-0.1.2/kernel/src/process/process.h?fi=switch_proc#switch_proc

在这串汇编里面,我修改了rax寄存器的值,并且rax不存在于内联汇编的输入、输出部分,也没有在损坏部分声明。GCC编译器并不知道我在这串汇编里面改了rax寄存器!那么,这段代码的行为就是未定义行为,因为编译器可能会利用rax来存一些临时数据,而我这样就破坏了它。因此,直接在损坏部分(下图第70行加上”rax”寄存器,再运行,bug就解决了!

http://opengrok.ringotek.cn/xref/DragonOS/kernel/src/process/process.h?r=d4f3de93#54

后续测试

为了验证是否像我想的那样,IDR中的大数据测试用例无法通过,且随机性assert failed的现象,是由于进程切换时的BUG导致的,我重新运行了IDR的所有测试用例,都直接通过了。

小结

这个BUG前前后后花了我5天时间去调试,如果算上之前调试实时调度器、IDR、XHCI以及其他模块的时候,由于玄学问题花费的时间,那么总耗时可能达到了将近一个月。真的是,未定义行为的代码,以及未保存上下文这个bug,浪费了我、小伙伴的很多时间。

这个bug,经过了codeQL、cppcheck、ControlFlag、腾讯云的代码检查服务的检测,都没法查出来,真的藏的够深的。或许是因为,那些工具都是为检查应用软件而研发的吧。

查bug不易,感兴趣的小伙伴,欢迎关注我的公众号“灯珑”哦~

转载请注明原文:

一个在关键路径上面隐藏了11个月的BUG:DragonOS进程切换查错​longjin666.cn/?p=1667正在上传…重新上传取消

欢迎关注我的公众号“灯珑”,让我们一起了解更多的事物~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/140921.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于jsp+sevlet+mysql实现用户登陆和增删改查功能

基于jspsevletmysql实现用户登陆和增删改查功能一、系统介绍二、功能展示1.用户登陆2.用户列表3.查询用户信息4.添加用户信息5.修改用户信息6.删除用户信息三、其它系统四、获取源码一、系统介绍 系统主要功能: 用户登陆、添加用户、查询用户、修改用户、删除用户 …

用javascript分类刷leetcode4.贪心(图文视频讲解)

什么是贪心算法 贪心法,又称贪心算法,贪婪算法,在对问题求解时,总是做出在当前看来最好的选择,期望通过每个阶段的局部最优选择达到全局最优,但结果不一定最优 适用场景:简单的说,…

手把手YOLOv5输出热力图

环境要求 我的版本是YOLOV5 7.0 先看结果: 结果仅供参考 具体步骤一: 首先配置好YOLO V5环境 这个采用pip install requirements即可 具体配置环境可以看我其他的博客有详细介绍 GPU环境自己配置 步骤二: 运行YOLO 没问题,输…

Excel表格的导入导出——EasyExcel

参考视频 csdn参考地址 一、导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version> </dependency>二、实体类 方式一&#xff1a;Excel Property&#xff08;&…

Kubernetes Pod 底层实现原理

文章目录前言一、探索 Container1.1 设置实验环境&#xff08;playground&#xff09;1.2 探索容器的 namespace1.3 探索容器的 cgroupsCheck the memory limit.二、探索 Pod2.1 设置实验环境&#xff08;playground&#xff09;2.2 探索 Pod 的容器2.3 探索 Pod 的命名空间2.4…

UDP协议重点总结(附实例)

文章目录前言一、网络的原生情况二、UDP协议2.1 UDP的特点2.1.1 不可靠性2.1.2 无连接&#xff08;不是缺点&#xff09;2.1.3 面向数据报&#xff08;优点&#xff09;2.1.4 缓冲区2.1.5 大小受限2.2 UDP协议端格式2.3 关于校验和2.4 基于UDP的应用层协议三、UDP总结&#xff…

P2279 [HNOI2003]消防局的设立

[HNOI2003]消防局的设立题目描述2020 年&#xff0c;人类在火星上建立了一个庞大的基地群&#xff0c;总共有 n 个基地。起初为了节约材料&#xff0c;人类只修建了 n-1 条道路来连接这些基地&#xff0c;并且每两个基地都能够通过道路到达&#xff0c;所以所有的基地形成了一个…

HTML5本地存储详解

html5 本地存储。前言一、localStorage 对象二、sessionStorage 对象三、localstorage 与 cookie 的区别四、localStorage 和 sessionStorage 二者的区别总结前言 ☀️本地存储是指在客户端存储数据&#xff0c;HTML5 为我们提供了两种 API&#xff0c;分别是 localStorage 与 …

算法是如何炼成的?

一、算 法 简 史算法可以追溯到古代埃及人和古希腊人使用的算术方法。在古代埃及&#xff0c;人们使用简单的加减法来解决基本的数学问题&#xff0c;而在古希腊&#xff0c;人们开始使用更加复杂的算术方法&#xff0c;比如平方、立方、平方根和立方根。随着数学的发展&#x…

css元素转换(旋转函数、rotateX 和 rotateY 的使用、移动函数、缩放函数、过渡、动画)详解

文章目录旋转函数rotateX 和 rotateY 的使用移动函数缩放函数过渡transition-timing-function 属性动画旋转函数 在 CSS3 中&#xff0c;使用 rotate 函数能够让指定的元素对象绕原点旋转&#xff0c;主要在二维空间内进行操作。 其语法格式如下所示&#xff1a; transform: …

【C++常用算法】STL基础语法学习 | 查找算法

目录 ●find ●find_if ●adjacent_find ●binary_ search ●count ●count_if ●find 1.功能描述&#xff1a; 查找指定元素&#xff0c;如果找到则放回指定元素的迭代器&#xff0c;若未找到则返回结束迭代器。 2.查看find定义下底层代码的函数原型&#xff1a; 3.…

Day849.ThreadLocal线程本地存储模式 -Java 性能调优实战

ThreadLocal线程本地存储模式 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于ThreadLocal线程本地存储模式的内容。 民国年间某山东省主席参加某大学校庆演讲&#xff0c;在篮球场看到十来个人穿着裤衩抢一个球&#xff0c;观之实在不雅&#xff0c;于是怒斥学校的…

用于安全医疗保健系统的基于机器学习的可伸缩区块链架构

文章目录背景相关技术简介区块链扩张性电子病历数据安全安全医疗保健的架构基于可扩展区块链架构的机器学习概述基于可扩展区块链架构的机器学习工作流程小结摘要从3.0到4.0的工业革命已经改变了医疗保健环境。患者电子健康记录(EHR)与医学研究机构共享&#xff0c;用于临床研究…

12月榜单丨B站UP主排行榜(飞瓜数据B站)发布!

飞瓜轻数发布2022年12月飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营能力强的B站UP主。飞瓜…

Python:python简介

1&#xff1a;特点 一种解释型&#xff0c;面向对象&#xff0c;动态数据类型的开源高级程序设计语言 其特点就是&#xff1a;优雅&#xff0c;明确&#xff0c;简单&#xff0c;完善的基础代码库和大量的第三方库。 2&#xff1a;解释VS解释 3&#xff1a;应用场景 python…

基于androidstudio校园快递APP系统的设计与实现

1.课题背景及研究的目的和意义 1.1 课题背景 在其发展速度可谓一日千里的电子商务时代&#xff0c;大学生群体已成为网络购物群体中不可或缺的一部分。因此&#xff0c;高校师生对网购的需求也愈来愈强烈&#xff0c;校园快递的问题也成为了焦点&#xff0c;其中校园快递代理…

98%的数据被浪费,企业该如何释放数据价值?

在数字经济时代&#xff0c;对于广大企业来说&#xff0c;数据就是生产资料&#xff0c;算力则是生产力。飞速增长的业务数据&#xff0c;为现代企业提供了最具价值的资产。然而另一方面&#xff0c;如何存储、清理、管理、挖掘、运用数据&#xff0c;也给广大企业提出了艰巨的…

4天带你上手HarmonyOS ArkUI开发——《HarmonyOS ArkUI入门训练营之健康生活实战》

《HarmonyOS ArkUI入门训练营之健康饮食应用》是面向入门开发者打造的实战课程系列。特邀华为终端BG高级开发工程师作为本次训练营讲师&#xff0c;以健康饮食为例&#xff0c;开展技术教学及实战案例分享&#xff0c;助力入门开发者快速提升技能实力进阶。 目标学员 入门开发者…

apache httpClient关于cookie解析的报错处理

报错信息&#xff1a;o.a.h.c.p.ResponseProcessCookies - Invalid cookie header: "Set-Cookie: account"xxxxx"; expiresFri, 03 Feb 2023 06:02:40 GMT; httponly; Path/". Invalid expires attribute: Fri, 03 Feb 2023 06:02:40 GMThttpClient版本&am…

4年翻4倍年薪30W+的测试工程师个人成长之路

欢迎同行来交流&#xff0c;wx 群二维码应该过不了审核&#xff0c;私聊要把。税收图保证真实性。 一、何为测试 简单做一下科普。测试简而言之就是应用上线前&#xff0c;验证应用是否存在bug&#xff0c;是否满足产品的需求。大家津津乐道的程序员&#xff0c;也就是开发&am…