关于在美拓的简短的总结与体会

时间:2024.5.2

关于在美拓的简短的总结与体会

概要:

这仅仅是个人的一点心得体会与总结,本登不得大雅之堂,然而我作为一个对于手机开发完全的零起点对于其中的艰难与痛苦有着切肤的体会,这一切促使我不敢身藏浅陋将一些零星琐碎公之于众,即作为自己在美拓的简短人生经历的一个总结,如果能给后来者提供一点点地借鉴也心满意足了。

1. 关于编译环境的认识体会

首先面对程序员的就是相对复杂的编译环境及其繁琐的配置工作。高通平台分为仿真和实际的arm编译两个截然不同的编译环境,其中有些相同有些不兼容。

a) 仿真编译

仿真完全使用vc6的编译机制,高通的例子是把一个个小applet编译成为dll动态库,我们的工程应该是把brew的模拟器作为静态库链接到meta的工程里面,模拟器的配置在win32/bin/brew_emu.dat中,在其中可以指定外观即devicepack配置文件,以及applet运行的文件系统路径,及其相应的mif文件所在路径。 PhoneFile=H:\CDMA\L200\Win32\DevicePack1\L200\DevicePack1.dpk

AppletDir=H:\CDMA\L600-new\Resource\FileSystem

MIFDir=H:\CDMA\L600-new\Resource\FileSystem

Vc6能够自动解析源文件的相应的依赖关系省却了程序员写makefile的痛苦,但是如何创建vc6的工程文件是一个很繁琐的工作。

美拓的代码统统使用cfg文件管理机制可以很方便地依靠gendsp.cmd工具加入工程组,也就是说每个源代码模块都定义了一个.cfg文件,里面列明了模块的头文件.h,源文件.c/.cpp,以及链接库.lib/.a。

[IncludeFiles]

$(METAMMI)/apps/Java/jblendia_jvm/include/settings/Meta_Java_Settings.h

[SourceFiles]

$(METAMMI)/apps/Java/jblendia_jvm/port/amsui/Meta_AmsUI.c

[LinkFiles]

!ifeq ($(META_TARGET),Arm)

$(METAMMI)/3Parts/JBlend/library/ajsc/ajsc_arm_ads.a

!else

$(METAMMI)/3Parts/JBlend/library/ajsc/ajsc_win32_msvc.lib

以上就是.cfg文件的格式,于是,运行win32/build目录下的gendsp.cmd就会运行一个perl的脚本gencfgmin.pl来读取metammi.cfg文件,这个文件列表了所有工程要包含的meta的模块cfg文件,然后通过这些cfg文件列明的.h,.c,.lib文件就被加入了工程,也就是加入了simulator.dsp的vc6的工程文件。

但是代码里面的include头文件是一个很麻烦的事情,首先,有些工程人员喜欢把其中的头文件名前面加上相对的路径,但是这个对于编译确实很麻烦的事情,因为编译器寻找这些路径是从一个运行的相对路径开始,然后按照编译设定的搜索路径和系统环境变量比如/include项下的路径搜索,一旦工程文件位置变动就有可能搜索不到,同时过多的设定搜索路径会减慢编译速度,因为编译器会不停在各个路径下搜索,而在系统环境变量中设置弊端更多因为有可能把不同项目的同名头文件包含进来,这才是程序员的噩梦。因此

美拓的做法是include统统使用双引号并且不带路径,并在遍历.cfg的时候把这些相应的头文件都拷贝到win32/build/include目录下,所以,你在vc6的工程里面看到的头文件在编译器看来却是在使用另外一个在build/include目录下的同名头文件,你要改动必须修改工程原路径下的重新编译才能更新拷贝,否则修改build/include下的拷贝都被覆盖掉了。

cfg机制对于非高通的meta的代码很方便,但是高通是采用一个min文件的方式来建立makefile的,添加到vc6工程就没有那么方便了,幸好不是很经常修改高通代码,因此,我们可以在simulator.dsp的源头template.dsp里面修改,就是把需要应用的高通的.c文件加到template.dsp文件里面,运行gendsp.cmd就更新了simlator.dsp。但是头文件是不能拷贝进来的,只能使用vc6的头文件搜索路径添加:ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\..\binstore"

这里的/I就是搜索头文件的路径,头文件的名字自然是在代码里面的了。

链接的链接库也是类似的使用# ADD LINK32 libjpeg_win32_msvc.lib /LIBPATH:"..\..\Source\Meta\3Parts\JBlend\library\libjpeg\lib"

其中的/LIBPATH就是指示编译器搜索库的路径。

