ffxz 发表于 2010-4-15 13:14:32

RT-Thread 0.4.0的新特性:应用模块

一直在考虑这个名字应该叫做什么,

最开始时沿用动态模块加载

后来觉得这个东西不应该类似于RTOS中的一些.o加载(例如vxWorks中的.o加载),应该更偏向应用性质,叫做应用吧。因为初始目的是,隔离系统与应用。

.o加载有它的坏处,例如.o加进去了就不容易拿出来,屁股搽不干净

而RT-Thread的努力则是,一切处理得干干净净。所以,更愿意称这个为应用。

但毕竟这个不是传统意义上的应用,所以这个姑且采用两者的混合体 —— 应用模块。

这个功能应该是RT-Thread所特有的:最开始时想把它划分到组件中,形成一个单独的组件,但这个将会是RT-Thread的一个基本功能,还不如直接放到内核中。所以,直接在内核的代码基础上进行修改吧。

这部分现在由shaolin来完成,这个部分与GUI、内核,才最终形成RT-Thread的优秀基因,才具备问鼎Linux、 BSD的实力。
<唔,我们无意与Linux、BSD竞争,因为我们都是开源同根生的兄弟>

shaolin已经在trunk下放出了加载器部分代码了,会在最近做一些demo进行演示(组件目录中的hello、thread实际上已经是可运行的模块,只是编译脚本还没放上去,scons脚本临时出了些bug)。

lgnq 发表于 2010-4-15 15:50:28

我觉得kernel部分越精简越好,只提供RTOS基本功能即可。
像kservice可以放在kernel外,因为有些应用不需要用到rt_kprintf

STM32_Study 发表于 2010-4-15 15:57:03

同意楼上

kernel提供最核心的功能,其他的全部都要可以分离的

zlei 发表于 2010-4-15 16:28:58

在eCos中,这个叫做Object loader,觉得这个命名不错,很底层……

July 03, 2009
Object loader enhancements

Anthony Tonizzo and Gernot Zankl have contributed enhancements to the eCos object loader package.
The enhancements support ARMv4T object relocation and the loading of objects directly from ROM
(without a filesystem) respectively. The revised code is only available from the CVS repository
at this time.

ffxz 发表于 2010-4-15 17:02:46

回复【1楼】lgnq
我觉得kernel部分越精简越好,只提供RTOS基本功能即可。
像kservice可以放在kernel外,因为有些应用不需要用到rt_kprintf

-----------------------------------------------------------------------

是的,这部分正考虑这么干,kservice只保留一个最最小集合。

zzzzzzzzzz 发表于 2010-4-17 17:45:34

建议楼主画一个图 就会很直观

ffxz 发表于 2010-4-25 22:27:38

现在module的情况,内核可以使用keil mdk或GCC编译,模块暂时还只能使用GCC来编译。

既然是使用GCC来编译,那么是否有可能STM32上也能够使用?<完全动态的似乎有些困难,毕竟内存摆在哪里,半动态?例如,把一部分module塞到片内FLASH中做静态链接>

这实际上是一个非常有意思的话题。

ffxz 发表于 2010-4-25 22:34:09

周末shaolin给我发邮件说,实际上RT-Thread应用模块这种情况很早很早以前是有先例的。也确实,人的智慧都差不多了,不可能你想到的东西别人一直想不到(呵呵,而且只会早不会晚):
把模块当做应用来使用,还有高通的brew平台,它也是类似这样整出了个applet,并且做得更加彻底,改成COM接口。

当然,RT-Thread不是高通,也不可能有这么大的影响力,但是至少说明这是一类方向,并且技术困难也不会见得有多大。

ffxz 发表于 2010-5-19 08:07:46

大概很多人不明白,RT-Thread中的文件系统实现实质是一个什么。

本身,RT-Thread这边目前并没有放出具体的文件系统实现,都是“拿来”的,以前的elfs,现在的ELM FatFs。

接触过radio的,知道STM32 Radio支持两个文件系统(不同介质上的两个文件系统,一个SPI flash,一个SD卡),除了这个之外就不知道了。

支持两种介质,并用统一的文件API访问,这个ELM FatFs也能够做到。RT-Thread的设备文件系统好处是什么?

具体的来说,RT-Thread的设备文件系统只是一套文件访问的接口,并且访问接口力图与POSIX标准相兼容。

