在第三节讲了二进制文件*.rbf的结构,在本节中,我主要想讲一下此文件的执行,从这里开始,就和内核文件密切相关关了。在内核代码中,*.rbf 主要是由lms2012工程相关的程序处理。在lms2012工程的bytecodes.h中共定义了5个执行槽,分别执行界面程序、用户程序,用户命令,终端命令,调试程序。界面程序就是启动EV3后看到的界面,用户程序就是通过LEGO MINDSTORMS Education EV3 运行按钮或者界面运行按钮启动的用户程序。
当用户程序被启动后,每个执行槽同时只能载入一个可执行程序*.rbf,各个执行槽之间的程序可以进行运行或者阻塞状态切换,也就是说最多同时只能有五个程序处于运行或者等待运行之中。在EV3操作系统启动后,自动启动lms程序,该程序还是二进制的机器语言程序,由lms2012编译生成,有点像一个微型的操作系统,它负责管理上面的五个执行槽中的程序运行。在该程序启动,就启动一个叫UI.rbf的程序,该程序就是我们在LED屏上看到的界面。该程序在GUI_SLOT执行槽中执行。该UI程序又可以载入用户程序*.rbf程序到USER_SLOT执行槽中执行。 在装在用户程序后(载入用户程序一般是UI.rbf执行),lms调用ProgramReset()函数对程序进程进行初始化。初始首先是CMomory 分配内存。内存大小由GetAmountOfRamForImage()函数计算。一个用户程序需要的内存的大小是由三部分组成的, a 全局变量的大小(四字节对齐) b 每个Object的表头的链表表头(表头占用的字节本身是4字节对齐的) c 每个Object的局部变量的大小(四字节对齐)
内存分配成功后,由MemoryInstance.pPoolList 进行管理 MemoryInstance.pPoolList[PrgId][TmpHandle].pPool; MemoryInstance.pPoolList[PrgId][TmpHandle].Type = Type; MemoryInstance.pPoolList[PrgId][TmpHandle].Size = Size; 该结构体分别记下了申请的内存的类型、大小、和起始位置。 每个进程槽可管理MAX_HANDLES(500)个以内的内存缓冲区。 cMemoryOpen(PrgId,RamSize,(void**)&pData); 最后一个参数传回的是申请到的缓冲区的首地址MemoryInstance.pPoolList[PrgId][TmpHandle].pPool。 在ProgramReset()初始成功后即可载入将该进程的切入运行态,运行的起始地址为第一个Object的偏移地址Offset to instructions。 在进程执行时,PrimParPointer()函数实现了类似于CPU程序计数器PC的功能,它自动实现一个函数一个函数的访问。访问的方法是通过第一个DATA8数据定位到具体的函数,有了函数就能知道这个函数的具体参数个数,然后依据后面接着的DATA8的数据位去访问具体的参数。访问参数的规则是依据下图自动从局部变量或者全局变量或者常量位置中提取参数。
通过对*.rbf文件的分析,我们就可以看出B语言中的模块调用对应的内核中的函数了,我们就可以依据这些函数,对内核进行改写。LEGO公开的文档和帮助算是比较完整的了,软硬件基本都能够找到源代码。我唯一没有找到源码的是LEGO MINDSTORMS Education EV3软件的源代码,听说这是第三方开发的。我也不知道 *.ev3文件是如何编译成*.rbf文件的,如果谁有这方面的资料,望不吝赐教。不过我们依据对*.rbf的分析基本够看出*.vix文件对应内核中的函数。
本期内容的确是没有啥实物可供下载,如果非要找提供实物, 第三期倒是有个Ract.ev3 编译后的 Ract.rbf 文件 Ract_Note.txt 和本主题相关,感兴趣可以下来看看。
链接:https://pan.baidu.com/s/1kas53edBp44qB_wgrzqGDw?pwd=euhg
|