Posted: May 16, 2009 at 12:13 am | Tags: bug, mtk, trace
前几天去一个公司帮他们解决BUG。BUG的描述是这样的,在使用在线QQ时,如果来电话,就会重启。没有发现ASSERT信息,只有stack dump信息。起初听他们描述,感觉像是QQ或者通话的问题。抓了TRACE之后,发现是MED模块的问题,由于MED主要是一些媒体文件的解码。由于观察现象时发现,通话时,还没有来得及响铃,就开始重启,因此可以大概推知是来电振铃出了问题,具体出在什么地方,需要查找TRACE信息。从别人那里获取的TRACE信息如下:
Trace 1745424 150706 MOD_NIL TRACE_ERROR [1] fatal error (4): Data_abort – MED
Trace 1745424 150706 MOD_NIL TRACE_ERROR Exception type: data abort
Trace 1745424 150706 MOD_NIL TRACE_ERROR software version:
E500_A.1.4
Trace 1745424 150706 MOD_NIL TRACE_ERROR boot mode: normal mode
Trace 1745424 150706 MOD_NIL TRACE_ERROR rtc sec = 16, rtc min = 33, rtc hour = 1
Trace 1745424 150706 MOD_NIL TRACE_ERROR rtc day = 1, rtc mon = 1, rtc wday = 1, rtc year = 9
Trace 1745424 150706 MOD_NIL TRACE_ERROR execution unit: MEDMED
Trace 1745424 150706 MOD_NIL TRACE_ERROR status: 0×00000000
Trace 1745424 150706 MOD_NIL TRACE_ERROR stack pointer: 0×00169380
Trace 1745424 150706 MOD_NIL TRACE_ERROR stack dump:
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0xA0001BED
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x08480FD5
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x0847CE59
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x084DE0EF
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x0866D4D3
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x0845E407
Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x0866D4A1
Trace 1745424 150706 MOD_NIL TRACE_ERROR number of messages in the external queue: 0
Trace 1745424 150706 MOD_NIL TRACE_ERROR messages in the external queue:
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR MSG_ID_INVALID_TYPE
观察TRACE,结合BUG描述,可以大致推知是MED的振铃问题,具体位置需要看catcher打印的栈信息。有些人遇到这个信息,就蒙了,感觉无从下手,我最早学习MTK时,也是无从下手,总觉得这样的信息没有ASSERT直观,一看就知道哪个文件哪一行出错。但ASSERT并不总是金牌,有一次遇到一个MEMCPY的ASSERT,调用的地方太多,照样需要查栈信息来寻找问题。当然写的代码,尽量在一些关键的地方加上ASSET,这会让人查找问题更轻松一些。当然有了这个信息,我们也可以大概推测出BUG的地方和原因的。要查找这个error的信息,必须要找到该软件版本对应的sym文件。该 sym和BIN文件同步生成,都位于BUILD文件下。该文件储存了ARM编绎器为软件中所有函数生成的地址。而出错时,MTK会保存当时栈中最近执行的 10条指令的地址,我们通过对这些地址的研究,就可以找到出错的地方。由于sym文件只保存了函数的地址,函数体里执行的代码地址没有列出,而一般打出的错误地址不会正好对应某个函数的地址,一般会会偏移N个单位。当然也有例外,我曾遇到一次出错,有一个地址正好和某函数地址对应。对于出错信息的地址与函数地址不对应的情况,我推测应该是函数体里的指令出错,所以应查找比该出错指令小且最近的函数。比如上例行 “Trace 1745424 150706 MOD_NIL TRACE_ERROR 0x085569C9”,我们在sym文件中不应该查找0x085569C9对应的指令,因为一般会一个也找不到,我们要查找x085569C或者 x085569对就的函数,这时大约可以查到许多函数,找到与该出错地址最近且比该地址小的地址对应的函数,十个地址大约可以找到十个左右的函数,通过仔细排查,确认以下函数:
med_int_left_size
med_int_sfreeaud_main
aud_tts_play_req_hdlr
med_int_smalloc
med_set_ext_memory_pool
TCC_Task_Shell
如此可以大致确定问题出处和原因了,请教项目负责人,知道aud_tts_play_req_hdlr
是语音王的播放函数,在呼入时,有来电报号,该功能和QQ都是从MED栈上动态分配内存,由于QQ占用了一部内存,有呼入电话时,语音王分配内存失败,导到语音解码故障。问题一下解决。
Posted: May 16, 2009 at 12:10 am | Tags: lbs, mtk, 基站
我们的移动网络是通过一个个基站连接起来的,而一个基站又被划分成若干小区以方便查找。因为基站的位置相对比较固定,所以我们可以通过基站的编号cell_id和小区的编号LAC来定位地理位置。
具体的理论原理大概是这样的。根据GSM协议通信管理过程,我们大致可以知道呼叫建立的过程首先是用户定位,查找被呼叫人的位置信息。在GSM协议中,我们知道,用户的位置信息由MSC/VLR管理,MSC/VLR被划分成若干个较小的区域。假设定义被叫人的区域为LA,则每一个LA由一个位置区标识(LAI)识别,它们结构如下:
LAI=MCC+MNC+LAC
·MCC=移动国家代码(被访问国家)
·MNC=移动网代码(服务的PLMN)
·LAC=位置区代码
比如中国的MCC:460(注意不是+86,+86是中国区号),移动的MNC:00,联通的MNC:01。LA的数据可以从VLR,下面让我们看看VLR中的数据。
VLR IMSI: 460 00 1234567890
LAC: 262 15 0987
Data: abc..
MCRN: 358 50 456456
IMSI是全世界唯一的,可以唯一标志一张SIM卡,通俗一些说,就是SIM的身份证,所以IMSI号可以用来设计防盗追踪或者检查SIM是否被更换。其结构如下:
IMSI=MCC+MNC+MSIN
·MCC=移动网国家代码(三位)
·MNC=移动网代码(两位)
·MSIN=移动用户识别码(十位)
不过与本文无关,做不做过多介绍。
既然通信网络可以通过LAI 查找到被叫者,我们应该也可以使用LAI来确定被叫者的位置,既然LAI可以确定被叫者位置,当然也可以确定我们自己的位置,只不过这个位置信息不是特别的精确,与当地基站分布的密度有关。当然在这个结构中,国家代码和网络代码只能确定国家和使用的网络,我们能够使用的应该是LAC,LAC大概由基站信息和小区编号组成。
其具体算法就是首先获取本机所处位置的LAI数据,然后建立一张查找表,表中用基站编号和小区编号对应一个地理位置。通过查表就可以知道自己的地理位置。
读MTK代码时发现一个形如下的结构:
typedef struct {
kal_uint8 mcc1; /* MCC DIGIT1*/
kal_uint8 mcc2; /* MCC DIGIT2*/
kal_uint8 mcc3; /* MCC DIGIT3*/
kal_uint8 mnc1; /* MNC DIGIT1*/
kal_uint8 mnc2; /* MNC DIGIT2*/
kal_uint8 mnc3; /* MNC DIGIT3 Always 1111*/
kal_uint8 la_code[2];
kal_uint8 ra_code; /* RA CODE*/
kal_uint16 cell_id; /* CELL ID*/
} cell_info_struct;
该结构描述了基站的信息,恰好与GSM协议相对应,该结构可以通过发消息获。网上也能找来基站与地理位置的数据信息,看来实现起来应该无啥难度。该功能在没有GPS的情况下实现粗略定位,相较GPS可以几乎无成本的使用,对于喜欢猎奇又不愿意花钱的朋友,无异是一个不错的选择。但由于网上流行的基站数据并不完整,对于基站和地理位置的对应,如果能建立一个共享的WAP网站,发动大众的力量实现动态更新升级,那可就是利国利民,造福世人的好事。不过刚刚在网上看贴子听说,基站与地理位置对应的信息属于国家机密,不知是真是假,要是那样的话,我们也就只能自己做了玩玩,却是不能推广的。把自己推广到监狱里了,那可就得不偿失了,玩的朋友们注意了。
Posted: December 29, 2008 at 6:11 pm | Tags: mtk
本文来自:我爱研发网(52RD.com) 详细出处:http://www.52rd.com/S_TXT/2008_12/TXT13397.HTM
:(
52RD研发网】联发科(MTK)受到第4季度营收目标由原先下滑9~16%,修正到衰退30~33%影响,公司目前存在库存偏高压力,对此对此联发科官方表示,第4季度度将在库存跌价损失提留上,采取较积极作法,配合10号公报实施,将提列长期投资损失,预期这样动作将会让联发科本季度业外亏损扩大到新台币10亿~15亿元水平,较以往单极度不到5亿元水平大幅增加。
联发科预估将先提列长期投资损失约3亿元,例如对络达等其它投资公司的损失,加上其亦有雷曼兄弟连动债等部位,将额外提列投资损失约3亿元,至于最重要的库存跌价损失,因为第4季度营收目标下修幅度颇大,加上展望未来景气不是很乐观,因此,联发科已决定采取较积极提列方式,预期库存跌价损失将会较过去每季度平均3亿~4亿元水平增加数倍。
联发科过去提列库存跌价损失方式,是采用一般标准会计原则,若客户超过120天没有提货,则提列100%库存跌价损失,若90天没有提货者,则采取另外比例来提列损失。由于联发科第4季度营收最后数字,比原先预期差距颇大,因此,当季度库存跌价损失就会相对来得较大。另外,联发科为因应未来景气,第4季度会再多提留一点,造成库存跌价损失金额再加大。
根据联发科最新财测目标,单季度营收将介在190亿~195亿元间,在公司结算10、11月营收为153.87亿元后,联发科12月营收将约38亿~42亿元水平,虽然近期传出因大陆客户出现农历年前的提前拉货动作,可望让联发科12月营收数字较预期40亿元好一些,但初估单月营收仍无法突破50亿元大关。
此外,由于2009年第1季度景气看来仍是持续低迷,加上单季度工作天数原本就较少,2009年第1季度淡季度效应可能会出现加倍效果,迫使联发科下季度营收可能还较本季度下滑30%以上。至于本季度因联发科受到业外亏损将较以往大出许多的影响,单季度EPS可能会较第3季度6.5元水平下滑逾半,2009年第1季度EPS亦可能进一步再下滑,联发科将面临史上最严厉获利寒冬期。
Posted: November 28, 2008 at 10:14 am | Tags: arm, mtk, ro, rw, zi
一直以来对于ARM体系中所描述的RO,RW和ZI数据存在似是而非的理解,这段时间对其仔细了解了一番,发现了一些规律,理解了一些以前书本上有的但是不理解的东西,我想应该有不少人也有和我同样的困惑,因此将我的一些关于RO,RW和ZI的理解写出来,希望能对大家有所帮助。
要了解RO,RW和ZI需要首先了解以下知识:
ARM程序的组成
此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。
一个ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量
RW是程序中的已初始化变量
ZI是程序中的未初始化的变量
由以上3点说明可以理解为:
RO就是readonly,
RW就是read/write,
ZI就是zero
ARM映像文件的组成
所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。
Image文件包含了RO和RW数据。
之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。
Q:为什么Image中必须包含RO和RW?
A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。
ARM程序的执行过程
从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。
实际上,RO中的指令至少应该有这样的功能:
1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中
在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。
说了上面的可能还是有些迷糊,RO,RW和ZI到底是什么,下面我将给出几个例子,最直观的来说明RO,RW,ZI在C中是什么意思。
1; RO
看下面两段程序,他们之间差了一条语句,这条语句就是声明一个字符常量。因此按照我们之前说的,他们之间应该只会在RO数据中相差一个字节(字符常量为1字节)。
Prog1:
#include <stdio.h>
void main(void)
{
;
}
Prog2:
#include <stdio.h>
const char a = 5;
void main(void)
{
;
}
Prog1编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog2编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 61 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
以上两个程序编译出来后的信息可以看出:
Prog1和Prog2的RO包含了Code和RO Data两类数据。他们的唯一区别就是Prog2的RO Data比Prog1多了1个字节。这正和之前的推测一致。
如果增加的是一条指令而不是一个常量,则结果应该是Code数据大小有差别。
2; RW
同样再看两个程序,他们之间只相差一个“已初始化的变量”,按照之前所讲的,已初始化的变量应该是算在RW中的,所以两个程序之间应该是RW大小有区别。
Prog3:
#include <stdio.h>
void main(void)
{
;
}
Prog4:
#include <stdio.h>
char a = 5;
void main(void)
{
;
}
Prog3编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 1 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
可以看出Prog3和Prog4之间确实只有RW Data之间相差了1个字节,这个字节正是被初始化过的一个字符型变量“a”所引起的。
3; ZI
再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。
Prog3:
#include <stdio.h>
void main(void)
{
;
}
Prog4:
#include <stdio.h>
char a;
void main(void)
{
;
}
Prog3编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4编译出来后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 97 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
编译的结果完全符合推测,只有ZI数据相差了1个字节。这个字节正是未初始化的一个字符型变量“a”所引起的。
注意:如果一个变量被初始化为0,则该变量的处理方法与未初始化华变量一样放在ZI区域。
即:ARM C程序中,所有的未初始化变量都会被自动初始化为0。
总结:
1; C中的指令以及常量被编译后是RO类型数据。
2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
3; C中的已被初始化成非0值的变量编译后市RW类型数据。
附:
程序的编译命令(假定C程序名为tst.c):
armcc -c -o tst.o tst.c
armlink -noremove -elf -nodebug -info totals -info sizes -map -list aa.map -o tst.elf tst.o
编译后的信息就在aa.map文件中。
ROM主要指:NAND Flash,Nor Flash
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
Posted: October 21, 2008 at 9:46 am | Tags: L6218E, mtk, 编译
昨天遇到一个十分麻烦的问题,我把MTK编译时遇到这样的错误:
Error : L6218E: Undefined symbol Image$$ZI$$Limit (referred from sys_stackheap.o).
Not enough information to produce a SYMDEFs file.
而没有这三个:
Not enough information to list image symbols.
Not enough information to list the image map.
Not enough information to list the image sizes and/or totals.
网上只有两种解释,是这样:
1. reimplement __user_initial_stackheap()
解决办法大概意思是重新装配__user_initial_stackheap()函数。
2.分配内存的时候,要分配内存的结构中使用了ARM不支持的数据类型.通常定义了结构体的指针,然后用
malloc分配空间时为结构体类型指针,而ARM不支持这种数据类型,所以会有这种错误。解决办法:用
typedef预定义这个结构类型,使得编译器识别这种类型。
第一种方法离我太远,应该不会涉及到;
第二种方法试过,没用,但我也大概知道方向是malloc函数的问题。
今天终于解决:
这个问题是由于代码或者Lib中调用了 C Lib的malloc或者类似于strdup,printf 这样的会调用malloc的
C Lib function 引起的。MTK Platform不支持 C lib的malloc,而用 Ctrl Buffer机制代替了malloc,
以便于调试memory leak问题。MTK中的Osl层有专门处理内存的函数,于是我想,我用malloc是跳过Osl层
直接分配内存,这样没有经过系统处理,危险性大。所以我用系统自带的OslMalloc和OslMfree来处理内
存空间,问题解决。
Posted: October 15, 2008 at 1:36 pm | Tags: mtk, 山寨, 秘诀, 蔡明介
“常常有人问我,联发科在过去10年里迅速崛起,有什么秘诀?并且希望这些’秘诀’对正在快速发展中的中国大陆芯片企业有所启发.”给人印象深刻的一副标志性的大眼镜,从外表看,联发科技董事长蔡明介并不具备在商场上拼杀的枭雄造型.然而,他却是台湾赴美引进半导体技术的先驱,被誉为台湾集成电路设计“教父”.

1995年蔡明介领导台湾“联华电子”的集成电路设计部门独立创业,成立了“联发科技”的前 身多媒体小组,联华电子是仅次于台积电的全球第二大流片厂。IC行业流传着一句难以溯源的话———现在的芯片业,每个非大厂都不希望联发科涉及自己的领 域,那样自己就“离死不远了”。
并非危言耸听,经历10余年的发展,目前联发科已成为世界顶尖的IC专业设计公司,位居全球 消费性IC片组的领航地位。产品领域覆盖数码消费、数字电视、光储存、无线通讯等多个系列,是亚洲唯一连续六年蝉联全球前十大IC设计公司唯一的华人企 业,被美国《福布斯》杂志评为“亚洲企业50强”。
没有听说过联发科,只要是对手机产业链稍有了解的人,也会对MTK这三个字母的组合有些印象。MTK是联发科技股份有限公司的英文简称,英文全称叫MediaTek.
「杀手锏」一站式方案
或许有人不太喜欢这家公司,因为从早年的黑手机到现在的山寨机,草莽气息背后是MTK方案被广泛应用。波导、夏新,老牌手机厂商令人堪忧的一份份财务报告中总会附带“受到黑手机、山寨机冲击”的说明,MTK可被视作始作俑者之一。
但你不得不佩服MTK的创新和成功,甚至也找不出更多口诛笔伐的理由。因为MTK的存在,近年来国内手机产业进入了前所未有的蓬勃发展阶段。事实上MTK除了为山寨机提供方案,也同时为正规厂商提供全面解决方案,包括近两年迅速崛起的天语、金立等正规品牌。
“我们有个客户的工程师曾经很形象地打了个比方,他说MTK的解决方案更像是一本”烹饪手册 “,它只能保证厨师不会把鱼香肉丝做成京酱肉丝,至于这道菜做出来的色香味如何,那还是看终端厂商的水准。”联发科首席财务官兼新闻发言人喻铭铎曾向本报 记者表示,MTK提供的是一个把厂商开发过程中可能遇到的问题尽量提前考虑到的参考性的解决方案。厂商在开发过程中如果遇到一些原先技术能力较为薄弱的地 方,可以从这个参考性的解决方案中获得借鉴与补充,从而不断弥补原来的不足。
凭借这种“菜谱”方式,MTK在大陆市场上结结实实地击败了众多国际芯片厂商。操作软件,甚至液晶、摄像头等部件,MTK都可以“一站解决”,大大缩短了手机的生产环节并降低了研发成本。这正是Turnkeysolution(一站式方案)模式。
Turnkeysolution正是MTK手机平台的核心。在这套解决方案中,将手机芯片和 手机软件平台预先整合到一起。这种方案可以使终端厂商节约成本,加速产品上市周期。MTK公司的产品因为集成较多的多媒体功能和较低的价格在大陆手机公司 和手机设计公司得到广泛的应用。加上MTK的完工率较高,基本上在60%以上,这样手机厂商拿到手机平台基本上就是一个半成品,只要稍稍的加工就可上架出 货了。
数年前,德州仪器(TI)的LoCosto、OMAP平台,英飞凌的ULC解决方案,还一度是这些国际知名厂商广泛采用的模式。然而MTKTurnkey方案的巨大成功则证明了在这一领域平台技术不再是TI、英飞凌这些巨头的专属。
「性价比」到底到什么程度
1999年,当时的联发科在台湾科技行业中还仅是一家生产光存储和DVD芯片的二线厂商。然而当时,蔡明介看中了利润更加丰厚的手机芯片业务,恰逢中国手机市场进入第一次高速增长期,他的眼光自然而然地落在了中国大陆市场。
2001年开始,MTK开始在手机行业逐渐渗透Turnkeysolutio模式,这种模式内蕴着极高的性价比。在国内手机制造仍处于朦胧阶段的时刻,这种性价比着实让人难以抗拒。
号称2007年全年销量达到1500万台的天宇朗通便广泛采用了MTK的产品。“感谢 MTK,感谢台湾人。”天宇朗通销售总监卢伟冰在今年天津手机展新闻发布会上曾表示,中国拥有全球配套最完善的手机产业集群。都说手机没有核心技术,都说 在欧美人的手里,大家都觉得很失望。但是我们的台湾人做了很多基础芯片研究,MTK的问世把没有技术、没有解决方案这个历史进行了改写。
天宇朗通是近年来当之无愧的国产手机黑马。在其面向下游的销售模式中,和MTK的高性价比、 低价出牌也有着惊人的相似。本地一位零售商代表向记者表示,天宇朗通完全摒弃了过去手机企业的价保模式,取而代之的是以较低的价格出货给代理商,再由下游 定价。“一款产品我们的进货价若只有300元,定价600元销售也有可能,毕竟同等功能配置的洋品牌还要贵上至少100元。”
300元,只是手机厂商给予渠道的价格,他们从MTK手中获得的方案呢,性价比究竟能高到什 么程度?以行业内最普遍使用的联发科(MTK)低端方案6223为代表。可支持T-flash扩展卡,彩屏,具备自动应答留言机功能,键盘采用动感炫灯。 如量产二十万台,单价在14美金左右,不到人民币100元。
同样是在2001年,联发科在台湾上市,更素有“台湾股王”之称。直至2007年,其营业收 入已达到新台币804.09亿元,较2006年增长51%.根据最新市场资料显示,2007年联发科年手机晶片出货量高达1.5亿颗,全球市场占有率近 14%,仅次于德州仪器及高通。
「声誉」骂名、美名
联发科真正开始在大陆市场上崭露头角,2005年是关键年。在那个还没有取消手机核准制度的 年代,早前依靠贴牌韩国方案而迅速崛起的国产手机在洋品牌的全线反攻下,跌入了全线溃败的谷底。夏新、波导、TCL,大量的民族手机品牌惨遭洗牌,国产手 机也因此背上了“质量不好”的包袱。
然而就在这一时期,具有中国特色的黑手机跃上了手机产业的历史舞台。这些手机主要通过模仿知 名品牌的外形。以及低廉的价格来吸引消费者。黑手机一词诞生于联发科Turnkeysolutio模式广泛进入中国市场之前,但因为MTK的存在,黑手机 发生了“革命性”的变化。MTK也因此背上了“黑手机之父”的骂名。
早期的黑手机,在没有采用MTK方案之前规模并不大,以翻新、通过各种渠道寻找廉价方案为主 要表相。在MTK全面解决方案进入中国市场后,生产方一次过便可以获得包括基带芯片、操作软件,甚至液晶显示屏、摄像头等部件等全套或半套解决方案。一举 将原来的6-9个月甚至是一年的手机研发周期,缩短到了3个月以内。
直至国家取消手机牌照核准制度,黑手机这个称谓已不合时宜,源于MP3行业的“山寨”一词取而代之。不过MTK并未因此而被改称作“山寨机之父”。在国产手机产业发展到目前的规模,讨论MTK的成分变得丝毫没有意义。
可以说,黑手机、山寨机空前的泛滥与MTK有着必然联系,但无论从技术还是道德的范畴,如今 更为理性的观点大都认为不应该由MTK为这种泛滥负责。Turnkeysolutio模式的崛起、MTK的成功,背后更多体现的是手机行业新兴市场飞速发 展的带动和技术的成熟和全球化,技术门槛降低后的必然结果。
至于黑手机、山寨机偷税漏税等问题,更多体现的是法律环境的不健全。在知识产权保护、税收、产品质量、工人权益、生产和市场准入、走私控制等方面所存在的漏洞,而这些实际才是黑手机为生产、流通和销售创造优越环境的根本。
MTK从未为任何有关于自己的报道辩驳,但依然落入了尴尬的境地。这种情况目前随着越来越多的品牌厂商采用其方案而获得改善,MTK的模式更成为不少IC企业效仿的对象。
联发科发展的几个瞬间
1995年,成立……
蔡明介领导台湾“联华电子”的集成电路设计部门独立创业,成立了“联发科技”的前身多媒体小组,初始只有20余人。
2001年,钟情手机芯片
联发科在台湾成功上市,51岁的蔡明介则必须做出选择———创业前4年,联发科必须在CD- ROM、CD-RW以及DVD-ROM等几代芯片的稳定发展后找到下一个拳头产品,这可以说是决定联发科生死的问题。按照蔡明介经验,一定要符合市场足够 大;有一定的技术门槛和别人拉开差距;和联发科原来的开发技术有一定的相关性这三个标准。
经过内部激烈的讨论后,联发科决定投入手机芯片研发。当时台湾手机代工行业逐渐兴起,作为上游,联发科最终决定跟着客户走,杀入手机芯片领域。
2003-2004年,成长的低谷
是联发科颇为难熬的一段日子,手机行业的前景在哪里?当时,处于中游的台湾手机代工企业活得并不好,选定道路的联发科则不得不加大研发费用的投入。
此时,联发科之前的光存储和DVD产品市场已经逐渐饱和,价格开始下滑。手机芯片虽然在 2003年底开发成功,却迟迟无法打开市场。更重要的是,想“跟着”的那些客户并没有预想的成功,联发科必须去寻找新的客户。2004年四季度,代工行业 的连锁反应落在了联发科身上,其毛利率大跌40%%,在投资人会议上,蔡明介甚至亲自拿出采用自家芯片的手机进行演示,希望能够给投资人以信心。
据统计,当年全球IC设计产业总体增长率为27%,而联发科营收仅增长了5.23%.
2004年,猛虎出笼……
最终台湾一家叫达智的手机制造小公司因为无力采购国际IC设计巨头的产品,在实属无奈的情况 下采用了联发科的方案。由于当时手机IC设计领域并没有重视媒体功能的植入,大多仍采用双芯片分别控制通话和多媒体的设计,成本较高。而联发科因为此前做 CD、DVD芯片的开发,有这方面基础,迅速打开了市场。
2008年,跻身世界前三
手机芯片部门的收入已经占到联发科营收的50%以上,联发科也仅次于德州仪器及高通,跻身世界前三大IC手机设计厂商。
文/《南方都市报》
Posted: September 11, 2008 at 1:23 am | Tags: mtk, vxworks, 动态加载
转至:http://www.cnblogs.com/hpunix/articles/355758.html
整个过程为:
创建文件系统–》下载文件–》加载模块–》查找符号地址并执行
以下为演示该过程的一个简易实现文件,有使用或者引用的话,也
打个招呼,或者给评论一下:
==============START OF THE FILE=============
/*********************************************************
* 版权所有 NP系统工作室。
*
* 文件名称: \FilseSystemCreate.c
* 文件标识:
* 内容摘要: 创建文件系统,在系统启动之后保存文件
* 其它说明:
* 当前版本: S 1.0
* 作 者: william
* 完成日期: 2005-10-12 17:04
* 当前责任人-1:william
*
* 修改记录1:
* 修改日期:2006-3-21 12:00
* 版 本 号:S 1.0
* 修 改 人:william
* 修改内容:创建
* 修改记录2:
**********************************************************/
#include <taskLib.h>
#include <vxWorks.h>
#include <stdio.h>
#include <string.h>
#include <ioLib.h >
#include <symLib.h >
#include <loadlib.h >
#include “dosFsLib.h”
#include “ramDrv.h”
#include “usrLib.h”
typedef int STATUS;
#define ERROR -1
#define OK 0
#define DIAG_RAM_DISK_SIZE (0×100000*10) /* 64M */
#define DIAG_RAM_DISK_NAME “/selfdev/”
STATUS FileSystem_Init()
{
BLK_DEV *pBlkDev;
char *pFileSysRamDiskBase = NULL;
ramDrv();
pFileSysRamDiskBase = malloc(DIAG_RAM_DISK_SIZE);
if(NULL == pFileSysRamDiskBase)
return ERROR;
bzero(pFileSysRamDiskBase,DIAG_RAM_DISK_SIZE);
pBlkDev = ramDevCreate( pFileSysRamDiskBase,
/* start address */
512,
/* sector size */
64,
/* sectors per track */
(int)(DIAG_RAM_DISK_SIZE/512),
/* total sectors 64 MBytes */
0);
/* offset */
if(NULL == pBlkDev)
{
free(pFileSysRamDiskBase);
return ERROR;
}
if(NULL == dosFsMkfs (DIAG_RAM_DISK_NAME, pBlkDev))
{
free(pFileSysRamDiskBase);
return ERROR;
}
return OK;
}
extern SYMTAB_ID sysSymTbl ;
void runModule()
{
STATUS status=ERROR;
int fd;
MODULE_ID hModule ;
FUNCPTR taskEntry = NULL ;
SYM_TYPE * pType ;
if ((fd = open(“/selfdev/youown.o”, O_RDONLY, 0)) < 0)
{
printf(“\nCannot open memory device.\n”);
goto done;
}
if ((hModule=loadModule(fd,LOAD_ALL_SYMBOLS))==NULL)
{
printf(“loadModule error = 0x%x.\n”,errno) ;
goto done;
}
status = symFindByName(sysSymTbl,
“willian_test”,
(char **)&taskEntry,pType ) ;
if (status==ERROR)
{
printf(“symFindByName error=%d\\n”, errno) ;
goto done;
}
else
{
printf(“taskEntryr=0x%x, type=%d\n.”,
(int)taskEntry,(int)*pType);
status = taskSpawn(“test1″,
100,
0,
30000,
taskEntry,
0,0,0,0,0,0,0,0,0,0) ;
if (status==ERROR)
{
printf(“taskSpawn error=%d\n”,errno) ;
goto done;
}
}
done:
if (fd >= 0)
close(fd);
}
int downLoadModules(
char *hostName,
char *srcfileName,
char *destfileName,
char *usr,
char *passwd)
{
………
/*实现下载远端PC模块到本地机*/
return (filesize);
}
STATUS test_dynamic_download()
{
STATUE status = OK;
int fileLenth = 0;
status = FileSystem_Init();
if(status == ERROR)
{
printf(“\nerror occured during init file system\n”);
return ERROK;
}
fileLenth = downLoadModules(“winner2″,
“youown.o”,
“/selfdev/youown.o”,
“target”,
“target”);
if(ERROR == fileLenth)
{
printf(“\nSome error occured when download files\n”);
return ERROK;
}
else
{
if(0 == fileLenth)
{
return OK;
}
}
runModule();
}
=============END OF THE FILE==========
几个需要注意的问题:
1:在文件系统初始化中使用的是MALLOC,该部分是使用的实际是
BSP的空间在实际申请的过程中一定要根据自己可能拷贝的文
件大小和实际的BSP的空间
2:在创建文件系统之后,可以查看该目录的文件列表,或者执行
COPY,前提是必须在TORNADO中选择初始化文件系统组件;
3:在下载文件的过程中,有的BSP初始化已经可以直接通过COPY
在远端FTP SERVER直接拷贝文件,比如:
copy “youown.o”,”/selfdev/youown.o”
这种情况没有必要再调用downLoadModules,至于如何使能,
大家关注的话可以在后边对其专门论述。
4:加载模块的过程可以调用如下代码:
if ((hModule=loadModule(fd,LOAD_ALL_SYMBOLS))==NULL)
同样也可以如下实现:
ld(1,0,”/selfdev/youown.o”);
至于各个参数什么意思,参考HELP就可以找到结论了!
5:在 status = symFindByName(sysSymTbl,
“willian_test”,
(char **)&taskEntry,pType ) ;
中,有可能实际返回错误,但是通过lkup 是可以实际找到的,
对于这个问题,我看网上答案比较少,原因是这样的:
假如你在主机运行shell,默认的远端目标机是没有符号表
(symbol table)的,只有在主机的shell上,你运行LKUP的
时候,才能找到这个符号,不信你可以这样尝试一下,打开两
个SHELL,一个通过串口,一个通过TORNADO的网口,你在其中
一个中能够使用LKUP找到,而在另外一个则不能找到,是不是
呀??
接下来继续解释,我们的symFindByName是在目标机器上
执行的,所以自然的就返回错误了,也是没有找到了!
解决方法是这样的:在目标机中包含符号表,同时将其和
主机保持同步。
Wind River told us to include all this defines in the
config files for the target:
#define INCLUDE_SYM_TBL_SYNC
#define INCLUDE_LOADER
/* object module loading */
#define INCLUDE_NET_SYM_TBL
/* load the symbol table by whatever means */
#define INCLUDE_SHELL
/* target-based shell */
#define INCLUDE_SHOW_ROUTINES
/* optional target-based system utilities */
#define INCLUDE_STAT_SYM_TBL
/* optional target error status routines */
#define INCLUDE_SYM_TBL
/* symbol table package */
#define INCLUDE_UNLOADER
/* optional object module unloading */
#define INCLUDE_WDB
Posted: September 11, 2008 at 12:42 am | Tags: arm, mtk, nucleus, 动态加载, 嵌入式
转至:http://blog.csdn.net/pengzhenwanli/archive/2008/04/23/2319412.aspx#875603
之前有一篇文章是关于嵌入式单地址空间实现动态加载的想法,里面描述的是我根据相关资料进行猜测的地方,以及从技术上来说,可能需要的技术,最近难得有空闲时间,我实现了一下动态加载的。目前已经成功实现,下面说一下实现的过程。
先说一下实现此技术需要的平台:
OS:Nucleus
CPU:ARM7+cache
Baseband:VT3406
ADS1.2
说一下这些东西的来历,Nucleus是实时嵌入式单地址空间操作系统,CPU是介于ARM7与ARM9之间的CPU,BB芯片是VIA出的,这些东西目前都已经收掉不再使用,我也正好离职,从而有时间去实现一下动态加载的问题。
从理论上来说,动态加载很简单,只需要把当前的PC指针指向下一句执行的语句即可。也就是使用如下的ASM就可以实现:
MOV PC, Address
这样就可以顺利执行程序,在我实现的时候,考虑如下问题,程序执行如何返回,参数如何传递,程序执行完毕返回到哪里。
这些问题的解决,看起来比较复杂,其实很简单,程序的返回是放在LR中,这样在上一个函数调用的时候,只要LR的值不变,这样可以在下一个函数调用的时候,同样使用LR,这样就可以顺利返回。关于参数传递,由于ARM中使用r0-r3传递参数,这样只要不更改r0-r3,就可以顺利传递参数。这个地方想明白,我用了好久,特别是返回地址的问题,程序如何执行,应该返回哪里。解决方法如下
实现DynamicLoader(UINT8 *pAddress)
MOV PC, R0
这个地方一定要用ASM实现,否则无法完成需要的功能。由于在调用此函数时,已经把函数的返回地址放到LR中,具体ASM如下:
MOV r0,address
BL DynamicLoader
由于DynamicLoaer的实现问题,没有实际的返回,也就是不需要使用
BX lr
这样来做为函数的返回。这是由于R0所指向的一个函数的开始地址,从DynamicLoader开始,其实执行的是另一个函数,这里的DynamicLoader只是起到了一个跳转的作用。但是又必须使用函数调用的方式来进行,而不能直接跳转,否则函数没有办法返回。由于BL的时候填充了LR,这样在下一个由于实际调用不是使用的B指令,因为并没有设置LR,这样仍旧是在调用DynamicLoader时的LR,因此可以正确返回。
函数可以正确调用并返回,这是程序很大的一个进步,这样就可以实际构造可以运行的程序了。
下面说一下ADS编译为二进制可执行文件的问题,使用编译器如果一开始把所有的数据都放好,这样包括全局变量和静态变量,以及函数的执行地址等,都已在LINK的时候根据指定规则确定实际的运行地址,也就是说所有的函数的实际运行地址在LINK的时候已经确定。这样对于动态运行来说是不可行的,因为既然要动态加载,就需要所有地址都是静态的,因为每次对于读入内存的数据,起始地址是不缺定的,因此不能再LINK时把所有的地址固定死。
解决这个问题有两种方式,一种是在scatter loader中把程序的可执行地址固定好,在LINK时不LINK实际的数据,而在系统启动的时候,把这部分可执行文件拷贝的固定的地址,这样可以作为一个整体运行。但是这种方式由问题,就是应用的大小什么的都是固定死的,不能太灵活,不能根据应用实际调整。
这里使用另外一种方式,选择程序不在LINK的时候把所有的地址固定死,而是使用相对独立的函数调用方式。如下:
原来的方式可能使这这样
BL 0×10008;
而使用相对的地址程序如下:
ADD r5,pc,#18
BL r5
这样虽然多了一句,但是可以做到函数的运行地址是动态指定的,而不是编译为固定的地址。
其实ADS提供了把函数编译为独立地址的方式,
COMPILER使用如下的参数/ropi/rwpi
LINK使用如下的参数-rwpi –ropi
就可以把编译的程序做到运行时地址是独立的。
从上面来开,编译时地址的问题,还有运行时加载的问题,都已经顺利解决。但是这里还有一个问题,就是如何确保动态应用如何每次在使用的时候,都从固定的入口进入的问题。也就是说,虽然有了内存中的运行地址,但是如何保证每次都从固定的函数开始执行呢?如果每次都从编译的可执行文件0地址开始执行,没有办法保证每次调用的是同一个函数。
这个可以通过LINK来保证每次是同一个函数在0地址,使用如下的参数
-first DyanmicAppEntry
这样就可以保证DyanmicAppEntry的入口地址为可执行文件的开始了。
上面的文章解决了动态编译和加载的问题,下面说一下动态应用的问题。如果要使一个应用有价值,比然需要提供本地的功能调用,而且对于手机来说,系统已经基本上实现了大多数的功能,如果在动态应用中再重复实现一些功能,可以说既浪费了空间,又浪费了时间。而且对于硬件相关的功能,必须通过本地调用来进行,这样就需要如何把本地调用传入动态应用中。如上文所说,本地的调用地址都是在LINK的时候确定的没有办法直接在动态应用中使用,这样需要在运行时把本地调用传入动态应用,由于动态应用的入口还有好几个参数可以使用,这样就可以构造一张系统调用的表,在运行的时候传入动态应用,这样可以通过表来调用系统功能,这样就解决了系统本地调用的问题。
这里要特别说一下安全的问题,由于动态应用是直接更改PC指针运行的,这样,如果应用出错,系统可能就CRASH了,无法再继续运行,而且由于可以调用本地系统调用,可能做许多意想不到的功能,这样就可以在系统调用的时候
增加一个中间层,一些核心功能,必须满足一定的权限在可以调用。
Posted: September 11, 2008 at 12:37 am | Tags: arm, dynamic load, mtk, 动态加载, 嵌入式
转至:http://blog.csdn.net/pengzhenwanli/archive/2007/02/26/1514689.aspx
本文的的主要想法是来源于手持设备可运行应用程序和如何实现智能机问题的思考。问题的主要来源是关于可扩充应用程序的考虑,目前大部分手机都是非智能机,也就是不能扩充应用程序
1.智能手机与非智能手机
一般来说,智能手机目前公认三大系统,Windows Mobile,Linux和Symbian,也即是说采用这三种系统的手机,都成为智能机。我认为,从技术上来讲,智能机最主要的特征就是第一可扩充应用程序,也就是说用户可以自行安装需要的程序而不局限于手机自带的,第二就是多用户任务,也就是用户具有同时运行多个应用程序的权力。而非智能手机的操作系统可以说是五花八门,什么都有。但是基本上都有一个核心的特征,就是同时只能运行一个应用程序,而且所有的应用程序都运行在相同的地址空间,而智能机是运行在独立地址空间。一般来说,非智能机也支持多任务,不同的是这些任务共用相同的地址空间。用户操作的UI就是一个单独的任务,用户所能使用的功能基本上就是由UI提供的。
我原来对于非智能机是非常的不屑,认为没有什么发展前途,但是我最近又弄了一个非智能机的手机用了一下,发现很多功能都非常的人性化,从使用上来讲,并不亚于智能机,由此引发了我对于非智机功能的思考。对于智能机来讲可扩充的应用程序一般来说,都是由第三方开发商开发的,稳定性都有所缺陷,并不如原生的系统应用程序稳定。而且有些比较好的用程序价格不菲,我见过使用智能机的人大部分都是使用破解的应用程序,我本人也是。这个行为是违法的,当市场成熟以后,比如像美国,是不太可能的。
智能机由于操作系统功能强大,一般要求系统的硬件性能强劲,这样相应的功耗也大,待机时间相应的缩短。非智能机可以运行在性能较差的硬件上,并且获得的UI表现,不弱于智能机。这样就可以在相同的电力消耗的情况下,获得更长的使用时间。
2.非智能机获得智能机功能必须的要求
如上文所说,只要非智能机实现智能机最主要的两个特征即可获得智能机的功能。一个是用户自行安装应用程序的功能。另一个是同时运行多个应用程序的功能。
3.用户自行安装应用程序的功能
除了智能机以外,目前有两种技术都实现了用户可自行安装应用程序的功能,一个是BREW另一个是Java ME(J2ME)。下面分别说一下这两种技术,BREW技术由Qualcomm(高通)创建,包括一整套的体系,从运营到分发都有。在本文里只是对于客户端技术的说明,对于BREW技术而言,已经非常符合本文中所提及的技术,本文从另一方面来讲,也可以说是分析了BREW的基本技术。BREW技术高通把持的非常严密,目前而言,并没有开放源代码而且对于技术内部也是进行严密的封锁,因此目前出现的文章都是猜测技术的实现,但是对于技术来讲,万变不离其宗,要实现某种技术,有些东西是绕不开的,就像使用CDMA技术,无论是WCDMA,CDMA2000,还是TDS-CDMA都无法绕开CDMA的基本专利一样。BREW技术基本达到了本文的技术要求,既能动态加载,也能同时运行多个任务,而且对于系统功能的使用不像JavaME一样有严重的限制,基本上来说,可以使用系统提供的所有功能。而对JavaME,所提供的功能十分有限,就连存取本地文件都不可以。对于系统功能的使用,如果没有附加的支持,基本上不可能,目前应用最得的是游戏,最多有些网络方面的应用。对于提供系统级别的应用,比如说闹钟等,根本无能为力。也许以后能够提供,但是本人不太看好。另外还有一个就是Flash Lite技术,这个技术我并没有接触,就不详细说了。对于BREW和JavaME我都有相当长时间的接触,了解也比较细致,有些问题还是能够说一下的。
要解决用户可以自行安装应用程序的问题,必须解决以下几个问题,应用程序的加载运行问题,系统API的调用问题。
3.1应用程序加载
对于嵌入系统来讲,与通常的Windows系统不一样,Windows的所用应用程序都在硬盘上,运行的时候根据需要加载到内存中,在运行,或者是使用虚拟内存技术,直接映射的硬盘也可以执行。而嵌入式系统通常是在ROM中,并不需要加载到内存才能运行,直接就可以运行,因此大部分的嵌入式系统都是统一做好一个系统的Image,然后放到ROM中运行。这样所有的地址都在编译期间确定,要是再动态加载应用程序,将会面临运行时地址确认的问题。一般而言,对于嵌入式系统,ROM使用Flash来代替,Flash中一部分作为ROM,另一部分作为嵌入式的文件系统,具体的系统格式这里不作考虑。要是可加载应用程序的话,一般来说是放在文件系统中。这样要运行可动态加载的应用程序,并不复杂,只要把应用程序调入内存中,运行时设置正确的寄存器就可以了。
这里也就是把可执行的文件加载到内存中,由于是单地址空间的,而不是像Windows一样每个应用程序都是独立的地址空间,这样应用程序可以从任意地址开始执行,这样载入内存以后,把当前的执行指针PC这为此内存地址即可。这也是单地址空间的程序可以执行的关键。
3.2系统API的提供
要提供可以运行时call的API,在单地址空间可加载应用程序的约束条件下,可以使用的方法也非常的单调。一般来说,单地址空间的应用程序是统一编译和链接的,这样生成可执行文件以后,所有的地址已经固定了,这样如3.1所说,应用程序如果每个都调用相同的系统API,比如memset等,由于应用程序时独立加载的,这样由于在编译过程中已经把所有的地址确定,所以每个应用必须链接独立的lib,这样造成了空间浪费。如果只是单独的应用,不提供这一步,系统已经可以动态加载了。
但是目前是要求在嵌入式单地址空间实现,必须考虑空间的问题。因此必须解决系统库可以动态加载的问题。也就是库的地址不是在链接的时候确定。只要能够解决编译时的链接问题,就可以做到。
关于
4.同时运行多个应用程序的功能
Posted: September 11, 2008 at 12:01 am | Tags: arm, dynamic load, mtk, 加载, 动态, 嵌入式
摘要
提出一种适用于嵌入式系统的模块动态加载技术,设计实现简单,占用资源少,开销小,并且成功运用于DeltaOS.可提高系统的灵活性和扩属性.介招加载与动态链接的原理和应用情况,解释相关术语,描述基本设计思路:详细说明该技术的核心。即模块声明、调用库、两级重定位表,最后给出结论。
关键词
模块 动态加栽 嵌入式系统DeltaOS
引 言
随着电子技术的飞速发展,嵌人式设备应用越来越广泛,复杂度也越来越高。这使得硬件和软件设计比例发生了很大变化,软件开发的比重越来越大。然而传统嵌入式开发过程中需要将应用与操作系统编译链接成一个整体,然后下载到目标机上运行。如果在调试过程中发现问题,需要重新编链接然后重复下载运行的过程。这样的开发流程周期长而且繁琐,已经越来越不适应快速市场化的需要。
为了适应多样化的嵌入式应用和加快嵌入式系统的开发过程,除了需要可靠的基础平台软件的支持,如带有文件系统、网络协议栈的RTOS和配套的集成开发环境,更重要的是需要可以动态扩展的系统平台。近年来,新一代的嵌入式操作系统已经开始使用动态扩展技术:将基本系统(包括操作系统以及其他共享功能调用库)和应用程序开发分开处理,支持模块更新和动态加载技术。很多主流的传统嵌入式操作系统厂商,如windRiver、Green HilIs、Lynxworks,都推出了面向航空航天、基础通信设备等领域的高可靠、高性能的RTOS版本,支持应用和系统组件的动态加载和更新;而在消费电子领域,相关的操作系统厂商,如symbian、Palm、Microsoft,更是积极推出了具有相应功能的操作系统,在新一代移动设备上得到了广泛应用。
为了成为可动态扩展系统平台,大部分嵌入式操作系统需要使用动态加载技术。总的来说,动态加载是指应用或者系统在运行过程中需要使用某模块的服务,于是通过一系列预定的动作将指定模块加载到系统中,让调用者继续顺利工作。它实现的关键就是加载与动态链接技术。因为加载和动态链接互相依赖,关系紧密,所以将两者放在一起进行讨论。
1 加载与动态链接机制
加载主要负责将模块程序从二级存储设备(比如硬盘或者Flash)搬移到指定内存空间,并且将模块交由系统加载器统一管理。
程序链接分为静态链接、加载时链接和运行时链接。静态链接就是将程序和它运行所需的全部库链接成一个执行文件。它的优点是可以独立运行、速度快,但是它链接生成的代码尺寸比较大。加载时链接是指程序在编译链接时不会把它用到的库链接到执行程序中,而是在它被加载器加载时才解析执行文件,依次把用到的库装载到系统中让其运行。它的优点是程序本身代码量减小,但运行时程序占的内存并没有减小,同时增加了加载器的工作量。动态链接是加载时链接的进一步发展,它是指将库的加载过程延迟到程序运行时执行。这种方式不会给程序引入额外的代码,也不会增加加载器的开销,只有当应用真正使用某库时才会加载该库,减少了不必要的空间占用。它的缺点是可能会有一些运行开销。
嵌入式系统中动态加载和普通的动态链接概念类似,但是嵌入式系统中的加载链接器有其自身的特点:它是交叉加载,主机端做一部分工作,比如程序的重定位,执行文件的解析等等;而目标机端相对简单,主要做模块搜索定位和空间分配,以及指定物理地址或者映射虚拟地址让其运行。一部分嵌入式系统不支持虚拟内存,应用和内核共享存储空间。当系统加载了多个应用到系统中时,一般需要使用overlap技术来解决内存空间有限的问题,即是当多个应用的运行地址空间冲突时,加载器会冻结当前暂时不运行的应用,让新加载的应用使用指定的地址空间,PairnOS中就采用了这样的设计。对于支持虚拟内存的嵌入式系统,加载器的工作被大大简化,每个应用都有可以运行在同样的虚拟的空间,不需要加载器为其重定位或使用overlap技术,因此提高了工作效率。Vxworks6.O,WinCE都使用了这种设计。两种方式在不同的领域都有比较多的应用。
文中提出的模块动态加载技术是基于支持MMU(Memory Management Unit)的32位嵌入式操作系统,采用了加载与动态链接技术。使用该技术构建的嵌人式系统面向高端市场,特别是对系统可靠性、安全性要求很高的领域。在DeltaOS新一代高可靠的版本HAR(High Available Reliable system)的研发过程中,即成功地实现了基于该设计的加载器LambdaLoader,达到了预期的性能要求。
2 模块动态加载的设计
2.1 设计思路
首先定义一些概念:模块、目标程序、接口函数地址表和调用库(call Library)。
①模块,主要是指加载器加载的一个单位,并且这里模块的概念主要是强调它是为应用或者系统提供一系列服务的提供者。
②目标程序,是指模块的使用者。它可以是应用,也可以是另一个模块。
③接口函数地址表(文中也称之为模块重定位表),指在模块中有一个数组表,该数组表的内容是该模块对外提供的函数接口的地址。
④调用库,是供模块调用者链接使用的专有库。它与相关模块一一对应,将封装了的模块接口供目标程序使用。除此以外,它还有一个运行时才确定的模块重定位表地址指针和模块动态查找定位的代码。
如果在系统中要实现动态加载,首先需要一种模块定位机制,使得调用者能够在系统中动态定位需要的模块,其次是要能让模块与目标程序动态的关联在一起,协调工作。为了解决这些问题,需要一系列相关的设计:规定模块的声明方式;简化目标机端模块地址空间定位的工作;重定位表的机制等等。基于这样的设计,系统可以比较顺利地实现动态加载。模块动态加载的工作流程如图l所示。这里描述的主要是目标机端的工作。
2.2 模块的声明
模块首先要定义它的相关属性。这里使用模块声明文件来完成这个工作。模块声明文件中需要定义:模块名字、版本、对外提供的API接口。在系统编译模块程序后,会调用一系列的script代码。这些script会根据模块名字查找模块对应的模块声明文件,并根据该文件生成供模块调用者使用的调用库和与模块一起链接的附加库。
附加库包含系统后台通过调用script生成的接口函数地址表和模块注册函数。在每个模块的初始化函数中,会调用一个模块的注册函数(该函数主要工作是向系统注册模块的名字和接口函数地址表地址)。当模块被加载时,初始化函数会被系统调用,向系统注册模块信息,此后模块交由加载器统一管理。
2.3 调用库
每个模块在提供一个模块重定位表的同时,必须提供一个与之对应的模块调用库。别的目标程序必须并且只能通过调用库来使用这个模块提供的服务。每个调用库都有一个存储本模块重定位表的地址指针变量。该变量在模块被目标程序第一次使用时会被初始化为相应模块重定位表地址。
在模块第一次被目标程序使用即开始动态加载过程时,首先运行的是调用库的库初始化代码(Library initialcode),它通过指定的系统调用来初始化库中的模块重定位表基地址指针。此后每次目标程序使用模块提供的函数接口时,都通过以下公式得到该接口的实际地址:模块接口实际地址=模块重定位表基地址+函数index×4
在该公式中,函数index是指对应函数在模块重定位表中的数组下标值。因为根据模块声明文件生成的调用库中已经包含了每个函数的索引信息(index),同时在32位系统中需要乘以4得到准确的偏移量,所以当调用库中重定位表地址被初始化后,可以通过这样一个简单计算得到指定接口实际地址,完成函数调用。
当一个目标程序使用了模块,并正确动态加载后,其关系如图2所示。目标程序中链接了调用库,包含了函数跳转表和指向模块重定位表基地址的指针(ModuleBase);模块中则链接了附加库,包含了函数接口地址表(模块重定位表)。调用模块函数时,经过动态加载模块的过程以后,目标程序的模块重定位表基址指针指向了对应模块的函数接口表,然后函数调用就可以顺利进行了。

2.4 两级重定位表
在嵌入式领域,为了降低性能开销和增加确定性,目标机端加载器不会做程序重定位,而将相关工作在主机端完成,所以目标机端加载的所有程序都是绝对定位后的程序.为了实现系统动态扩展,必须使各个模块能够单独链接生成执行程序,并且运行时不用关心彼此的定位,这样即使一个模块被动态替换后也能同其他程序一起协调运行。这里通过两级重定位表机制来完成这个协调性的工作。
对于内核、操作系统组件模块或提供服务给其他目标程序使用的模块,要维护一张本模块提供的接口函数地址表(即模块重定位表,这里称之为二级重定位表)。为了保证本模块的向后兼容性,模块必须保证其接口函数在模块重定位表中的相对位置固定。即使今后不能提供这个接口函数,也需要将其保留,以保证同以前版本的二进制兼容性。
在模块的初始化代码中,模块通过系统调用向加载器注册这个模块重定位表的地址,注册时需提供模块名和模块重定位表的地址。加载器中管理着一个称为一级重定向表的表格。这个表的表项是“模块名”到“模块重定位表地址”的映射。因为这只是一个映射关系,所以各个模块对应的表项在一级表中的具体位置是可以改变的。
二级重定位表如图3所示。
使用两级重定位表的规则如下:
①模块可通过模块重定位表向其他目标程序提供接口函数;
②目标程序要使用别的模块提供的接口函数必须通过对应模块的调用库来实现;
③目标程序在使用别的模块提供的接口函数之前,必须通过加载器提供的系统调用服务获取对应模块重定位表来基地址初始化对方的调用库。
结 语
该设计实现了在嵌入式系统中的模块动态加载与更新,使得在嵌入式软件开发过程中,开发人员可以更有效的设计系统,共享资源,达到提高效率、产品快速市场化的目的。在基于DeltaOS的实现中,可以完成应用的任意加载卸载,系统组件的动态更新;多个应用可以共享一个全局的模块;一个应用可以同时使用多个模块等等。整个系统扩展性和灵活性大大提高,较好地满足了实际需要。但是设计中对容错性、健壮性的考虑还不够,在应用与模块的间接调用处理上还有优化的空间,所以在这些方面还需要进一步改进。
Previous Page