Nucleus PLUS介绍
今天在公司看到VIA的CBP系列CDMA手机方案用到了Nucleus +OS,说实话还是第一次听说,所以查了些资料,引用别人的总结做个百科小文,原文链接如下:
http://www.upsdn.net/html/2005-01/250.html
一、Nucleus PLUS嵌入式操作系统的简单介绍
Nucleus PLUS嵌入式操作系统是目前最受欢迎的操作系统之一,是美国源代码操作系统商ATI公司为实时嵌入式应用而设计的一个抢先式多任务操作系统内核,其95%的代码是用ANSIC写成的,因此非常便于移植并能够支持大多数类型的处理器。
从实现角度来看,Nucleus PLUS 是一组C函数库,应用程序代码与核心函数库连接在一起,生成一个目标代码,下载到目标板的RAM中或直接烧录到目标板的ROM中执行。
Nucleus Plus内核在典型的CISC体系结构上占据大约20k空间,而在典型的RISC体系结构上占据空间为40k左右,其内核数据结构占据1.5k字节的空间。Nucleus Plus以其实时响应、抢先、多任务以及源代码开放特性获得在通讯、国防、工业控制、航空/航天、铁路、网络、POS、自动化控制、智能家电等领域的广泛应用。
Nucleus PLUS 采用了软件组件的方法。每个组件具有单一而明确的目的,通常由几个C及汇编语言模块构成,提供清晰的外部接口,对组件的引用就是通过这些接口完成的。除了少数一些特殊情况外,不允许从外部对组件内的全局进行访问。由于采用了软件组件的方法,Nucleus PLUS各个组件非常易于替换和复用。Nucleus PLUS的组件包括任务控制、内存管理、任务间通信、任务的同步与互斥、中断管理、定时器及I/O驱动等。
二、Nucleus具有的优点:
1、提供源代码
Nucleus PLUS提供注释严格的C源级代码给每一个用户。这样,用户能够深入地了解底层内核的运作方式,并可根据自己的特殊要求删减或改动系统软件,这对软件的规范化管理及系统软件的测试都有极大的帮助。另外,由于提供了RTOS的源级代码,用户不但可以进行 RTOS 的学习和研究,而且产品在量产时也不必支付 License,可以省去大量的费用。对于军方来说,由于提供了源代码,用户完全可以控制内核而不必担心操作系统中可能会存在异常任务导致系统崩溃。
2、性价比高
Nucleus PLUS由于采用了先进的微内核 ( Micro-kernel ) 技术,因而在优先级安排,任务调度,任务切换等各个方面都有相当大的优势。另外,对C++语言的全面支持又使得Nucleus PLUS 的 Kernel 成为名副其实的面向对象的实时操作系统内核。然而,其价格却比较合理。所以,容易被广大的研发单位接受。
3、易学易用
Nucleus PLUS 能够结合 Paradigm,SDS以及 ATI自己的多任务调试器组成功能强大的集成开发环境,配合相应的编译器和动态联结库以及各类底层驱动软件,用户可以轻松地进行 RTOS 的开发和调试。另外,由于这些集成开发环境 ( IDE ) 为所有的开发工程师所熟悉,因而,容易学习和使用。
4、功能模块丰富
Nucleus PLUS除提供功能强大的内核操作系统外,还提供种类丰富的功能模块。例如用于通讯系统的局域和广域网络模块,支持图形应用的实时化Windows模块,支持Internet网的WEB产品模块,工控机实时BIOS模块,图形化用户接口以及应用软件性能分析模块等。用户可以根据自己的应用来选择不同的应用模块。
Nucleus PLUS的RTOS内核可支持如下类型的CPU:x86,68xxx,68HCxx,NEC V25, ColdFire, 29K,i960, MIPS, SPARClite, TI DSP, ARM6/7, StrongARM, H8/300H, SH1/2/3, PowerPC, V8xx, Tricore, Mcore, Panasonic MN10200, Tricore, Mcore等。可以说NUCLEUS+是支持CPU类型最丰富的实时多任务操作系统。针对各种嵌入式应用,Nucleus PLUS 还提供相应的网络协议(如TCP/IP,SNMP等),以满足用户对通讯系统的开发要求。另外,可重入的文件系统、可重入的C函数库以及图形化界面等也给开发者提供了方便。针对不同的CPU类型,Nucleus 还提供编译器、动态连接库、多任务调试器等相应的工具来配置用户的开发环境。
值得提出的是ATI公司最近还发表了基于Microsoft Developers Studio的嵌入式集成开发环境-NUCLEUS EDE。从而率先将嵌入式开发工具与Microsoft的强大开发环境结合起来,提供给工程师们强大的开发手段。
三、 源代码带来的优势众所周知,Nucleus实时多任务操作系统提供给用户源代码。
这除商务上给用户带来巨大益处(免交Royalty)外,还在技术方面给用户极大的方便,即无需编写和调试BSP,从而达到易学易用的目的,加速产品上市。对RTOS有一定知识的工程师一定清楚,使用RTOS最大的障碍在于编写和调试BSP。大家知道,在调试目标系统的软件之前,必须将目标与主机连接起来并建立通讯。为此,我们可以编写一段监控程序(Monitor)。然而,如果要调试基于RTOS内核的程序,主机上的调试器(Debugger)除要与目标建立通讯外,更重要的是必须识别RTOS的任务,这样才能进行任务级调试(Task-aware Debugger)。因此,只有Monitor是不够的。如果我们选用的RTOS不提供源代码,那么,主机上的调试器(Debugger)就只有通过用户编写的BSP来了解Kernel在现有硬件平台上对各个任务进行调度的情况。显然,编写BSP必须对CPU目标系统的硬件以及应用软件等有全面而深入的了解。一般说来,对于一个有一定硬件开发经验的工程师来说,编写一个新的BSP要花的平均时间为两个月左右。这对于一个新手来说可是比较困难的。对于编写BSP的工程师来说,另外一个更大的挑战就是如何调试BSP,即如何验证所编写的BSP是否正确。通常刚刚焊接安装好的PCB板中,硬件或软件的故障(Bugs)是比较多的。甚至更常见的是CPU部分都没有运作正常。有时时序错误和总线错误都还存在。在您把写好的BSP烧入EPROM(或FLASH)中试图将目标与主机建立联系时,您几乎100%地会发现根本无法通讯,眼前一团漆黑,不知是软件有错误还是硬件不运转。BSP在正常运行吗?不得而知。在焦急和摸索中您可能发现几个月已悄然而过。对于早期的实时操作系统来说,BSP是必由之路。然而,新一代的RTOS-Nucleus PLUS则避免了BSP带来的痛苦过程。因为Nucleus的调试是基于全新的动态连接库(DLL)。用户只须通过监控程序(Monitor)或者BDM调试口(或者JTAG调试口)建立目标系统(Target)与主机(Host)之间的通讯,并给主机上的Debugger初始化特性中加入Nucleus的动态连接库(DLLs),这时,调试器就能够自动地去识别运行在目标系统中的Nucleus内核和各个应用任务,从而完成任务级调试。上述的动态连接库(DLLs)是由RTOS厂商和Debugger厂商合作完成的,用户无须自行编写。因此,Nucleus的用户只需要将精力放在基于Nucleus的编程工作中。对于一个新手,往往经过一天到两天的学习和培训,就可以投入到应用程序编制工作中去,无需花大量的时间去研究CPU,特定的硬件等。
另外,由于有了源程序,用户在调试程序时可以清楚地通过STEP INTO命令,追踪到RTOS的内层中去,观看和学习Kernel对任务的管理和调度机制。对于有志研究RTOS深层技术的工程师来说极为方便。对于Motorola 68K和PowerPC,用户可以利用GreenHill公司的Mutil调试器或TRACE-ICD来完成Nucleus PLUS的调试;对于ARM和StrongARM,用户可以利用ARM公司的SDT251调试器或TRACE-ICD来完成Nucleus PLUS的调试;对于Intel x86实模式,用户可以利用Paradigm公司的调试器及其DLL来进行任务级调试;对于Intel x86保护模式则可以利用SSI公司Softprobe调试器和SSI DLL来调试;对于i960,SH3/4,ARM6/7,MIPS等芯片,则可以使用ATI公司自己的UDB调试器来进行任务的调试。结论:动态连接库(DLL)是在RTOS工具中新出现的应用趋势,通过这种方式,用户可以免去BSP带来的麻烦,灵活方便地进行开发和调试,大大加速开发进度。Nucleus实时操作系统提供源代码,支持丰富的CPU种类,配合各类DLL动态连接库,为使用和研究RTOS技术的工程带来极大的利益。
进行ARM系列的开发需要大量的设备投入,另外如果做比较大型的系统,还必须要操作系统,购买一个好的操作系统也是需要几十万元。ARM的应用层研发可以建立在C,C++及其他的大多数开发语言上,这对于软件公司来说是很方便的,只要准备好硬件及操作系统,其他的工作就可以分模块给N个人来进行。而对于底层的东西,ARM公司也可以有比较大力度的支持,因此如果需要用ARM系列开发高端产品,可行性是比较高的,开发周期也不会很长。当然,对于arm芯片,还是有一定的限制,比如没有除法指令,这样在编程时就要尽量避免用除法,否则会带来程序代码的增加和执行速度的降低。一般说,除法还是可以通过移位和乘法来代替。
另外,好久没上来码字了,上周结束失业,估计金融危机对我的影响暂时结束一下了吧。
posted @
2008-11-27 22:37 frank.sunny 阅读(214) |
评论 (0) |
编辑 收藏
这次失业之后,突然发现现在工作好像真的不是很好找,没办法,主动权不在自己手里,静下心来想想就当通过笔试来给自己查漏补缺吧,昨天笔试遇到一个虚拟继承的概念,这不虽说2分的题,但是这个玩意有大内容,我学习了下,也就先整个入门出来吧:
为什么要引入虚拟继承?
虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要(因为这样只会降低效率和占用更多的空间,关于这一点,我自己还没有太多深刻的理解,有兴趣的可以看网络上白杨的作品《RTTI、虚函数和虚基类的开销分析及使用指导》,说实话我目前还没看得很明白,高人可以指点下我)。
以下面的一个例子为例:
#include <iostream.h>
#include <memory.h>
class CA
{
int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性
public:
void f() {cout << "CA::f" << endl;}
};
class CB : public CA
{
};
class CC : public CA
{
};
class CD : public CB, public CC
{
};
void main()
{
CD d;
d.f();
}
当编译上述代码时,我们会收到如下的错误提示:
error C2385: 'CD::f' is ambiguous
即编译器无法确定你在d.f()中要调用的函数f到底是哪一个。这里可能会让人觉得有些奇怪,命名只定义了一个CA::f,既然大家都派生自CA,那自然就是调用的CA::f,为什么还无法确定呢?
这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CB、CC时还需要在编译器的语法树中生成CB::f,CC::f等标识,那么,在编译CD的时候,由于CB、CC都有一个函数f,此时,编译器将试图生成这两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译)
要解决这个问题,有两个方法:
1、重载函数f():此时由于我们明确定义了CD::f,编译器检查到CD::f()调用时就无需再像上面一样去逐级生成CD::f标识了;
|CB(CA)|
故此时的sizeof(CD) = 8;(CB、CC各有一个元素k)
2、使用虚拟继承:虚拟继承又称作共享继承,这种共享其实也是编译期间实现的,当使用虚拟继承时,上面的程序将变成下面的形式:
#include <iostream.h>
#include <memory.h>
class CA
{
int k;
public:
void f() {cout << "CA::f" << endl;}
};
class CB : virtual public CA //也有一种写法是class CB : public virtual CA
{ //实际上这两种方法都可以
};
class CC : virtual public CA
{
};
class CD : public CB, public CC
{
};
void main()
{
CD d;
d.f();
}
此时,当编译器确定d.f()调用的具体含义时,将生成如下的CD结构:
|CB|
|CC|
同时,在CB、CC中都分别包含了一个指向CA的虚基类指针列表vbptr(virtual base table pointer),其中记录的是从CB、CC的元素到CA的元素之间的偏移量。此时,不会生成各子类的函数f标识,除非子类重载了该函数,从而达到“共享”的目的(这里的具体内存布局,可以参看钻石型继承内存布局,在白杨的那篇文章中也有)。
也正因此,此时的sizeof(CD) = 12(两个vbptr + sizoef(int));
另注:
如果CB,CC中各定义一个int型变量,则sizeof(CD)就变成20(两个vbptr + 3个sizoef(int)
如果CA中添加一个virtual void f1(){},sizeof(CD) = 16(两个vbptr + sizoef(int)+vptr);
再添加virtual void f2(){},sizeof(CD) = 16不变。原因如下所示:带有虚函数的类,其内存布局上包含一个指向虚函数列表的指针(vptr),这跟有几个虚函数无关。
以上内容涉及到类对象内存布局问题,本人还难以做过多展开,先贴这么多,本篇文章只是考虑对于虚拟继承进行入门,至于效率、应用等未作展开。本文在网上文章基础上修改了下而得此篇,原文载于http://blog.csdn.net/billdavid/archive/2004/06/23/24317.aspx
另外关于虚继承和虚基类的讨论,博客园有篇文章《虚继承与虚基类的本质》,总结得更为详细一点。
posted @
2008-10-16 16:55 frank.sunny 阅读(2291) |
评论 (4) |
编辑 收藏
symbian官方推荐使用活动服务对象(CActive)来代替多线程的使用,我想这个道理是很明了的,在手机这样的小内存设备里,运行多线程的程序是非常耗资源的,为了节约资源,symbian提供了一个活动服务对象的框架,允许把程序里并发执行对象(其实不是并发,不过宏观上看来是)放在一个线程里面执行,这些并发工作的对象就通过活动规划器(ActiveScheduler)来进行管理.
关于这两个东西的介绍,网上有一大堆的文档,我就不在这里废话了,如何使用呢?这里我先举一个简单的计数器的例子.我选择写一个exe的程序,也就是说程序是以E32Main为入口的.
GLDEF_C TInt E32Main()
{
CTrapCleanup* cleanup=CTrapCleanup::New();
TRAPD(error,callInstanceL());
if (error != KErrNone)
{
printf("get error %d\r\n", error);
}
delete cleanup;
return 0;
}
以上的内容是每一个exe文件都应该做的,CTrapCleanup* cleanup=CTrapCleanup::New()建立一个清除堆栈,以便程序在异常退出的时候把清除堆栈里面的资源都释放掉.当然你也可以加上堆检测宏(__UHEAP_MARK,__UHEAP_MARKEND),这里我就不多说了。TRAPD是symbian里面经常使用的宏,功能类似于try,第一个参数是让定义一个错误返回值变量的名字, 后面就是可能有异常的你写的函数.当这个函数异常时,程序不会crash, 你可以得到异常的原因.可以参考nokia论坛上的一些关于这些使用的文档.
接下来是vcallInstanceL函数,在这个函数里面我来建立ActiveScheduler.
LOCAL_C void callInstanceL()
{
CActiveScheduler* scheduler = new(ELeave) CActiveScheduler();
CleanupStack::PushL(scheduler);
CActiveScheduler::Install(scheduler);
TRAPD(error,doInstanceL());
if(error)
{
printf("error code=%d\r\n",error);
}
else
{
printf("OK!\r\n[press any key]");
}
CleanupStack::PopAndDestroy(scheduler);
}
这段程序很简单就是创建一个活动规划器,并压入清除栈,然后安装活动规划器,这样就可以用了.再执行真正的实例函数,最后出栈销毁。doinstance(该函数将在最后的代码中给出,主要的功能就是调用我们自己写的活动计数器)我们放到最后来写,现在来构造我们的活动计数器对象。
class TimeCount : public CActive
{
public :
static TimeCount* NewLC(); // 构造函数
~TimeCount();
void StartL(); // 计数开始
void ConstructL();
void RunL(); // 延时事件到达以后的处理函数
void DoCancel(); // 取消请求提交
void setDelayTime(int delayTime);
private:
TimeCount();
RTimer iTimer; // 定时器
int iTimeCount; // 计数器
int mTime; // 计数间隔时间 单位秒
};
TimeCount::TimeCount():CActive(0) // 这里可以设置活动对象的优先级
{
// 把自己加入活动规划器
CActiveScheduler::Add(this);
}
TimeCount* TimeCount::NewLC()
{
TimeCount* result = new (ELeave) TimeCount();
CleanupStack::PushL( result );
result->ConstructL();
return result;
}
void TimeCount::DoCancel(void)
{
iTimer.Cancel();
}
void TimeCount::setDelayTime(int mTime)
{
DelayTime = mTime;
}
TimeCount::~TimeCount()
{
Cancel();
iTimer.Close();
}
void TimeCount::StartL()
{
// 设定定时器状态为每隔mTime秒钟状态完成一次
iTimer.After(iStatus, 10000 * 100 * mTime);
// 提交异步请求
SetActive();
}
void TimeCount::ConstructL()
{
// 初始化计数器和定时器
iTimeCount = 0;
User::LeaveIfError(iTimer.CreateLocal());
}
void TimeCount::RunL()
{
// 计数器+1以后继续提交延时请求事件
printf("The Count is ->>%d", iTimeCount++);
StartL();
}
每一个活动服务对象都有一个iStatus来标识当前对象的状态.在这里我们把iStatus设定为iTimer.After(iStatus, 10000 * 100 * mTime);也就是定时器定时mTime秒钟以后iStatus发生改变,这个时候活动规划器会收到这个状态的改变,从而调用相应活动对象的处理函数,也就是RunL函数.在RunL函数里面进行计数和输出,然后调用startL重新设置定时器和对象状态,再提交给活动规划器。这样mTime秒钟以后活动规划器会再次调用RunL函数.一直这样重复,这样就达到了计数器的效果。
最后我们来写doinstanceL函数
LOCAL_C void doInstanceL()
{
TimeCount* timeCount = TimeCount::NewLC();
// 每隔一秒钟打印一次
TimeCount->setDelayTime(1);
TimeCount->StartL();
CActiveScheduler::Start();
CleanupStack::PopAndDestroy(1);
}
创建好对象以后,加上CActiveScheduler::Start()程序就开始运行了,这句话告诉活动规划器该等待对象的状态的改变了(正常情况下,一旦CActiveScheduler::Start()之后,程序直到CActiveScheduler::Stop()才能终止运行),在这里就是timeCount的iStatus的改变.等iStatus改变并调用了RunL以后,继续等待iStstus的改变,这样我们使用活动对象的计数器就能够通过消息驱动运行起来了.
这里的CActiveScheduler只管理了一个CActive对象,就是timeCount,可以用类似的方法实现多个CActive,并且都加入CActiveScheduler,CActiveScheduler将会等待所有加入它的CActive的状态的改变,其中有一个的状态改变就会去执行对应的活动对象的处理函数,当状态同时发生的时候,会通过对象的优先级来决定先调用谁的RunL函数.CActiveScheduler也是非抢占式的,当一个RunL函数还没有执行完的时候,如果另一个CActive的状态改变,会等待RunL执行完以后再执行另一个CActive的处理函数(正因为这一点,所以通常RunL函数不能设计为长函数,否则会阻塞活动对象)。
本文在网上根据网上用人提供的原本阅读学习而成,可算是转载类型的。
posted @
2008-10-11 21:03 frank.sunny 阅读(1309) |
评论 (0) |
编辑 收藏
Active Object (AO) 框架,是Symbian的基本工作部分。它是为了满足多个任务同时执行的要求。在 Windows/Unix 平台上,我们可以不加思索的使用多线程来完成多任务。可是在嵌入式平台上,系统的资源是有限的。比如CPU、内存都比我们平时用的个人计算机要低。这就要求嵌入式系统能够合理的使用系统资源。不能频繁的切换线程或者进程。
Symbian为这种特别需求设计了Active Object (AO)框架。AO框架是运行于一个线程内部的调度框架。其基本思想就是把一个单线程分为多个时间片,来运行不同的任务。
这和多线程有很大区别。多线程之间是可以被抢占的(由操作系统调度),但是AO框架中的各个任务是不可被抢占的,一个任务必须完成,才能开始下一个任务。
下面是多线程和AO框架的简单比较:
多线程 AO框架
可以被抢占 不可被抢占
上下文切换耗费CPU时间 没有上下文切换
由操作系统调度 由线程自己的AO框架调度
每个线程都有至少4K Stack. AO没有单独的Stack.
操作系统还要分配额外的资源记录线程 只是一个Active Object.
Symbian系统本身使用了大量的AO框架来实现一些系统服务。这使得Symbian和其他嵌入式系统相比较,对系统资源的使用更加合理。
AO框架包括CActiveScheduler 和CActive (Active Object)。一个线程的所有的Active Object对象都被安装在该线程的CActiveScheduler对象内.由CActiveScheduler对象监控每个Active Object是否完成了当前任务,如果完成了,就调度下一个Active Object来执行。CActiveScheduler根据优先级来调度各个Active Object.
关于CActiveScheduler的创建和安装,CActive的创建和安装,和CActive的任务处理,可以参看 SDK 文档。理解起来不难。下面要说一个比较容易忽略的地方。这对理解AO框架非常重要。
创建调度器:
CActiveScheduler * scheduler = new (ELeave) CActiveScheduler;
CleanupStack::PushL(scheduler);
CActiveScheduler::Install(scheduler);
运行调度器:
CActiveScheduler::Start();
停止调度器:
CActiveScheduler::Stop();
以上代码都是运行在一个线程中的,一般来讲,一个EXE只有一个主线程.
可是如果真的有2个线程呢?
为什么在当前线程下调用CActiveScheduler::Start(),CActiveScheduler::Stop()运行/停止的就是当前线程的调度器而不是另一个线程的调度器?
每个线程都有自己的CActiveScheduler,那么这个CActiveScheduler类是怎么调用CActiveScheduler::Start(),CActiveScheduler::Stop()来运行/停止当前的调度器的呢?
我们看到Start/Stop并没有参数.
打开CActiveScheduler的类定义:
class CActiveScheduler : public CBase
{
public:
IMPORT_C CActiveScheduler();
IMPORT_C ~CActiveScheduler();
IMPORT_C static void Install(CActiveScheduler* aScheduler);
IMPORT_C static CActiveScheduler* Current();
IMPORT_C static void Add(CActive* anActive);
IMPORT_C static void Start();
IMPORT_C static void Stop();
IMPORT_C static TBool RunIfReady(TInt& aError, TInt aMinimumPriority);
IMPORT_C static CActiveScheduler* Replace(CActiveScheduler* aNewActiveScheduler);
IMPORT_C virtual void WaitForAnyRequest();
IMPORT_C virtual void Error(TInt anError) const;
private:
void DoStart();
void OwnedStartLoop(TInt& aRunning);
IMPORT_C virtual void OnStarting();
IMPORT_C virtual void OnStopping();
IMPORT_C virtual void Reserved_1();
IMPORT_C virtual void Reserved_2();
private:
// private interface used through by CActiveSchedulerWait objects
friend class CActiveSchedulerWait;
static void OwnedStart(CActiveSchedulerWait& aOwner);
protected:
inline TInt Level() const;
private:
TInt iLevel;
TPriQue<CActive> iActiveQ;
};
我们并没有看到静态的成员来表示线程,但是却有一个static函数CActiveScheduler* Current();返回当前线程的调度器.
现在猜想奥秘就在这个函数是怎么实现的。这个静态的函数怎么就能得到当前这个运行线程的调度器,而不是别的线程的调度器。我们可以猜想,肯定是Current()内部实现能取到当前线程的标识信息.用这个标识,静态函数能取到这个线程的CActiveScheduler.这个具体如何实现呢?
答案就是:当前线程的线程ID可以这样得到:
创建一个缺省的线程对象:
RThread thread;
取得当前线程的ID:
TThreadId threadId = thread.Id();
能得到当前线程的threadId,当然可以得到和当前线程关联的CActiveScheduler。因此以上两个问题也就迎刃而解了,在一个线程内调用CActiveScheduler::Start(),CActiveScheduler::Stop()开启的就是当前线程。
既然回复了上面的问题,那么我们自然就能明确,在一个线程内是不能通过Start和Stop函数来开启和停止另一个线程内的活动对象规划器CActiveScheduler。
补充点其他东西:
在Symbian操作系统中,每个进程都有一个或多个线程。线程是执行的基本单位。一个进程的主线程是在进程启动时生成的。Symbian属于抢占式多任务操作系统,这意味着每个线程都有自己的执行时间,直到系统将CPU使用权给予其他线程。当系统调度时,具有最高优先权的线程将首先获得执行。
进程边界是受内存保护的。所有的用户进程都有自己的内存地址空间,同一进程中的所有线程共享这一空间,用户进程不能直接访问其他进程的地址空间。
每个线程都有它自己的stack和heap,这里heap可以是私有的,也可以被其他线程共享。应用程序框架生成并安装了一个active scheduler,并且为主线程准备了清除栈。如果没有使用框架(如编写exe程序)那就要手动生成这些了。
Symbian操作系统专为单线程应用优化,因此强烈推荐使用“活动对象”代替多线程。
posted @
2008-10-11 20:34 frank.sunny 阅读(1250) |
评论 (2) |
编辑 收藏
如何在C++中调用C的代码
以前曾经总结过一篇(http://www.cppblog.com/franksunny/archive/2007/11/29/37510.html),关于在C中如何调用C++的代码,当时并未做完全的展开,只是简单的做了下调试,最近看到一个题目要求实现C和C++中代码的互相调用,其结果虽然都是通过extern “C”来实现,但是具体还是有些差别的。
先对C中调用C++代码作个简单回顾:
1、 对于C++中非类的成员函数,可以简单的在函数声明前面加extern “C”,通常函数声明位于头文件中,当然也可以将声明和函数定义一起放在cpp中,在没有声明的情况下,直接在定义前添加extern “C”也可
2、 对于C++类的成员函数,则需要另外做一个cpp文件,将需要调用的函数进行包装。
以上两项的实例参看前面C中如何调用C++代码的文章。
要实现C++中调用C的代码,具体操作:
对于C中的函数代码,要么将C代码的头文件进行修改,在其被含入C++代码时在声明中加入extern “C”或者在C++代码中重新声明一下C函数,重新声明时添加上extern “C”头。
通过以上的说明,我明白一点,那就是加extern “C”头一定是加在C++的代码文件中才能起作用的。
下面分析一下这个现象的实质原因:
C编译器编译函数时不带函数的类型信息,只包含函数符号名字,如C编译器把函数int a(float x)编译成类似_a这样的符号,C连接器只要找到了调用函数的符号,就可以连接成功,它假设参数类型信息是正确的,这是C编译连接器的缺点。而C++编译器为了实现函数重载,编译时会带上函数的类型信息,如他把上面的a函数可能编译成_a_float这样的符号为了实现重载,注意它还是没有带返回值得信息,这也是为什么C++不支持采用函数返回值来区别函数重载的原因之一,当然,函数的使用者对函数返回值的处理方式(如忽略)也是重要原因。
基于以上,C调用C++,首先需要用封装函数把对C++的类等的调用封装成C函数以便C调用,于是extern "C"的作用是:让编译器知道这件事,然后以C语言的方式编译和连接封装函数(通常是把封装函数用C++编译器按C++方式编译,用了extern "C" 后,编译器便依C的方式编译封装接口,当然接口函数里面的C++语法还是按C++方式编译;对于C语言部分--调用者,还是按C语言编译;分别对C++接口部分和C部分编译后,再连接就可以实现C调用C++了)。相反,C++调用C函数,extern "C" 的作用是:让C++连接器找调用函数的符号时采用C的方式,即使用_a而不是_a_float来找调用函数。
具体示例请见http://www.cppblog.com/Files/franksunny/CCallCpp.rar
注:如果你用VC6.0编译附件时遇到类似“fatal error C1010: unexpected end of file while looking for precompiled header directive”报错的话,请将bb.c文件做如下处理右键点击项目工程中的该文件,选择setting,在c/c++栏,选择PreCompiled headers,然后设置第一选项,选择不使用预编译头。
posted @
2008-10-10 17:54 frank.sunny 阅读(1650) |
评论 (1) |
编辑 收藏
摘要:
活动对象框架原理
一、概述:
Symbian OS是一个多任务的操作系统,那么为了实现多任务,同时使系统能够快速响应,高效的进行事件处理,并减轻应用程序员的工作负担(申请大多数耗时的操作(例如文件系统)由服务提供器来完成,服务提供器完成程序员提交的请求后,将会返回给程序员一个成功或失败的信号。),Symbian OS特意引入了活动对象的概念。
服务提供器API...
阅读全文
posted @
2008-10-09 20:42 frank.sunny 阅读(1183) |
评论 (0) |
编辑 收藏
这种在Symbian C/S架构中,服务器程序与客户UI进程主动通信中用的比较多。
对于在往UI框架应用程序发送消息,可以通过Symbian OS的Application Architecture Services可以进行应用程序间的通信,主要用到的类包括:TApaTaskList和TApaTask。
TApaTaskList:用于访问设备中正在运行的任务(假如有些任务隐藏了的话,那么通过这种方法也无法进行访问)。
TApaTask:表示设备中某个运行的任务,通过与程序关联的窗口组(window group)标识。
具体的解决方案:
发送消息端:使用TApaTaskList找到等待接收消息的任务,TApaTaskList::FindApp()提供了两个重载版本,可以使用程序的标题,也可以使用程序的UID进行查找。获得需要发消息的任务后就可以通过TApaTask:: SendMessage()发送消息了,它有两个参数,第一个参数用于标识消息,第二个参数是一个描述符的引用,可以用来提供不同消息时附加的具体信息。
TUid uid( TUid::Uid( 0x0116C9D3 ) );
TApaTaskList taskList( iCoeEnv->WsSession() );
TApaTask task = taskList.FindApp(uid );
if( task.Exists() ) //判断任务是否正在运行
{
LIT8( KTestMsg, "CustomMessage" );
TUid msgUid( TUid::Uid( 1 ) );
task.SendMessage( uid, KTestMsg );
}
接收消息端可以使用如下两种方案:
第一种方案:由于MCoeMessageObserver是处理来自窗口服务器消息的接口类,而CEikAppUi已经继承自MCoeMessageObserver,所以我们只需要在自己的UI类中重现实现MCoeMessageObserver的唯一成员函数HandleMessageL()用来处理接收到的消息即可,代码如下:
MCoeMessageObserver::TMessageResponse CXXXAppUi::HandleMessageL(TUint32 aClientHandleOfTargetWindowGroup, TUid aMessageUid, const TDesC8& aMessageParameters)
{
_LIT( KFormatStr, "%x" );
TBuf<32> bufUid;
TBuf<32> bufPara;
bufUid.AppendFormat( KFormatStr, aMessageUid.iUid );
bufPara.Copy( aMessageParameters );
iEikonEnv->InfoWinL( bufUid, bufPara );
return MCoeMessageObserver::EMessageHandled;
}
第二种方案:由于TApaTask::SendMessage()发送的消息可以被CEikAppUI的成员函数ProcessMessageL()拦截并处理,不过必须在没有重载HandleMessageL()函数的前提下,而且函数ProcessMessageL()只负责拦截消息标识为KUidApaMessageSwitchOpenFileValue和KUidApaMessageSwitchCreateFileValue的这两个消息,其它标识值的消息不会被传到ProcessMessageL()中,所以这种方案个人觉得很受限制,不自由,还是采用第一种方案好,具体代码代码如下:
//发送:
TUid uid( TUid::Uid( 0x0116C9D3 ) );
TApaTaskList taskList( iCoeEnv->WsSession() );
TApaTask task = taskList.FindApp(uid );
if( task.Exists() ) //判断任务是否正在运行
{
LIT8( KTestMsg, "CustomMessage" );
//这里的Uid不能使用自定义的,而且只有系统提供的两个
TUid msgUid( TUid::Uid(KUidApaMessageSwitchCreateFileValue) );
task.SendMessage( uid, KTestMsg );
}
//接收:
void CXXXAppUi::ProcessMessageL(TUid aUid,const TDesC8& aParams)
{
RFileLogger iLog;
iLog.Connect();
iLog.CreateLog(_L("tb"), _L("UpdateListener2.txt"), EFileLoggingModeOverwrite);
iLog.Write(_L("smms appui"));
if (aUid.iUid == KUidApaMessageSwitchCreateFileValue)
{
TBuf<256> buf;
buf.Copy(aParams);
iLog.Write(aParams);
BringMeToFront();
ShowCreateFile(buf,CFileMonitorEngine::EImageType);
}
else
{
CAknViewAppUi::ProcessMessageL(aUid,aParams);
}
iLog.Close();
}
明天就是中秋了,恭祝大家中秋节快乐
posted @
2008-09-13 07:46 frank.sunny 阅读(1245) |
评论 (1) |
编辑 收藏
摘要:
关于vCard和Symbian上的操作
前阵子关于Symbian通讯录操作的时候曾提到vCard,但是由于当时项目比较紧,所以也没有时间整理,今天特意抽了点时间小试了一下,发现很多手机(我试了下索爱的和诺基亚的)如果选中通讯录中的记录发送联系人或者发送名片之类的操作,就是会以vcf文件格式进行发送。不过手机上的vcf文件通常是用UTF-8编码的,所以虽然可以用ou...
阅读全文
posted @
2008-09-13 07:20 frank.sunny 阅读(1101) |
评论 (0) |
编辑 收藏
摘要: Symbian OS平台简体汉字编程编码处理
相信大家都在处理symbian中文显示的时候遇到了编码的问题,我现在就给总结一下这种问题的解决方法:
字符串编码中文表示常用的有:GB2312,GBK,Unicode,UTF-8
其中GBK是GB2312的超集,也就是涵盖了GB2312编码的所有内容;
UTF-8是Unicode的在网络传输中的一种编码格式。
如果我们使用...
阅读全文
posted @
2008-09-10 20:11 frank.sunny 阅读(1740) |
评论 (1) |
编辑 收藏
摘要: Symbian OS中的消息存储与常用操作
说明:本文前面消息的基本知识主要参考《Series60应用程序开发》中的有关内容,后面是前段做MTM开发中用到的代码。
一、消息存储基本知识
Symbian OS提供的消息传送架构基于Client/Server机制,服务器负责管理手机上的各种消息,在进行消息相关操作之前我们需要了解Symbian OS是如何组织和存储消息的。
手机中的各种消息...
阅读全文
posted @
2008-07-30 21:04 frank.sunny 阅读(1748) |
评论 (1) |
编辑 收藏