POSIX兼容做到,RT-Thread上的文件操作与PC(UNIX:Unix、Linux、FreeBSD等)上的相同。额外的,用户也不需要关系底层文件系统的实现,不需要理会它是FAT的或其他的。

这个似乎在没有多样化的文件系统支持前没什么说服力,都是FAT嘛

0.4.x会加入新的文件系统支持:
- 第一个亮相的是NFS
(注:加入NFS文件系统后,访问NFS上的文件目录,API不需要有任何更高,依然是那几个POSIX文件接口)

网络文件系统(Network File System,NFS ),是在 Unix 系统间实现磁盘文件共享的一种方法,它支持应用程序在客户端通过网络访问位于服务器磁盘中数据的一种文件系统协议。最早于1984年由SUN开发。功能是通过网络让不同的机器、不同的操作系统能够彼此分享个别的数据。

RT-Thread/NFS这是一个NFS Client v3的实行,RT-Thread设备能够直接mount Unix类主机的文件系统,能够直接读写网络端的文件,对于一些数据采集类的设备,能够直接把结果传到服务器中,而不再需要自定义网络协议。那么对于RT-Thread 0.4.x意味着什么:应用模块的本地开发,模块不再需要烧写到flash上,也不需要复制到SD卡上,直接在RT-Thread设备上通过NFS的方式加载应用模块。另外这个特性也非常适合于一些高清播放器,通过WIFI直接访问主机上的文件系统,然后播放出来。

NFS v3文件系统的实现不算多,大多数RTOS NFS的实现是NFS v2。这个最大的问题当然就是Windows上是否有相配套的NFS服务器,因为Windows并不是Unix(Linux、FreeBSD有原生的NFS v3/v4支持),并且这个服务端实现是NFS v3。自由软件是神奇的,小巧的FreeNFS软件,一个绿色文件,200k,把NFS v3服务器所搞定了。

那么还有什么,静等发布时。。。

taoriran 发表于 2010-5-19 08:20:12

支持!期待!

zhiyuan1106 发表于 2010-5-19 08:23:55

支持

flight871 发表于 2010-5-19 09:14:44

.o加载 和 NFS 对调试和应用都是很方便的,以前想过等空了,自己看看vxworks 和 linux的代码学习在rt-thread 上加,现在看来有人已经干了,呵呵

ffxz 发表于 2010-5-19 09:56:01

回复【11楼】flight871
.o加载 和 nfs 对调试和应用都是很方便的,以前想过等空了,自己看看vxworks 和 linux的代码学习在rt-thread 上加,现在看来有人已经干了,呵呵
-----------------------------------------------------------------------

(⊙o⊙)…欢迎加入其他的!0.4.x的计划是POSIX兼容,newlib、pthread都需要更多的人来完成。

gzc1017 发表于 2010-5-20 12:46:58

支持!加油!

byx8379 发表于 2010-7-8 17:42:49

不懂 ,学习中

langley 发表于 2010-7-9 23:35:07

就是传说中的应用与内核分开加载?

shaolin 发表于 2010-7-10 09:11:41

是的,目前这块功能已经具备,可以实现从文件系统中加载应用程序。
还需要继续优化和完善达到实用的目的,用于该功能的pc端的后链接工具也在探索制作中,0.4.X发布时,这部分功能应该就能够实用了。

9509238 发表于 2010-7-10 12:36:33

这个功能是以后大存储系统必需的,而且更容易实现应用程序远程升级等特性。重定位过程的实现可以放在rtt中,也可以放在pc的后期链接中,很灵活。

langley 发表于 2010-7-10 20:15:12

一直在玩ucos,它做的中断管理做还可以,不过发现它内核频繁的开关中断

langley 发表于 2010-7-10 20:18:22

回复【16楼】shaolin
是的,目前这块功能已经具备,可以实现从文件系统中加载应用程序。
还需要继续优化和完善达到实用的目的,用于该功能的pc端的后链接工具也在探索制作中,0.4.x发布时,这部分功能应该就能够实用了。
-----------------------------------------------------------------------
arm的编译器好像不支持可重定位的编译选项吧?从文件系统中加载应用程序是怎么实现的呢

ffxz 发表于 2010-8-28 07:52:38

\ | /
- RT -   Thread Operating System
/ | \ 0.4.0 build Aug 28 2010
2006 - 2010 Copyright by rt-thread team

