随着功能需求与项目规模的增长,我们也在开发中边学习边实践地引入了优秀的软件工程方法。主要有如下几点:
更好的版本控制,由于内核开发中难点众多,预先设想的roadmap往往在实现时遇到阻碍,因此我们采用了更为复杂的多分支开发方式。如在实现新系统调用的同时,另一位同学在重构文件系统、内存系统,此时发现调用层传参的bug则会通过bug-fix分支同时合入两边的开发主线;如在最终对busybox和musl-libc的适配中,某一测例上难以定位bug,则会优先推进另一测例;如信号模块的完整功能实现过程较长,我们会保留开发分支,逐步演进集成。
更好的编码规范,我们能够通过doxygen风格的@todo @bug @note
等注释进行更好的交流交接,通过commit message快速定位bug或是feature来源,通过防御性编程更好的发现问题。我们也正在优化项目的代码结构,将部分过长的单一模块拆分至多个子模块,将过长的头文件移动至源码文件中。
b panic
b reboot
b ld.cc:53
c
set enableLevel=1
set kLogger.outputLevel=1
b trap.cc:222
c
delete breakpoints
add-symbol-file obj/testsuit/sdcard/entry-dynamic.exe
add-symbol-file /mnt/rootb/libc.so 0x70014250
directory obj/testsuit/libc-test/src
directory obj/testsuit/musl/riscv-musl
通过断点计数找到对应测例进入的时机,通过进程切换页表后的临界区进入用户进程地址空间
-
EASTL移植中为了支持C++17的结构化绑定
auto [x,y,z]=tuple1;
,需引入<tuple>
头文件。但EASTL里挺多地方依赖STL,比如此处。其内部实现是自己declare了一套tuple_size/tuple_element
的模板,然而最下面结合std的部分,是直接引用的<tuple>
。因为LuoOS没法引STL给注释了,就缺少了模板类的declare。报错信息也不具有可读性:xxx is not a template
。- 从STL中剥离了所需的模板定义,拷贝至EASTL头文件中解决。
-
报错
undefined reference to __atomic_compare_exchange_1/2
- 先是手动
-latomic
,然而并没有什么效果 - 然后反汇编了一下
libatomic.a
- 本以为是没使用原子指令,其实有
lr.d/sc.d
,当然占比比想象的少很多 - 最后发现没有这个函数,是数据宽度的问题,RISC-V指令只支持4或者8字节的原子操作
- 本以为是没使用原子指令,其实有
- 先是手动
-
尝试使用EASTL中封装好的
lru_cache
结构实现BIO层Cache,然而LRU不会管当前的块是否被占用,碰到了一致性问题。因为LRU通常是one-shot的(如值拷贝),但这里却是transactional的(共享指针使用中,拷贝会被释放),会造成未释放的对象无法通过cache找到,从而同时存在同一物理块的多个拷贝。- 思路:自行实现
shared_lru
,list作为lru并持有ref,map只保留weakref,并使用共享指针的deleter移除weakref,防止map中存在过多expired指针 - 目前的实现其实不好,正常来说每次access都该对lru产生影响,然后很久没access之后就清理了;考虑ref的影响则是引用全释放之后、若不在lru里就清理掉
- 由于ref的存在,为了一致性必须在ref销毁前保留索引
- 思路:自行实现
-
在docker里编译,运行的时候
memInit
末尾的log会卡死在vsnprintf
,表现为一个字符串地址读不了;在本地编译则不会出现该症状。- 有个段叫srodata,之前一直给忽略了
- 之前实际上srodta藏在rodata页的末尾,碰巧字符串多了之后,docker里的编译结果可能会srodata变长超出rodata页
- 改了下链接脚本,把其他的srodata都扔rodata里了
-
某个版本的编译结果能在QEMU 5上跑通,但不能在7.0上跑通,调整调试信息级别后都跑不通了,clone测例会死循环。表现为,ctx存着kctx的内容
- 原因:
_strapexit
里之前留了个地方记录ctx.pc<0x100000
的情况,结果是在切页表中间,新版QEMU估计检测更严格,写入就expection了 - 为防止切页表之后和当前特权级之间可能有不一致性,加个assertion
- 原因:
-
内核无法开O1优化
- 一番debug发现tp被改写了,似乎并不保证不使用tp。所以后续需继续优化内核运行环境,用sscratch之类的寄存器存hartid或者khartobj指针
-
疑似使用释放之后的blockbuf进行写入
- C++千万记得该
=delete
的构造函数得声明、该引用的得引用,不能随便把指针解引用
- C++千万记得该
-
busybox --help
会死在close_file
的__lockfile
里,busybox sh --help
会死在xfunc_die
里- 调试后发现,这里又是因为装载elf的时候行为有所偏差。filesz部分是装进内存的,余下的应当赋0,而且这个长度也不是页对齐的
-
busybox cat cmd.txt | read line
会卡死在管道上,经检查发现是管道行为有所偏差:- 如果管道中没有数据,并且没有其他进程打开了写入端,那么read()会返回0,表示已经到达文件末尾。
- 如果管道中没有数据,并且有其他进程打开了写入端,并且没有设置非阻塞标志,那么read()会阻塞,直到有数据可读或者写入端被关闭。
- 如果管道中没有数据,并且有其他进程打开了写入端,并且设置了非阻塞标志,那么read()会返回-1,并且设置errno为EAGAIN或者EWOULDBLOCK,表示暂时没有数据可读。
- 如果管道中有数据,并且要读取的字节数(count)大于或等于管道中的字节数,那么read()会返回实际读取到的字节数,并且清空管道。
- 如果管道中有数据,并且要读取的字节数(count)小于管道中的字节数,那么read()会返回要读取的字节数(count),并且保留剩余的数据在管道中。
-
运行lua测例会panic在libc初始化过程中。为了方便调试重新编译添加dbgsym,结果却跑通了;把-g去掉再跑,还是通了;甚至musl查不到挂掉的elf里的好多符号
- 可能是初期制作sdcard.img时,错误修改了编译脚本,严重怀疑不是musl编译出来的,而是glibc
-
libc-test脚本中间的部分测试无法通过,也有一些不甚理解原因
- 有个测例挂在用户栈空间不足,调成8 pages又好了,为什么不是通过rlimit之类的获取和调整呢?
- 实现了futex,靠后的几个test都过了,唯独一开始的几个pthread测试过不了
- 中间的socket没有实现,其会读取到一个非UTF-8字符从而无法通过评测。最后模仿busybox测例,用
grep -v -e xxx | read line eval line
对部分测例进行黑名单
-
尝试实现动态链接库,用户态出错后都是汇编难以调试
- 自行开启
--enable-debug
选项编译了musl。由于此处是使用的动态链接库,也不需要重新编译全部的测例 - 调试脚本,主要是自己编译的musl,以及动态链接库要给定
.text
起址b panic b reboot b ld.cc:53 c set enableLevel=1 set kLogger.outputLevel=1 b trap.cc:222 c delete breakpoints add-symbol-file obj/testsuit/sdcard/entry-dynamic.exe add-symbol-file /mnt/rootb/libc.so 0x70014250 directory obj/testsuit/libc-test/src directory obj/testsuit/musl/riscv-musl
- 给
0x5ebc0([email protected])
打了watch point,结果突然没触发就变成0了 - 考虑是mprotect挂了,查看后确实如此
- 还两个问题,一个是
base+len-1
,一个是splitChild
之后的offset没偏移
- 还两个问题,一个是
-
> #[[new bing]] 根据我的搜索结果,如果你用mmap系统调用映射了一个大小为8KB的文件到内存里,但是映射区域长度为12KB,那么对超出部分的读写会有以下的行为: - 如果你使用**MAP_PRIVATE**标志,那么超出部分的读取会返回**零**,写入会导致**拷贝写**分配,但是不会影响文件本身¹²。 - 如果你使用**MAP_SHARED**标志,那么超出部分的读取会返回**零**,写入会导致文件被**扩展**并且反映到磁盘上²³。这种情况下,你必须用**O_RDWR**打开文件,并且用**PROT_WRITE**保护映射区域²。 希望这能帮到你。👍 Source: Conversation with Bing, 2023/7/31 (1) why we can mmap to a file but exceed the file size?. https://stackoverflow.com/questions/47604431/why-we-can-mmap-to-a-file-but-exceed-the-file-size. (2) c - Mmap() an entire large file - Stack Overflow. https://stackoverflow.com/questions/7222164/mmap-an-entire-large-file. (3) What is the behaviour of a file-backed memory map when reading from or .... https://unix.stackexchange.com/questions/616848/what-is-the-behaviour-of-a-file-backed-memory-map-when-reading-from-or-writing-t. (4) mmap - How big can a memory-mapped file be? - Stack Overflow. https://stackoverflow.com/questions/726471/how-big-can-a-memory-mapped-file-be.
- 自行开启
-
向VisonFive2 开发板移植内核,卡死在
csrw satp
指令上。由于在线使用开发板无法使用JTAG等进行调试,只能盲猜原因- 最后在群友的提醒下,发现privileged手册中对Sv39页表项的a/d位有两种定义。通常接触的是硬件去设置a/d以提醒软件的方式,但开发板往往实现的是第二种——若未设置a/d位则触发一次page fault,让软件来更改页表项。
-
实现VisionFive 2的SDIO驱动,区域赛阶段曾移植过SD卡驱动,移植自egos-2000 yhzhang,因此以为较为容易。但实际适配时才发现,SD卡控制器规格繁多,此前移植的是采用GPIO操作SPI总线的方式,而VF2似乎不支持该方式。进一步适配发现,VF2的SDIO控制器与sdcard.org官网上的标准控制器也不同,寄存器映射完全不一样。
- 查看了另一支参赛队MankorOS的仓库后,想起技术支持群中曾发过一份寄存器地址映射表。目前正在按照该规格重新实现SDIO驱动,完成了时钟初始化、CMD0、CMD8、ACMD41。