![c0e771ce2360f193d2cd7ffb26c7d136.png](https://i-blog.csdnimg.cn/blog_migrate/5f205de0c37e1757624cec0eb054c18b.png)
![048030e3676899f7c21b11241c920f4a.png](https://i-blog.csdnimg.cn/blog_migrate/0e9e7a8a299087a135a43535d8f4d58c.jpeg)
![0ef0e0f007a2fe30624700e292089838.png](https://i-blog.csdnimg.cn/blog_migrate/19b08fbbf57a92e642e992eca2fefa35.png)
摘 要
龙芯在信息化推广中,用户需要在龙芯平台上移植一些社区开源软件,用来支撑应用系统。龙芯长期建设开源软件工程师团队,完成社区开源软件在龙芯平台上的移植工作。 容器热迁移工具CRIU是云平台中广泛使用的工具,经过龙芯工程师的努力,克服汇编语言、动态编译与链接、内核级调试等众多技术难点,最终成功完成移植。本项目移植一共添加和修改代码7655行,其中汇编语言代码200行。代码已经提交CRIU官方网站。通过本项目的经验表明,生态是“做”出来的,龙芯的程序员是有信心不断完善龙芯生态的。 一、为什么要移植CRIU 龙芯在推广过程中,用户使用的社区开源软件都需要移植到龙芯平台上。在应用系统和操作系统之间,有一层“软件生态”,即依托于Linux操作系统而开发的大量社区开源软件。实际应用系统都大量基于这些社区开源软件进行开发,例如Hadoop大数据软件、Docker/Kubernetes虚拟机容器平台、Electron/NW.js等新兴桌面图形编程框架、vlc/ffmpeg等媒体解码框架,这些都是龙芯计算机产品推广中所使用到的开源软件和第三方库。在《龙芯建设软件生态需要什么人才》这篇文章中已经清晰的阐明团队理念。▲ 社区开源软件对应用软件起到重要支撑作用
(选自《龙芯建设软件生态需要什么人才》)
龙芯公司长期建设承担开源软件移植任务的团队。Checkpoint Restore In Userspace(简称CRIU)是在Linux平台上实现进程冻结和恢复功能的开源项目。它可以冻结一个正在运行的程序并存储相关状态数据,存储后的数据可以用来恢复这个程序并保证恢复后的程序从上次被冻结的点开始运行。目前docker已经集成criu,并把它作为容器内运行的进程的热迁移工具。▲ CRIU项目主页(https://criu.org)
criu源码中已经集成的架构有x86、arm等。浪潮Docker云平台使用到了criu这个软件,所以浪潮云迁移到龙芯平台的问题之一就是criu无法在mips下运行。 龙芯生态团队就是为解决客户实际需求而生。我作为龙芯软件生态工程师,承担了criu这个重要云平台软件的移植工作。该工作开始于7月中旬,历时4个月,结果是成功的。开工之初,我简单查看了一下criu源码下x86架构、arm架构相关的代码量后,抱着很乐观的心态开始了criu工具的移植。当我花了半个月完成mips架构相关的代码编写,才发现用于链接脚本的编写、符号表重定位、进程状态信息的存储、汇编语言的编写和调试才是此工作的核心。 二、移植难点 criu声称可以在用户态实现checkpoint/restore功能,完全是依赖于一个叫parasite code的功能模块。parasite(寄生虫)的名字很形象,如果把Linux下一个已运行的程序看作是一个活体,那么parasite code就是一条可以寄生在这个活体上的寄生虫。其实parasite code就是一段可以加载并运行到已运行程序中的代码。通过这段代码,criu就有能力获取到这个程序的当前进程信息,这些状态信息可被用于程序的死后恢复。parasite code有它自己的结构,而且原理并不复杂,但是它和体系架构紧紧依赖,每个架构都要对它重新实现。 在这里,我在尽可能简单描述技术本身的同时对criu移植过程遇到的难点做归类,并谈谈心得体会。为正在进行或将要进行mips软件移植的开发者带来一点儿启发。 在我看来,criu移植的难点在于动态编译与链接、汇编语言编写和调试过程。难点一:动态编译与链接
对于大部分的程序员,平时我们很少接触到这部分内容。因为我们平时的关注点都在程序实现的业务逻辑,很少涉及到程序构建过程的细节(做性能优化的人员可另当别论)。在流行书籍《程序员的自我修养--链接、装载与库》一书,普及了程序构建的编译、汇编和链接过程的一些原理和细节,也对ELF符号表有所介绍。但是也仅仅是入门级的介绍,而且是基于x86的。所以当我遇到类似于“relocation truncated to fit: R_MIPS_CALL16 ”的错误时,一时难于入手。 而类似这样的错误,求助于搜索引擎是无济于事的。解决的办法就是看Executable-and-Linking-Format(ELF)-Specification-v1.2.pdf、mipsabi.pdf文档和binutils的源码(里面是链接工具ld的实现)。最后经过几轮的编写和调试,才完成了R_MIPS_PC16、R_MIPS_64、R_MIPS_LO16、R_MIPS_HI16、R_MIPS_HIGHEST、R_MIPS_HIGHER、R_MIPS_26等符号类型的重定位。 同样在链接脚本(*.lds) 、字节序、进程状态序列化、epc重置等问题上也踩坑无数并花费过多时间。目前还没有中文文档介绍.pdr段的作用,.MIPS.options段的作用,在动态链接的过程它们是否可以被废弃掉?-mno-abicalls作用是什么?这些疑问只能通过检索官方文档和编译器源码来找到答案,所以良好的英文文献阅读能力、源码阅读能力和坚韧平和的心态是移植工程师必备素质。难点二:MIPS汇编语言
criu里面为了让parasite code发挥作用,需要实现一些和架构相关的汇编指令,比如函数调用、系统调用、sigframe、rt_sigreturn、memcpy实现等等。这要求移植者必须具备的2个基本技能就是能读懂x86/arm汇编指令和能编写mips汇编指令。能读懂x86/arm指令还算没有太多门槛,因为除了有官方资料外,网络上的相关博客帖子还是很多的,可以快速帮我入门。但是MIPS汇编语言的编写相关资料少的现象此时就显得很突出了。印象深刻的一个例子就是开始写的第一个mips汇编来实现入口函数的调用,最终实现就下面这10行:ENTRY(__export_parasite_head_start)
.set noreorder
lwa0, __export_parasite_cmd
dlaa1, __export_parasite_args
jalparasite_service
nop
.byte 0x0d, 0x00, 0x00, 0x00 //break
__export_parasite_cmd:
.long 0
END(__export_parasite_head_start)
▲ 龙芯汇编代码雏形
这一点儿代码我不断的修改和调试了近半个月之久,当然这和我从没写过汇编也有很大关系。但是当时我最大的感触就是如果用一本教材让我快速入门mips汇编就好了。能帮助我的只有《See MIPS Run Linux--mips体系结构剖析》和龙芯MIPS指令手册,剩下的信息就只能靠打听、靠猜测、靠调试。 其实《See MIPS Run Linux--mips体系结构剖析》可以说是很经典的一本教程,是从事mips软件开发的必备教程。但这个文档已经年代久远,很多内容没有更新,作为入门教程来说和现实脱节严重。最起码我一开始时是看不进去的,当在编写调试mips指令一段时间后,反过来再去研读才有了一定的收获。对我而言,它和龙芯mips指令文档一样可以作为随身必备手册,需要时及时查阅。未来我更希望有一本类似《龙芯汇编语言教程》的书籍,更多的从实例出发来讲解mips汇编指令。难点三:调试
如果说把代码调试作为难点之一来列出,估计很多从事系统软件开发的同事会有同感。criu的功能很大一部分由ptrace来帮忙实现,这就意味着gdb这个最强大的调试工具我是不能使用了。而且由于有parasite code的存在,criu在和被追踪进程建立socket链接之前,连基本的printf都无法使用,也就是说“打印输出”这个调试方法也是无效的。如果没有ejtag、串口的帮忙,那真的就剩下凭感觉盲调了。 所以很长一段时间内,我的调试手段就是把被追踪进程绑定到一个固定的处理器上(独占cpu0),然后使用ejtag调试。这也是我从业以来第一次使用ejtag,印象最深的是用它解决掉了一个表象为一个string变量值无故改变的问题(最终原因是由于链接脚本没有写好,导致内存数据段的地址重叠,真是自己挖坑埋自己)。当移植工作进行到criu restore阶段时,ejtag也无法使用了,我又启用kernel的print-fatal-signals参数,依赖串口信息继续调试。还有一段时间是靠在汇编指令里面插入break/loop指令来辅助定位问题。真是想尽办法来debug。这还是好的,毕竟有思路有手段。最绝望的一段时间是终端无打印无错误也无阻塞,但是进程就是没有起来。在没有任何思路的情况下,我只能看书。真的就静下心来看《深入Linux内核架构》,找思路。最后不断的在内核里可能地方添加log来最终确认是我少做了TLS(thread local storage)的信息存储导致。在这里我想说:一个bug就代表了你的一个知识盲区,知识盲区解决后bug也就自然解决了。 三、代码提交 龙芯生态源于开源,也要回馈开源,目前我已经把相关改动代码提交到criu开源社区的pull request, 对内核修改的patch也发信给linux kernel,代码审核过程还有一段路要走,争取可以早日并入到criu开源社区。待版本稳定后,用户也可以从loongnix的yum源上下载使用。 由于criu本身和内核版本是强依赖,目前criu能在Mips下运行只能是criu v3.6版本。后续随着内核升级,我也会对criu做持续升级。▲ 龙芯代码已经提交CRIU官方社区
使用git clone https://github.com/sunny868/criu.git 即可下载,下载后切换到criu-mips-supports分支。▲ 龙芯平台上CRIU运行效果
四、回顾与启示回顾这几个月的移植工作,收获颇多,有技术上的也有思想上的。大概总结为以下几点:1. 生态是“做”出来的,龙芯的程序员是有信心不断完善龙芯生态的。来龙芯快一年了,一直听周边的同事和客户谈生态。生态这个词其实范围很大,像森林也像大海,树林大了才能有生态形成,无数水滴才能汇成大海,也就是说生态是一步一步的做出来的。龙芯工程师踏踏实实做事,同时倡导源于开源、回馈开源。2. 最难的软件是需要写汇编语言的软件。 本项目移植一共添加和修改代码7655行,其中汇编语言代码200行。在龙芯移植的所有社区软件中,像CRIU这样紧密涉及到内核和汇编语言的软件移植项目并不会很多。有了criu的移植经验,对于我后续的移植工作是有力的经验积累,应该不会有更难的任务了。3. 希望自己能有机会写一本《龙芯汇编语言教程》。有这本书可以解决目前网络上文章数量少、知识碎片的问题,给开发者提供成体系、具有更高实用性的技术材料,让更多的开发者更容易的加入到mips的生态建设中来。![fd4fb08fcbbd78173a7e399e2e35414d.png](https://i-blog.csdnimg.cn/blog_migrate/67d2c73a9d095ae30b93156588c45a91.jpeg)
作者简介:
孙国云 龙芯软件生态工程师
多年Linux系统、移植平台、消费类产品的软件方案设计和研发经验。擅长领域:Linux、移动OS系统开发,音视频编解码,正在研究云平台、大数据人工智能等技术方向。申请技术专利多篇。
相关链接
《龙芯建设软件生态需要什么人才》
《龙芯KVM虚拟机研发纪实》
点击“在看”,好内容一起看
![f127f6ab4abb18de840d2038a7800b9e.gif](https://i-blog.csdnimg.cn/blog_migrate/02e90aefbfa794367cf5f6209cba12d3.gif)