八月    Auguest 2010
----------------------------------
SUNMONTUEWEDTHUFRISAT
----------------------------------
1    2    3    4    5    6    7
8    9    10   11   12   13   14
15   16   17   18   19   20   21
22   23   24   25   26   27   28
29   30   31
In SD ready
found part, begin: 116224, size: 1.890GB
finsh>>File System initialized!
dm9000 id: 0x90000a46
event size: 32
event size: 20
operating at 100M full duplex mode
TCP/IP initialized!
NFSv3 File System initialized! <--- NFS文件系统加载成功

>>
finsh>>run_module("/nfs/basicapp.mo") <-- 加载应用模块: basicapp.mo
read 2207 bytes from file
rt_module_load: basicapp.mo
relocate symbol b
relocate symbol c
relocate symbol a
relocate symbol rt_kprintf <-- 重定向符号表
      0, 0x00000000
finsh>>application entry <-- 执行应用模块,入口函数是 rt_application_entry
Hello RT-Thread 1 1
Hello RT-Thread 2 2
Hello RT-Thread 3 3
Hello RT-Thread 4 4
Hello RT-Thread 5 5
Hello RT-Thread 6 6
Hello RT-Thread 7 7
Hello RT-Thread 8 8
Hello RT-Thread 9 9
Hello RT-Thread 10 10
Hello RT-Thread 11 11
Hello RT-Thread 12 12
Hello RT-Thread 13 13
Hello RT-Thread 14 14
Hello RT-Thread 15 15
Hello RT-Thread 16 16
Hello RT-Thread 17 17
Hello RT-Thread 18 18
Hello RT-Thread 19 19
Hello RT-Thread 20 20
Hello RT-Thread 21 21
Hello RT-Thread 22 22
Hello RT-Thread 23 23
Hello RT-Thread 24 24
Hello RT-Thread 25 25
Hello RT-Thread 26 26
Hello RT-Thread 27 27
Hello RT-Thread 28 28
Hello RT-Thread 29 29
Hello RT-Thread 30 30
Hello RT-Thread 31 31
Hello RT-Thread 32 32
Hello RT-Thread 33 33
Hello RT-Thread 34 34
Hello RT-Thread 35 35
Hello RT-Thread 36 36
Hello RT-Thread 37 37
Hello RT-Thread 38 38
Hello RT-Thread 39 39
Hello RT-Thread 40 40
Hello RT-Thread 41 41
Hello RT-Thread 42 42
Hello RT-Thread 43 43
Hello RT-Thread 44 44
Hello RT-Thread 45 45
Hello RT-Thread 46 46
Hello RT-Thread 47 47
Hello RT-Thread 48 48
Hello RT-Thread 49 49
Hello RT-Thread 50 50
Hello RT-Thread 51 51
Hello RT-Thread 52 52
Hello RT-Thread 53 53
Hello RT-Thread 54 54
Hello RT-Thread 55 55
Hello RT-Thread 56 56
Hello RT-Thread 57 57
Hello RT-Thread 58 58
Hello RT-Thread 59 59
Hello RT-Thread 60 60
Hello RT-Thread 61 61
Hello RT-Thread 62 62
Hello RT-Thread 63 63
Hello RT-Thread 64 64
Hello RT-Thread 65 65
Hello RT-Thread 66 66
Hello RT-Thread 67 67
Hello RT-Thread 68 68
Hello RT-Thread 69 69
Hello RT-Thread 70 70
Hello RT-Thread 71 71
Hello RT-Thread 72 72
Hello RT-Thread 73 73
Hello RT-Thread 74 74
Hello RT-Thread 75 75
Hello RT-Thread 76 76
Hello RT-Thread 77 77
Hello RT-Thread 78 78
Hello RT-Thread 79 79
Hello RT-Thread 80 80
Hello RT-Thread 81 81
Hello RT-Thread 82 82
Hello RT-Thread 83 83
Hello RT-Thread 84 84
Hello RT-Thread 85 85
Hello RT-Thread 86 86
Hello RT-Thread 87 87
Hello RT-Thread 88 88
Hello RT-Thread 89 89
Hello RT-Thread 90 90
Hello RT-Thread 91 91
Hello RT-Thread 92 92
Hello RT-Thread 93 93
Hello RT-Thread 94 94
Hello RT-Thread 95 95
Hello RT-Thread 96 96
Hello RT-Thread 97 97
Hello RT-Thread 98 98
Hello RT-Thread 99 99
Hello RT-Thread 100 100

