使用动态模块
当要在系统中测试使用动态模块,需要编译一份支持动态模块的固件,以及需要运行的动态模块。下面将固件和动态模块的编译方式分为两部分进行介绍。
编译固件
当要使用动态模块时,需要在固件的配置中打开对应的选项,如下配置:
- RT-Thread Components --->
- POSIX layer and C standard library --->
- [*] Enable dynamic module with dlopen/dlsym/dlclose feature
也需要在bsp对应的rtconfig.py中设置动态模块编译时需要用到的配置参数:
- M_CFLAGS = CFLAGS + ' -mlong-calls -fPIC '
- M_CXXFLAGS = CXXFLAGS + ' -mlong-calls -fPIC'
- M_LFLAGS = DEVICE + CXXFLAGS + ' -Wl,--gc-sections,-z,max-page-size=0x4' +\
- ' -shared -fPIC -nostartfiles -nostdlib -static-libgcc'
- M_POST_ACTION = STRIP + ' -R .hash $TARGET\n' + SIZE + ' $TARGET \n'
- M_BIN_PATH = r'E:\qemu-dev310\fatdisk\root'
相关的解释如下:
- M_CFLAGS - 动态模块编译时用到的C代码编译参数,一般此处以PIC方式进行编译(即代码地址支持浮动方式执行);
- M_CXXFLAGS - 动态模块编译时用到的C++代码编译参数,参数和上面的M_CFLAGS类似;
- M_LFLAGS - 动态模块进行链接时的参数。同样是PIC方式,并且是按照共享库方式链接(部分链接);
- M_POST_ACTIOn - 动态模块编译完成后要进行的动作,这里会对elf文件进行strip下,以减少elf文件的大小;
- M_BIN_PATH - 当动态模块编译成功时,对应的动态模块文件是否需要复制到统一的地方;
基本上来说,ARM9、Cortex-A、Cortex-M系列的这些编译配置参数是一样的。
内核固件也会通过RTM(function)
的方式导出一些函数API给动态模块使用,这些导出符号可以在msh下通过命令
- list_symbols
列出固件中所有导出的符号信息。dlmodule
加载器也是把动态模块中需要解析的符号按照这里导出的符号表来进行解析,完成最终的绑定动作。
这段符号表会放在一个专门的,名字是RTMSymTab的section中,所以对应的固件链接脚本也需要保留这块区域,而不会被链接器优化移除掉。可以在链接脚本中添加对应的信息:
- /* section information for modules */
- . = ALIGN(4);
- __rtmsymtab_start = .;
- KEEP(*(RTMSymTab))
- __rtmsymtab_end = .;
然后在bsp工程目录下执行scons
正确无误地生成固件后,在bsp工程目录下执行:
- scons --target=ua -s
来生成编译动态模块时需要包括的内核头文件搜索路径及全局宏定义。
建立动态模块
在github上有一份独立仓库: rtthread-apps ,这份仓库中放置了一些和动态模块,动态库相关的示例。
其目录结构如下:
目录名 | 说明 |
---|---|
cxx | 演示了如何在动态模块中使用C++进行编程 |
hello | 最简单的hello world 示例 |
lib | 动态库的示例 |
md5 | 为一个文件产生 md5 码 |
tools | 动态模块编译时需要使用到的Python/SConscript脚本 |
ymodem | 通过串口以 YModem协议下载一个文件到文件系统上 |
可以把这份git clone到本地,然后在命令行下以scons工具进行编译,如果是Windows平台,推荐使用RT-Thread/ENV工具。
进入控制台命令行后,进入到这个rtthread-apps repo所在的目录(同样的,请保证这个目录所在全路径不包含空格,中文字符等字符),并设置好两个变量:
- RTT_ROOT - 指向到RT-Thread代码的根目录;
- BSP_ROOT - 指向到BSP的工程目录;
Windows下可以使用(假设使用的BSP是qemu-vexpress-a9):
- set RTT_ROOT=d:\your_rtthread
- set BSP_ROOT=d:\your_rtthread\bsp\qemu-vexpress-a9
来设置对应的环境变量。然后使用如下命令来编译动态模块,例如hello的例子:
- scons --app=hello
编译成功后,它会在rtthread-apps/hello目录下生成hello.mo文件。
也可以编译动态库,例如lib的例子:
- scons --lib=lib
编译成功后,它会在rtthread-apps/lib目录下生成lib.so文件。
我们可以把这些mo、so文件放到RT-Thread文件系统下。在msh下,可以简单的以hello
命令方式执行hello.mo
动态模块:
- msh />ls
- Directory /:
- hello.mo 1368
- lib.so 1376
- msh />hello
- msh />Hello, world
调用hello后,会执行hello.mo里的main函数,执行完毕后退出对应的动态模块。其中hello/main.c
的代码如下:
- #include <stdio.h>
- int main(int argc, char *argv[])
- {
- printf("Hello, world\n");
- return 0;
- }