因此,如果要添加一个新的代码文件模块或者链接库,使用高通的方式很麻繁,不如使用meta的办法在cfg里面定义,同时这个方式是仿真和arm通用的,不需要再手动修改template.dsp。 (即便是高通的代码我们也可以使用cfg方式,只不过高通自己有一套编译顺序我们不应该干扰他。)

我们要添加定义的宏怎么办呢?原本meta的设计是在一系列的custXX.h文件里面作,这个cust头文件在source/qualcomm/build/ms目录下主要是定义了一系列的所谓feature的开关,其中的XX原本被设计成项目代号,这样就可以在不同项目打开不同的所谓feature开关编译,但是我对这个方法是有保留意见的,因为他的宏定义有一个小问题,他的所谓FEATURE_OFF/FEATURE_ON被定义为了0/1,在代码中的宏判断是#if(FEATURE_STATUSBAR_ONLYIDLE == FEATURE_OFF)可是使用宏最危险的就是它有可能因为编译顺序或遗漏include还没有定义,如果FEATURE_OFF还没有定义,默认也是0,所以这个宏就可能被误读。 还有一个仿真添加宏定义比如至关重要的META_EDIT宏的定义,这个也可以放在template.dsp里面# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "FEATURE_BREW_DOWNLOAD" 其中的/D就是宏定义的开关。

搜索template.dsp有如下

# Begin Custom Build - Checking all include files...

InputPath=.\res\Simulator.ico

"copy.log" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"

call ..\..\Source\Qualcomm\build\ms\ads12.bat make -f gendsp.mak copyincfile

# End Custom Build

这里就是以上提及的copy include代码到我们的build/include目录的地方,这是利用了vc6的custom-build的机制,在编译前调用perl脚本。

这里要顺便提一下ads12.bat的重要性,因为高通的编译机制大量地使用perl脚本因此如何初始化perl的运行环境至关重要,因此在所有调用perl脚本前都要先调用这个批命令。同时perl运行于linux/unix环境依靠cygwin才得以运行于windows环境,所以也需要初始化cygwin的环境变量,然而linux/window毕竟有很大区别,比如文件系统就还是有区别,如果你在运行arm编译看到类似于什么as.exe不能正确执行的错误即便你的ads12.bat已经正确运行了很有可能是因为你没有把as.exe的文件属性加上system,比如attrib +s as.exe

b) arm编译

arm编译和仿真编译有着巨大的差别,要复杂的多。首先要从编译资源做起。资源包括图片,文字存放在resource/meta下的image,string,theme目录下,给每个资源编写资源id是一件很繁琐的事情,这里也是依靠了工具运行ImageResConv.bat把image下的所有图片都编篡相应的资源id,比如一个图片文件input_123.png最后就在MetaImg.brh里面变成了

#define IDI_PNG_INPUT_123 8145

命名的原则就是IDI_TYPE_FILENAME,其中type应该是文件类型即扩展名,8145是程序顺序产生无关紧要。这样程序员在代码中按照这个命名原则就省却了更新命名资源id的烦恼了。这个metaImg.brh最终被放在source/meta/res/project目录下,其中project未当前的项目代号,这个必须要在build_res.bat里面设定了比如 @set PROJECT=0803_L201。最终这个metaimg.brh成为metammi.cfg的一栏。文字和图片稍有不同,就是把中文字串资源“T_Yes 是”和英文资源“T_Yes Yes”进行统一编号为metaStr.brh里的“#define T_Yes 3”而相应的实际资源文件则是#define CHINESE_RES_FILE "Chinese.bar"变成了高通的资源文件。

这一切的编译工作都是在一个resource/meta/Build_Res.bat下完成的。

需要提醒注意的是,image路径下任何文件都回被搜罗当作资源图片文件,因此如果使用svn需要删除svn文件或者把图片拷贝出来编译。

编译完资源后就可以开始编译了,但是首先要做的还是设置好ads12.bat,这个的重要性前面已经讲过了,每个人都有可能不同要根据自己的安装路径来设置。运行arm/prj_code.bat其中prj_code代表当前的项目代号,因此相应的在/source/qualcomm/build/ms目录下你要有相同项目代号的prj_code.cmd, prj_code.mak,cust_prj_code.h,同时在/source/meta/res/prj_code/下要有相应的资源文件以便链接。正是编译同样使用meta的收集cfg的方式生成实际的makefile,高通的编译器是支持c++编译的,比如tcpp.exe就是c++编译器,在dmss_rules.min里面有perl脚本根据代码文件扩展名自动选择编译器的规则,因此你也完全可以写c++代码只要你使用.cpp文件扩展名。