basicapp.mo的代码为:
#include <rtthread.h>

int a = 0;
int b = 1000000;
int c = 0;

static void function(int count1, int count2, int count3)
{
        rt_kprintf("Hello RT-Thread %d %d\n", count1, count2, count3);
}

int rt_application_entry(void)
{
        int i;
        rt_kprintf("application entry\n");
        for(i=0; i<100; i++)
        {       
                a++;
                b--;
                c++;
                function(a, c, b );
        }

        return 0;
}

====
通过应用模块,能够直接在主机上编译文件,形成应用模块,然后放到NFS网络文件系统上,

在finsh shell中,再通过run_module来执行一个应用模块,不再需要复杂的烧写动作。

clever0725 发表于 2010-8-28 08:25:36

越来越精彩了

cyxavr 发表于 2010-8-28 08:58:29

今天有空,过来顶贴的。

gaiwang42 发表于 2010-8-28 11:02:04

想问一下 应用模块怎么重定位rtt系统的api 是像linux系统调用那样 用中断号传递吗
应用模块有可能对系统内核造成的安全问题,目前有哪些考量,比如说数据访问越界
另外,应用模块之间是否会存在相互依赖,如解析符号重定位等

shaolin 发表于 2010-8-29 08:25:59

动态加载是指将应用程序从文件系统中加载到操作系统中运行,其中会遇到的一个问题是是应用程序如何调用操作系统提供的接口API。
一般来说有三种方式:
第一,中断陷入方式
应用程序执行中断陷入或者软中断指令,同时提供中断陷入ID号,进入软件中断模式,由内核接管,内核获得ID号,调用ID号对应的内核函数,执行完后返回,退出软件中断模式。
第二,符号链接方式
操作系统维护一组内核符号表,该符号表中有内核接口的地址信息。应用程序在编译时生成部分链接的可执行文件,在将应用程序加载到操作系统中时将未链接的应用程序符号和操作系统内核符号表进行运行时链接和重定位,得到完整的执行环境。
第三,指针传递方式
所有内核接口地址存放在一个表中,该数组中的每个内核函数偏移位置固定,在加载应用程序时将该接口地址表的指针传递给应用程序的入口函数,然后应用程序即可以通过该指针以及内核函数偏移位置就可以调用操作系统接口功能了。

RT-Thread module采用的是第二种,符号链接方式。模块出错和模块之间的依赖问题,RT-Thread都考虑了相应的解决方案,不久后就会看到。

ffxz 发表于 2010-8-29 09:03:19

中断陷入方式(俗称软中断、系统调用)会涉及到运作状态的转换,从用户态切换到核心态。通常实时系统都不采用这类方式,这样做了后实时响应能力将大幅降低,例如linux,通常只能做到ms级别的响应能力。而一般RTOS则轻松做到us级别的响应能力(当然不仅仅是这个因素造成的,还有很多其他的因素,例如linux的MMU策略,copy-on-write等)。

gaiwang42 发表于 2010-8-29 10:27:27

.

gaiwang42 发表于 2010-8-29 10:30:22

如上所述,那编译和链接应用模块的工具需要自己来开发

gaiwang42 发表于 2010-8-29 10:40:31

开发编译和链接的工具难度比较大,因为需要考虑不同cpu构架的问题等各种问题
rtt是否有考虑过采用预链接的方法,方法虽然没有动态连接的灵活,但是开发起来简单
也就是在编译内核的源码树下来编译应用模块,因为内核已经编译完成为独立的elf文件,符号地址已经确定了,只需要在makefile中做一个后处理的脚本,导出内核输出的符号表,这样应用模块就可以很容易定位到系统api的运行地址,当然加载时可能需要对应用模块进行版本验证,以便于与内核相匹配

shaolin 发表于 2010-8-29 10:43:07

