Symbian操作系统将应用程序设计为单线程的,在大多数情况下等待异步事件的产生和完成。虽然可以使用多线程,但是不提倡这种做法,一方面是上下文切换开销导致电池使用寿命降低,另外一方面就是开发较复杂。 Symbian使用Active Object框架来处理操作系统的异步事件处理特性。由两方面构成:
- The Active Scheduler: 用来调度事件,每个线程只能有一个
- Active Objects:这些对象用于处理事件,在一个线程中可以有多个
void After (TRequestStatus& aStatus, TTimeIntervalMicroSeconds32 aInterval)
- TRequestStatus:包含一个TInt类型的状态值,调用时值为TRequestStatus
- 时间请求当且仅当aStatus != TRequestStatus时完成,而非函数返回时完成
RTimer timer;
timer.CreateLocal();
TRequestStatus status;
timer.After(status, 10000000);
User::WaitForRequest(status);
这是同步调用,即当User::WaitForRequest(status);执行时,其他事件都被挂起,用户界面停止响应。一个比较好的解决办法是:当时间请求结束后收到一个通知,而在该过程中其他事件可以被处理。
Active Objects 可以被用来处理异步函数调用。从CActive继承,并有如下成员变量:优先级决定了何时被检查是否完成
- TRequestStatus:类成员变量,iStatus,传入异步函数调用
- RunL():当异步请求结束时被调用
- DoCancel():当异步请求被取消时调用
- 同步等待任何发出请求的结束
- 根据优先级测试每个注册的active object,看是否有发出的请求、它的请求是否已经结束
- 对于每个请求已经完成的active object,调用其RunL()函数
- 在RunL()返回后,再检查其他请求
- 对于RunL()可能Leave的情况,调用需函数RunError()
Implementing Active Objects
要实现一个Active Object,必须遵循以下步骤:
- 从CActive继承
- C++构造函数中:设置优先级EPriorityStandard,调用CActiveScheduler::Add()
- 提供一个进行异步函数调用的成员函数,传入:CActive::iStatus,调用CActive::SetActive()
- 实现RunL()和DoCancel()函数,因为他们都是纯虚函数,必须实现
- 在析构函数中调用CActive::Cancel()
上图展示了Active Object使用的调用流程。
Active Objects Other uses
- 处理异步函数调用
- 将处理器开销大的任务分成几个阶段
- 实现一个异步服务
- 调用一系列的异步函数
读后感 Active Object也是Symbian的特点之一,用来处理异步事件,相当于多线程的概念。
使用过程中有很多pitfalls,要千万小心。
虽然代码都能看懂,想通,感觉自己对她还不是非常了解......
你的应用程序需要实现某个功能(如Bubble排序),你需要实现一个AO,并且设置一个回调函数,以便AO将结果通知UI。
peter (2007-11-04 16:05:20)
本课从用户角度学习客户端/服务器框架,关注如何使用客户端API而非实现服务器端功能。Introduction Symbian OS使用客户端/服务器框架来访问系统资源:如打开文件、拨打、设置闹钟等。需要由服务器来管理系统资源来防止多个客户访问可能引起的问题。
服务器运行在自己的进程或者线程空间中,任何来自客户端的请求都要穿过线程边界。这表明:服务器对资源有着绝对的控制权,可以拒绝来自客户端的请求(如一个客户端请求删除另外一个客户端正在编辑的文件)。
Example Servers and Client APIs Symbian OS主要包含的服务器,如下图所示,其中:
- 蓝色:在自己的进程空间中
- 绿色:在一个线程空间中,与其他服务器共享一个进程
Kernel Server:运行在最高权限,管理系统中所有其他进程对硬件和内存的访问。
- RTimer:提供异步时间服务
- RThread: 提供线程访问和创建
- RSemaphre:提供线程间同步机制
- RFs:提供到文件服务器的一个会话,可以完成高层次的驱动器、目录和文件的操作以及获取目录列表
- RFile:提供文件创建、读取和写入操作
- RDir:读取目录中所含的entries
- RWindow:提供屏幕显示操作,开发人员一般使用CCoeControl::Draw()
- RAnim:与服务器端animation通信(服务器端高优先级drawing)
- CWindowGc:提供屏幕绘图操作的图形上下文
- RFbsSession:负责与字体和位图服务器之间的一个会话,程序开发中一般不使用该类
- CFbsBitmap:表示一个位图
- CFbsDevice:表示一个用于显示位图的图形设备
- RTelServer:提供到服务器的工具级别的访问
- RPhone:提供设备上操作
- RLine:提供一路的操作
- RCall:提供在某一路上拨打/接收的功能
- RSocketServ:连接到Socket服务器,发现所有的协议
- RSocket:提供连接到,接收和发送数据到另外一个socket的功能
- RHostResolver:提供DNS解析功能
- RCommServ:提供一个到Comms Server的会话
- RComm:提供通过串行口通行的功能
- CMsvSession:描述一个和消息服务器的会话
- CMsvEntry:描述消息stroe中的一个entry
- CBaseMtm:提供访问和操作消息服务器entry的接口
- Message Server:MMS,SMS,POP3,SMTP
- Socket Server:红外,蓝牙,CSD(Circuit Switched Data)
下图展示了不同的客户端/服务器会话场景:
Client1:和服务器之间只有一个会话,在应用程序的control environment中与文件服务器的默认会话。
Client2:和服务器之间有两个会话,除默认会话外还有一个显式的文件操作。
Client3:和服务器之间有一个会话和两个字会话,与文件服务器有一个连接,对每个打开的文件分别由一个会话。
Requests 下图展示了一个客户端如何穿越线程边界与服务器进行通信,RSessionBase等类都是基类,需要继承。
具体过程如下:
- 调用一个客户端接口所提供的API,如RFs::Drive(),来获取驱动器信息
- RSessionBase::SendReceive()被调用,参数为操作码和一个包含4个指向客户端内存指针的数组
- 内部的private函数RSessionBase::SendSync()被调用,访问内核
- 内核调用服务器线程中的CSession2::ServiceL(), 封装了消息内容的RMessage2作为参数
- 调用RMessage2::WriteL()在用户端的空间中写入所需要的数据
- 调用RMessage2::Complete()来表示对客户端请求的完成
客户端API继承自RSessionBase或者RSubSessionBase,必须以以下顺序使用:
- 建立到相应的服务器的连接,一般通过函数Connect()或者Open()。
- 在建立连接后,可以使用所有的API,请求可以通过同步或者异步的方式发送。
- 在使用完后必须关闭到服务器的连接,否则将发生资源泄漏
ClientAPI的使用案例:
HBufC* CFileUtil::ReadL(const TDesC& aFileName)
{
RFs fs;
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
RFile file;
User::LeaveIfError(file.Open(fs, aFileName, EFileRead);
CleanupClosePushL(file);
TInt fileSize;
file.Size(fileSize);
HBufC8* data = HBufC8::NewL(fileSize);
file.Read(data);
CleanupStack::PopAndDestroy(2);
return data;
}
读后感 这就是传说中的微内核设计?