高通使用min文件来定义每个小模块包含的源文件,其中还包含了.s文件,这个是一个对大多数pc程序员的巨大的surprise,这里要从程序运行谈起。因为pc的程序的运行地址一般都是relocable的,也就是说运行起在哪里是不需要关心的,是由操作系统的loader加载同时修改代码的偏移地址完成在任意物理地址运行的,但是手机程序的静态加载模块地址却是定死的,当然各个模块的地址可以依靠.scl的文件来调整,这个是linker的一个配置文件,它规定了各个模块的boot内存地址的相对关系,比如以/source/qualcomm/build/ms/q60x0a_rom.scl为例,

BB_RAM +0x0

{

dloadarm.o (+RW)

dloadusb.o (+RW)

}

/////////////////////////////////////////////

// added by nick for jvm

//Add liaohs.amoi.com.cn for JAVA

JBLEND_RAM +0x0

{

jblend_*.o (+RW)

}

//End liaohs for JAVA

// added by nick for jvm

/////////////////////////////////////////////

这里实际上规定了dloadarm.o和dloadusb.o的模块的相对位置,同时也隐含着输出了一个地址symbol BB_RAM,同样的java虚拟机的起始地址JBLEND_RAM会在回编码里编成两个symbol: Image$$JBLEND_RAM$$Base和 Image$$JBLEND_RAM$$Length,注意这里使用的$$符号是arm编译器内部使用的,和普通的c程序变量名不兼容,为了能够让c程序在链接时候引用这两个地址相关的变量名,需要一个所谓的.s文件来转换。在source/qualcomm/driver/boot目录下的bootmem.c里面可以使用extern来声明

extern byte *Image__ JBLEND_RAM __Base;

extern byte *Image__ JBLEND_RAM __Length;

在boot_data.s文件里面把这些变量名从汇编码的”$“形式转换为”_”:

IMPORT |Image$$JBLEND_RAM$$Base|

IMPORT |Image$$JBLEND_RAM$$Length|

EXPORT Load__JBLEND_RAM__Base

EXPORT Image__JBLEND_RAM__Length

他们的定义是这样子的:

Image__JBLEND_RAM__Base

Image__JBLEND_RAM__Length

而这个所谓的.s文件是在相应的boot.min文件里包含了。这种机制的核心就是为了能够让java虚拟机在起始运行阶段记录相应的内存地址,而这一切都是由于静态加载的地址在编译器就决定了。

b) 关于组键编程

高通的组键模型编程基本上和微软的COM机制类似,只不过搞通为了方便嵌入式程序员大多不熟悉c++语法以及某些效率的考虑才使用纯c语法模拟实现,但是微软的COM机制博大精深,高通仅仅借用了其中内存资源释放管理与组建创建的很小一部分。

任何一个组键一定要实现最基本的三个接口方法,即AddRef,Release,QueryInterface,这三个方法看似简单,但是meta的代码的写法似乎都有不妥之处。

第一, 这三个方法都应该是完全对外调用的不应该随随便便当作内部方法来使用,比如有的人在组件的

constructor,也就是所谓component_new方法里面不设定nRef计数为1,却调用QueryInterface来

增加引用计数,虽然效果看上去一样,实际却是有令人混淆的嫌疑,我以为在constructor里面直

接设定引用计数为1是最自然的,因为逻辑上一个组件的创建者也就是它的拥有者当然应该增加

计数为1。

第二, queryInterface另一个被滥用的例子是meta的程序组件不论classid为何都一律最后返回SUCCESS,

这个从原理上肯定是不对的,原本一个组件有可能支持多个接口方法,取决于传进来的classid是

有可能不支持而返回失败的。这些都为将来的可能的扩展埋下了隐患。

DCD |Image$$JBLEND_RAM$$Length| DCD |Image$$JBLEND_RAM$$Base|

高通的组件机制提供的内存资源管理还是一种非强制的模式,也就时说这个机制需要程序员自觉遵守,一旦有人违反了这个规则那么资源就不能被自动回收,比如,你在调用了queryinterface或者通过createinstance获得了这个接口指针,但是忘记调用release,或者别的模块传递给你一个参数你调用了addref却没有在结束调用release,那么这个资源就再也不会被释放。

有鉴于此,高通还有一套类似于java的垃圾回收机制强制资源的回收,这个应该是在IModule里面的一个实现,也就是说在一个module创建的时候就建立了某种上下文(AEEAppContext,按照高通的代码说明就是所谓的一个不透明的32位长整数,我们可以想象很可能就是module的起始地址,当然这个纯属猜测无关紧要。)于是在这个所谓上下文中所有调用内存分配函数malloc都会在内存的起始处被加上这么一个上下文标志,于是在这个module结束的时候,brew会强制把所有在heap里有这个标志的内存回收,这个就是所谓的垃圾回收机制。这个本来是好事可以让程序员不用操心资源回收有缺漏,比如象上边提到的组建的引用计数错误,忘记release的情况,但是对于一些跨模块使用的资源却有可能是一个灾难。