恩,主要的思想和你想的也差不多。
编译工具不需要自己开发,只要用现有的编译器生成位置独立的目标文件即可。
链接工具这块,已经开发了rtmlinker用来将应用模块中的未决符号和内核符号链接.其实,这块也可以在运行时来做,在加载应用模块时查找未决的符号,然后从内核符号表中找到相应的符号,进行动态链接,只是这样效率上会有点损失。因此,考虑了使用PC端的rtmlinker工具来做这部分的工作,rtmlinker的工作方式是直接和内核elf文件进行链接。

gaiwang42 发表于 2010-8-29 11:20:36

rtt允许应用模块间的依赖吗 即类似linux的so库那样的
如果是动态链接 需要开发一个ld.so程序来在运行时解决符号重定位的问题
符号重定位需要加载和分析elf文件,cpu架构的差异会导致这个程序开发比较困难
在linux上这个ld.so的程序由ld连接器提供,这个需要考虑一大堆cpu构架相关的问题,很多cpu都有构架相关的section

armrtems 发表于 2010-8-29 12:07:36

这个有意思的很,忍不住回个帖。
大家实现的方式应该差不多:操作系统编译成elf的时候内部建立了一个符号表,符号表包含了所有的全局函数声明和全局变量声明。
另建一个工程,生成一个新的elf.然后由操作系统加载这个elf,为新应用模块申请运行内存、解析其中未实现的调用。把引用到的数据替换到原有镜像中的实际地址.
新的elf此时即可以运行,新开一个线程直接运行的话,和一个应用程序没啥区别。
如果不直接应用,把新elf中的数据添加到符号表中,类似加载模块,和linux的就有点像.

wyoujtg 发表于 2010-8-29 13:05:05

呵呵,看来rtt的nfs已经可以working了.

ffxz 发表于 2010-8-29 13:14:58

编译器主要支持gcc及gcc兼容的编译器。gcc在开源社区还是绝对的主流编译器,虽然现在还有另外一支llvm,但还远未到大规模普及的程度,即使普及,估计也会向gcc兼容靠拢。

gaiwang42 发表于 2010-8-29 15:37:35

"由操作系统加载这个elf,为新应用模块申请运行内存、解析其中未实现的调用"

这个就需要做很多事情,不同的cpu构架,实现是不一样的,工作量很大
关键在于不同的cpu构架对elf文件的解析方式也是不一样的

ffxz 发表于 2010-8-29 15:48:58

回复【34楼】gaiwang42
"由操作系统加载这个elf,为新应用模块申请运行内存、解析其中未实现的调用"
这个就需要做很多事情,不同的cpu构架,实现是不一样的,工作量很大
关键在于不同的cpu构架对elf文件的解析方式也是不一样的
-----------------------------------------------------------------------

还好的,不过构架的链接方式总是得做的,就类似你要支持一个芯片,相应的移植代码你不可能不写吧。而未决符号解析部分工作量还好,主要是每类构架的代码需要移植,例如ARM的,MIPS的。然后每类构架中的具体芯片还好,差别不大。

gaiwang42 发表于 2010-8-29 15:52:02

采用预链接方式 可以将这部分工作转交给交叉编译工具来做 检交叉编译工具里面的ld连接器 已经支持很多种cpu构架了

langley 发表于 2010-8-29 18:00:36

看来在MDK里没法玩这个功能

ffxz 发表于 2010-8-29 18:28:10

回复【37楼】langley
看来在mdk里没法玩这个功能
-----------------------------------------------------------------------

那可不是,只是现在Cortex-M上,MDK似乎还不能链接出 PIC 的代码,除非自行写一个全链接器。

【24楼】 shaolin 提到的第三种方法可以用于当前的MDK & Cortex-M上,但Cortex-M芯片上一般内存都比较小,应用了这个功能也没多少发挥的余地。

ffxz 发表于 2010-8-29 18:28:48

回复【32楼】wyoujtg
呵呵,看来rtt的nfs已经可以working了.
-----------------------------------------------------------------------

多谢指出NFSv3实现的问题,现在的NFS确实比较好用了。

jordonwu 发表于 2010-8-30 09:46:05

mark

gavin_li 发表于 2010-8-31 17:44:10

实现应用模块,那以后不就可以像wince, linux一样可以独立开发,安装应用程序了。
呵呵,有意思。关注ing........

itspy 发表于 2010-9-6 18:25:57

关注,静待发布。。。

lhj200304 发表于 2010-9-7 11:38:06

个头也越来越大了

shaolin 发表于 2010-9-7 12:00:22

