毛晓冬 2010 10 10
为什么在BMP中开始引入MOD1格式,并且同时支持MOD和MOD1两种格式那? 这还得从最初的MOD格式开始说起。
话说,最初时,BREW利用MOD这种格式作为动态模块加载的文件格式。 其实, MOD文件是纯的可执行文件,除了RO DATA, RO Code Section以外,不再包含任何的非可执行的数据,如符号表等。 MOD是从ELF转换而来的,剔除冗余的数据后,就变成了一个纯的可执行文件 MOD。
由于是纯的可执行文件,所以,理论上当MOD被加载后,PC跳转到加载的RAM的起始地址执行即可。 当然,这里需要解决几个问题。
1. 由于是纯的可执行文件格式,加载时,BREW不会负责从额外的信息中提取入口函数地址,即 Entry Point, BREW仅仅是简单的跳转至MOD加载后的RAM起始地址(严格意义上说,这里需要偏移一个AEESTDLIB的 pvt指针,即4个字节)。 所以,我们的MOD Link时,必须要求其Entry Point(AEE_MOD_Load)被Link在最开始的位置,即 First order。
2. 另外,由于MOD是在运行时按需加载,所以会加载到任意的地址。 那么,就必须要求MOD是PI的,即Position Independent, 加载至任何地址都可以运行。 通常PI包含两种, ROPI 和 RWPI。 ROPI(只读位置无关)可以不依赖于运行平台而单独由编译期编译时实现,通常的实现方式为基于PC相对寻址即可。 而RWPI(读写位置无关),通常情况下需要运行平台的支持,需要在运行时进行重定向,否则加载时的RW域地址和运行时域地址不一致,会导致Crash。 由于BREW不依赖于底层平台,所以最初的BREW要求MOD必须是ROPI的。
OK, 告一段落, 上面的两个问题,在最初的MOD中很好的解决了, 在BREW1.0,2.0,3.0都运行的不错, 当时的BREW全部运行与一个Process中,所以,MOD只会被加载一次,此后,整个Process中的BREW全部共享该 in-Memory MOD
时间发展到BREW3.1左右(可能有偏差,应该差不多)。越来越多的呼声要求BREW的Mod中可以使用RW,即全局或者静态变量。 大家知道,当BREW平台不进行更改时,RW数据是不可能在运行时被BREW进行重定向的,所以,依靠BREW平台本身,是不可能支持MOD中包含RW数据的。 那怎么办?! 后来,一个讨巧的办法找到了,就是在MOD中做文章,即利用ELF2MOD工具, 当Build出ELF后,利用该工具转换成Mod文件,那么就可以支持RW数据了, 其基本原理是,将ELF中的RW段的重定向信息以table的形式加到Mod文件的头部, AEEMOD_Load位置顺移。 BREW平台的加载机制仍然没用变化,当然,此时加载后跳转到的入口地址不是AEEMod_Load了,而是一段小型的重定向程序,由其读取重定向table后进行RW的运行时重定向,然后再跳转到AEEMod_Load
如此一来, BREW动态应用中也可以使用全局/静态数据了,而且据我所知,很多的Developer确实这么做了,但是,其带来的很现实的问题也不容小觑。 大的软件思想方面的耦合变紧且不谈,具体的至少有下面两点:
1. ELF2MOD生成的Mod中如果包含RW,那么动态的方式运行没有问题,一旦编成ConstFile Link到Handset的Image中,运行时就会Crash,这是由于,Mod一旦变成ConstFile后,就是ReadOnly了,这是操作系统层次保护的,那么当运行时,对RO段进行写(重定向MOD中的RW)操作,肯定就Crash了。
2. 多应用共享一个In Memory MOD时,如果该MOD中使用了全局/静态数据,而该全局/静态数据为一全局/静态指针,并指向了一段运行时分配的内存,而该内存却在应用的环境下分配,那么,当第一个应用退出后,该内存会被BREW自动释放,那么,这个全局的指针就变成了野指针了。 诸如此类的问题,应该还有很多。
但是,不管怎么样,在BMP出来之前,这样的现状一直持续着。
OK, 新纪元来临了, BMP的到来使得动态模块的文件格式有机会彻底改革一下。 如何改革那?
1. 首先, 对于MOD, 不能去除。 必须兼容它, 并兼容它的行为。 因为任何事物我们都应该慢慢的演进,并存,而不应该以激进的方式。毕竟遗留的Mod Style的代码和设计理念还是大量存在。
2. OK,保留Mod,且兼容它的行为,所以, 仍然保证一个Process内完全共享一个in memory MOD,而不管它是否包含RW。
3. 由于BMP支持多Process,那么,在多Process间是否需要Share 同一个in memory MOD那,还是每个Process都加载一个Mod到自己的Memory内?
由于MOD格式不包含任何运行时可以给平台检测的额外信息,诸如是否RO,或者包含RW。 另外,已经存在的现实是,ELF2MOD工具使得MOD中可以包含RW,而包含RW的MOD必须不能被多个Process共享。 所以,BMP为了同时兼容MOD为RO和包含RW两种,只能让每个Process都单独加载MOD到自己的Memory,而不管Mod究竟是不是真的包含RW。
为了兼容,当然需要牺牲一些性能和特性, 那么,新增加的MOD1格式,当然要把MOD带来的问题都一并解决掉,MOD1提供给那些喜欢尝试新事物,新技术而带来乐趣的的人们。 我们慢慢看来:
1. 首先, MOD是纯可执行文件格式,不包含任何额外的信息,使得平台无法区分其是RO还是包含RW,从而不能区分处理。 其实,对于RO的MOD,整个系统的所有Process都可以通过memory share和mapping的方式共享一份in memory MOD。
2. 所以,MOD1采用了ELF格式,而不再是纯的可执行文件格式,由于ELF包含了众多特性,如支持重定向,从而天生支持了RW, 也支持Entry Point特性,使得BMP平台可以从ELF头中获取Entry Point并调用, 还有,支持表征是否包含RW数据。
3. OK,由于MOD1采用ELF格式,所以,BMP加载时,需要做一些额外的ELF头分析处理后,才最终调用。
4. 由于MOD1现在可以支持表征是否是RO还是包含RW, 所以,BMP可以作出决策,如果运行时加载的MOD1是RO的, 那么整个系统的所有进程全部共享一份Code,即 in memory MOD1, 即所谓的Code Sharing。
如果MOD1是包含RW的,那么就不能进行Code Sharing
5. 对于RW的MOD1,不能再按Process加载了,因为这样的话,多个Applet运行于一个Process中,同时去使用一个MOD1的话,还是会出现之前MOD中谈到的野指针的问题。 所以,对于RW的MOD1,将不再按照Per Process加载的策略, 而是按照Per Env加载的策略。 大家知道,在BMP中,即便是一个Process中的多个Applet,他们分别都运行在自己独立的Env中,所以,他们都会加载自己的MOD1(如果MOD1包含RW),这样的话,RW的读写相互不会影响了。 而对于处于一个Process中的多个Service(BMP中的概念),他们共享一个Env,所以共享一个In Memory MOD1, 但这个没有问题,因为Service运行时,不存在App Ctx的内存绑定的问题。
OK, 至此, MOD1解决了遗留的MOD的所有问题。 当然, MOD还是存在的, 到底用MOD 还是 MOD1, 各位看客自己决定拉~~~~
------解决方案--------------------
原来如北,当时在考虑怎么在bmp上用全局静态变量呢。。东方很厉害,看的很明白
------解决方案--------------------
多进程共享时候,每个进程多一份rw段的备份就行了吧,为啥每个进行还得吧所有的东西都加载一遍。。
------解决方案--------------------
MOD1 的service 应用很有特色