案例1:一个组件作为参数从模块A传递到了模块B,模块B的程序也正确的addref表明他还要继续使用这个对象。但是当模块A结束的时候,在组建的destructor里面的release方法检查到了引用计数不为0也正确的不做释放内存的调用,看上去挺好,但是brew的垃圾回收机制却是无条件执行的,他不管三七二十一会把所有模块A上下文的内存统统释放。于是模块B虽然进行了正确的addref以为组件还存在继续调用结果访问的却是错误的内容,因为很有可能brew把回收的内存已经分配给了新生成的组件或者模块。 针对这个情形要求程序员对于可能跨模块使用的对象的内存分配使用所谓的系统上下文,也就是调用AEEEnterAppContext的时候使用NULL参数,然后再分配内存,随后在AeeLeaveAppContext,这个方法要保证的一个前提是该组件在所有的内存分配时候都要采用这种系统上下文。一旦在初始化之外也有分配内存的做法或者使用别的函数分配的内存也还有可能有问题。比如笔者在使用sqlite的组件中的一个所谓DBManager接口时就遇到这个问题,当时考虑把这个manager接口做成singleton以便节省资源,这就要求这个对象独立于创建的模块始终存在于系统中,于是在他的初始化在系统上下文内进行,但是没有想到随后每次当创建的模块关闭后再打开新的模块调用这个manager接口都会发生访问错误,后来分析高通很有可能为了加快初始化manager的进度不把所有的内存分配一次性地在初始化做完,而是在实际产生新的database实例的时候分配了一些内存,而这个时候没有在系统上下文处理,属于当前的模块上下文,于是当前模块解体其中的内存自然变成非法了。

案例2:一个组件注册了一个timer的回调事件,但随后就再回调发生之前结束运行被释放了。

Timer的event注册要两个参数都匹配才行,也就是说一个是回调函数指针还有一个是用户自定义的数据指针,所以你要canceltimerevent的时候必须把两个参数都传递正确才行,笔者对于这一点已开始比较吃惊,因为这个和PC上的一些习惯不一致。后来注意到了高通的说明就是你可以把回调函数指针传递null,这个CancelTimerEvent就把所有绑定这个数据结构的event都取消了,所以,鉴于此,我觉得不如自行规定所有的setTimerEvent都把当前的组件指针作为用户数据传递,因为所有的数据都是定义在实现结构里面的,那么在组建的destructor里面无条件调用AEECancelTimerEvent(NULL,pThis)确保取消一切的事件。

案例3:笔者在撰写sqlite的组件时候,需求要求用户可以注册一个eventNotifier以便通知数据库数据的变化,这个要求关心者注册一个回调函数以便在数据表数据变化的时候回调来处理。因为数据库组件很有可能是跨模块使用,比如媒体播放其播放的mp3列表在被资源浏览器删除了文件之后得到通知以便修订播放列表。但是在这个回调函数里面笔者已开始并没有意识到也需要切换到不同的模块上下文,这因为当初设计回调通知机制的时候要求注册信息包括三个要素:感兴趣者的模块上下文,感兴趣的数据表,以及使用的数据库接口指针。那么在数据库组件注意到数据变化调用通知的回调函数的时候是在谁的上下文呢?这里我们没有使用异步通知,而是所谓的同步通知就是说数据已修改立刻通知,因此现在的代码是在数据修

改者的模块上下文,他的通知函数里面的参数调用的数据却很有可能是被通知者的上下文产生的,一旦回调函数有分配内存的动作并且保存到了被通知模块内,就意味着通知者上下文下的内存被保存在了通知者的模块中,将来程序crash也就不足为奇了。

组件的创建以来与所谓的createInstance机制,在windows,这个是紧密以来与GUID和windows的注册表来共同实现的,GUID保证了组建的唯一性,注册表则记录了组件的相关信息帮助系统实例化组件。高通的机制与此类似,使用了一个所谓的

