现在的位置: 首页 > 综合 > 正文

Symbian DLL中可写静态数据问题

2018年01月11日 ⁄ 综合 ⁄ 共 1794字 ⁄ 字号 评论关闭

  可写静态数据在EKA1的DLL中是不允许的,因为EKA1中,DLL具有分离的程序代码和只读数据区域,没有可写数据的区域。

  EKA2中,支持在DLL中使用可写静态数据,但不建议使用,因为从内存使用看它的代价很大。

  本文只讨论EKA2的情况,对EKA1中的情况不做讨论。

  正常情况下,如果在DLL中加入可写静态数据(如全局变量)当编译真机版DLL代码时,返产生编译错误"XXX.dll' has initialised data."。需要注意:S60 V3 FP2及以上的版本中只声明定义一个可写静态数据是不会报错的,只有在DLL中使用该数据才会报错。

DLL如何使用可写静态数据?

  可写静态数据Writable static data,下面以WSD简写。

  1. 在DLL的MMP文件中添加关键字EPOCALLOWDLLDATA,可以正常使用WSD,但是不推荐使用。后面介绍

  2. 用TLS,推荐也是通用方法,但是存在效率问题。后面介绍

  3. 通过类封装WSD,并把类作为参数传递给使用者

  4. 封装客户端/服务器结构,利用exe可有全局变量的特点

为什么不推荐直接使用WSD?

  这是因为对于每个载入DLL的进程,DLL中的WSD通常需要消耗4KB的内存。

  当进程载入第一个包含WSD的DLL时,它创建一个单独的虚拟内存区域来保存该WSD。一个虚拟内存区域最小为4KB,并且它的大小与WSD占用空间大小无关。4KB中WSD实际占用空间之外的内存都被浪费了。但是,如果随后的其它DLL载入该进程并且也使用WSD,那么这个虚拟内存区域可供其它DLL使用, 而不用为每个使用WSD的DLL都分割4KB的虚拟内存区域。

  既然内存是针对进程而言,那么潜在的内存浪费量为:(4KB – WSD 字节) × 客户端进程数

  如果你创建一个只被一个应用程序载入一次的DLL,那么开销还算可以接受的。但是,如果你的DLL可能被许多进程使用,带来的内存开销和限制就不太值得了。

  除了内存问题,Symbian模拟器并不完全支持使用WSD的DLL,在单独的正在运行的进程中,模拟器只能载入一个使用WSD的DLL(原因是Symbian OS模拟器在Windows上层的实现方式)。如果在仿真器中有第二个进程试图载入相同的DLL,该操作会失败并返回错误代码 KErrNotSupported。

可能被忽略的使用WSD的情况

  有时候你会发现自己不自觉地使用WSD,一些Symbian OS类具有非平凡的构造函数,这意味着其对象必须在运行时构造。这里是一些示例:

static const TPoint KGlobalStartingPoint(50, 50);
static const TChar KExclamation('!');
static const TRgb KDefaultColour(0, 0, 0);

  也就是说KGlobalStartingPoint只有在运行时才被构造,那么它的值并不是常量,是根据运行时刻分配不同的内存块的。所以如上的声明定义也算是使用WSD。

线程本地存储TLS(Thread Local Storage)

  这是一种基于线程的方法,在调用线程内dll使用同一份数据,但其他线程调用dll则有另外的数据。
  原理:在调用线程对应的内核线程DThread中,有一份调用dll的tls表,每个被调用的dll都会在该表内分配一个机器字的空间(所以说tls是基于线程的)。通常我们把用到的全局变量封装在一个结构体中,把这个结构体的指针存在这个空间上。当创建线程时在堆中分配该对象,线程销毁时该数据也被销毁。
static TInt SetTls(TAny* aPtr); // 设置TLS数据
static TAny* Tls(); // 获得保存于TLS中的指针
static void FreeTls(); // 清除TLS数据

关于Tls效率的问题

  存取tls涉及到CPU模式转换,需要先切换到特权模式,内核找到对应DTread,然后从Tls表中找到对应dll的tls,其执行指令数是普通指针操作的20倍(数据来源 symbian高效编程),所以要少存取tls。所以我采用的办法就是在封装tls的类中设计一个static成员变量,指向封装全局变量的结构体,避免每次调用Dll::Tls()。
  OK,终于整理完了,下面一篇介绍Symbian DLL中实现单例模式。

抱歉!评论已关闭.