回复【43楼】lhj200304
个头也越来越大了
-----------------------------------------------------------------------

这部分代码功能是可配置的,只有在选择使用应用模块的时候才打开该配置。
不打开该功能的情况下,编译出来的可执行文件大小不会增加。
另外,这部分代码量不大,对体积的影响很小。

ffxz 发表于 2010-9-7 12:08:37

通过这个特性也能够把一些组件变成一个模块,kernel的个头反而越来越小了^-^

lugang_2920213 发表于 2010-9-10 16:23:53

NFS+应用模块 = RT-Thread 的光芒四射      
看来RT-Thread想不壮大都难

lugang_2920213 发表于 2010-9-10 16:38:49

期待啊

lhj200304 发表于 2010-9-11 16:12:05

越来越爽了啊

lhj200304 发表于 2010-9-11 16:12:26

开发工具要跟上哦

hygbeyond 发表于 2010-9-30 10:10:22

开发工具选择在开发系统时比较重要,但在应用来说,arm-GCC一般都能满足.主要是系统中的调试接口做好了,随时可以访问寄存器等一些系统的参数。不用仿真器调试程序,最后看一下微软工程师写的编程精粹,上面有好多调试技巧。

likazhou 发表于 2010-10-21 16:22:41

期待,,,,,,,,,,

felix_tang 发表于 2011-1-11 14:15:36

持续关注,希望越来越强大。

wenyu520 发表于 2011-1-12 09:39:52

持续关注,希望对应的应用说明也及时跟上啊

hwdpaley 发表于 2011-1-13 00:37:34

希望早点出来!

gfs0521 发表于 2011-1-13 08:31:01

关注并支持 RT-Thread

questioner 发表于 2011-7-5 22:55:03

失败,原来早就能动态加载应用程序了,现在才发现。顶!用全力顶!!

Alexkey 发表于 2011-7-7 13:06:53

rt-thread什么时候能支持STM32F207啊,希望能尽快出个STM32F207的移植

ffxz 发表于 2011-7-7 13:08:38

回复【58楼】Alexkey
rt-thread什么时候能支持stm32f207啊,希望能尽快出个stm32f207的移植
-----------------------------------------------------------------------

0.4.0 beta2发布中已经有207基本移植

waterx3 发表于 2011-7-22 11:30:38

ffxz 能不能出个关于应用模块的教程啊?

ffxz 发表于 2011-7-22 11:49:59

回复【61楼】waterx3
ffxz 能不能出个关于应用模块的教程啊?
-----------------------------------------------------------------------

等0.4.0 rc1吧,关于应用模块的特性,我们还在继续。

liuweiele 发表于 2011-7-22 12:37:37

回复【24楼】shaolin
动态加载是指将应用程序从文件系统中加载到操作系统中运行,其中会遇到的一个问题是是应用程序如何调用操作系统提供的接口api。
一般来说有三种方式:
第一,中断陷入方式
应用程序执行中断陷入或者软中断指令,同时提供中断陷入id号,进入软件中断模式,由内核接管,内核获得id号,调用id号对应的内核函数,执行完后返回,退出软件中断模式。
第二,符号链接方式
操作系统维护一组内核符号表,该符号表中有内核接口的地址信息。应用程序在编译时生成部分链接的可执行文件,在将应用程序加载到操作系统中时将未链接的应用程序符号和操作系统内核符号表进行运行时链接和重定位,得到完整的执行环境。
第三,指针传递方式
所有内核接口地址存放在一个表中,该数组中的每个内核函数偏移位置固定,在加载应用程序时将该接口地址表的指针传递给应用程序的入口函数,然后应用程序即可以通过该指针以及内核函数偏移位置就可以调用操作系......
-----------------------------------------------------------------------

深有体会,SWI方式的系统调用会严重影响系统实时性能...
已经改用其它方式实现了.

valley 发表于 2013-7-25 21:56:51

shaolin 发表于 2010-8-29 08:25 static/image/common/back.gif
动态加载是指将应用程序从文件系统中加载到操作系统中运行,其中会遇到的一个问题是是应用程序如何调用操作 ...

好。找机会看看能不能实现第一种或是第三种。这两种方法应该对编译器和知识背景无较多要求,个人玩玩应该比较容易实现。
页: [1]
查看完整版本: RT-Thread 0.4.0的新特性:应用模块