以组件模型开发的一个重要原因是仰仗于c++在代码实现上的继承性,而纯c语法仅仅能够模仿c++的某些语法却不能够完全替代。而高通在这个方面也做得不是很严格,但最起码还是有遵守基本的原则,不过amoi的程序员却不是很有意识这样做,以UTKForm与由此继承而来的UTKListForm为例,后者从前者继承而来,大部分的方法都是一样的,可是后者不仅仅是把这些臃瘀的代码拷贝了一遍,而且增加了今后扩展的难度。比如,笔者在设计一个对于所有form都通用的特效方法时候需要找到一个所有form的共性,即在其处于topvisible的时候进行某种特效处理,假如所有的form对于通用的方法比如show,没有特别的实现都调用自己继承而来的实现(比如都应该调用IForm的show方法,因为并没有什么特别的做法,都不过时拷贝了高通的实现),那么可以很容易的在IForm一级做一个处理,可是由于很多从UtkForm“继承”而来的各式各样的form有都把相同的方法的相同的实现都拷贝了一遍,变成我需要把我的修改再一级级的修改。

  • 1
  • 2
  • 全文阅读
更多相关推荐:
物理实验总结与体会

物理实验的总结与体会——090601145——邢春雷摘要:做了两个学期的物理实验,是我对物理这一门学科有了更深层次的理解,在中学的时候,什么什么实验的都只有文字说明,没有自己亲自做过,正所谓实践出真知,物理实验…

大学物理实验的心得与体会

物理实验心得及对微小量测量方法的探究大学物理实验课程结束了,在这门课程结束的同时,我发现自己学到的不仅仅一门实验课程,还收获了许多与该课程有关的知识以及学习方法。在一次又一次的实验过程中,我深切体会到这门课程开…

实验心得体会_个人总结

一在做XXX的实验前我以为不会难做就像以前做物理实验一样做完实验然后两下子就将实验报告做完直到做完测试实验时我才知道其实并不容易做但学到的知识与难度成正比使我受益匪浅在做实验前一定要将课本上的知识吃透因为这是做...

毕业体会与感受

毕业体会与感受年华匆匆似水而过四年光阴转眼即逝面对毕业带着跃跃欲试的激情和惴惴不安的期待以及对未来的些许惶恐终于走到了人生的又一个转折点回顾四年大学生活从开始的新鲜兴奋到中间的颓废生活又到突然的觉醒再到最后的来...

毕业设计心得体会与收获

毕业设计心得体会与收获gt毕业设计gt心得体会与收获一随着毕业日子的到来毕业设计也接近了尾声经过几周的奋战我的毕业设计终于完成了在没有做毕业设计以前觉得毕业设计只是对这几年来所学知识的单纯总结但是通过这次做毕业...

数电实训心得与体会

心得与体会实践是检验真理的唯一标准第十五周的数字电子技术实训周已落下帷幕在这一周里虽然真正在实验室里做实验的时间只有短短的两三天但给我的感触却是很深刻当然对于喜欢动手实际操作的我来说这两三天真的很短暂但也很宝贵...

新教师岗前培训心得体会及实习工作总结

新教师岗前培训心得体会及实习工作总结20xx年x月,我们县教育局县委托重点中学澄迈第一中学为我们这批即将上岗的新老师安排了为期十二天的岗前培训。在为期十二天的培训里,学校为我们这些学员安排了新老师必备技能课程,…

个人工作体会与感悟

个人工作体会与感悟看着日历又掀过一页,感叹时间过的真快,这一个月又在手指间不经意的溜走了,回顾过去,展望未来。积极参与部里的活动,更努力的工作。3月这个朝气蓬勃的日子里,我充满活力,有激情,有动力,参与了部里的…

关于文秘工作的感悟与体会

文秘工作总结文秘工作,最需默默无闻、埋头苦干,所谓“孜孜不倦抓学习、兢兢业业干工作、勤勤恳恳搞服务、任劳任怨做奉献”及“三平精神”(平凡之中的不懈追求、平静之中的满腔热情、平常之中的强烈责任)等,所以,其工作常…

班长工作心得与体会

班长工作心得与体会时间飞快,转眼间我在班长的位子上干了快五个月了,在这段时间里我对班长的职责有了更加深刻的理解和体会。作为班长,我的工作是统筹并协调各班委管理好班级日常事务,协同同学们建设好我们的班集体,并努力…

新员工工作总结与心得

新员工工作总结与心得时间一晃而过,转眼间半年的试用期已接近尾声。这是我人生中弥足珍贵的经历,也给我留下了精彩而美好的回忆,在这半年的时间里,我在联社领导、排沙信用社领导及同事们的关心与帮助下,从一开始的手忙脚乱…

20xx年度工作总结与体会

20xx年系统项目部工作总结与体会xx公司系统项目部xxx本人从20xx年x月初到xxxxxxxxxxxxxxxxxx公司任职以来,经历了xx公司从筹建、资料的制作与完善、公司的正式运营与发展等阶段,可以说这是…

总结与体会(87篇)