Subject: 可写静态数据(WSD):问题解释和解决方法
Lee
Administrator
Rank: 9Rank: 9Rank: 9



UID 2
Digest Posts 0
Credits 18567
Posts 133
Money 189
Reading Access 200
Registered 13-3-2007
Status Offline
Post at 17-5-2007 13:09  Profile | Blog | P.M. 
可写静态数据(WSD):问题解释和解决方法

程序和线程的占用的内存在symbian里,一个程序占用的地址空间包括:

  System-wide 被ROM和RAM导入的DLL占用内存
  Process-wide 代码和数据块的占用的内存
  Thread-wide stack和 heap (不总是)占用的内存

在一个线程被创建的时候,默认情况下每个线程将会有自己的8KB的stack。我们可以在创建线程的修改stack大小来改变这个。我们也可以在MMP文件里用epocstaksize来设置线程大小。一旦线程开始的话它的stack不能被增大。如果全部的stack都是变数的话将不能被适应为特定线程的stack,这样将会是个混乱。当一个线程被创建以后,这个可以拥有自己的heap,或共享上层线程的heap。默认情况一个线程能会有至少4KB的heap,至多1M的heap空间。我们可以通过MMP文件用epocheapsize来修改这个。我们也可以在创建线程的指定这两个参数。一个线程的默认heap可以被增大,前提是当前的heap不能适应全部的heap资源。如果系统空闲目录没有足够的空间的话,将会出现内存溢出。

代表性的,在symbian里,可执行文件可以是EXE或DLL。所有基于ROM的可执行文件都在in-place执行,看起来想这些可执行文件不是在ROM里,一定要先导入到RAM里才可以。

每个基于RAM的EXE为了代码将会有自己的空间,只读数据和可写数据。当这个是基于ROM的EXE的时候会有一些优化;每个基于ROM的EXE会有自己的RAM空间为了读/写数据。代码和只读数据将被共享,然后可以直接可以从ROM读取。

基于ROM的DLL一个都不会都被导入。他们只是的用着ROM的in-place,基于RAM的DLL会被从新定义到一个特别的地址。当第二个程序需要同样的DLL,这将被附上现存的代码。Symbian OS将有一个参考数,所以当没有线程连接DLL的时候DLL不会被导入。所以DLL被全部的程序共享。

DLL被共享以后,当访问可写共用数据的DLL会有些问题。这些数据我们叫WSD(Write-able Static Data).每个有WSD的DLL需要分配给每个程序来一个分开的内存空间(4KB)来连接这个DLL.假定一个DLL有一个WSD的共用非恒量的字符,而且有50个应用程序连接它,这样的话就有50 * 4 = 200 KB的RAM空间将被分配给这个WSD,这样将会占用50Bytes的内存!如果有上百个的这样的DLL连接到上百个的应用程序,那样的话将会有很大的内存空间(100*100*4KB)会为WSD而浪费掉。

因为这个问题,我在上面也提过,Symbian不鼓励在DLL上用WSD。事实上symbian 9.1以前,我们不可以有WSD在symbian的DLL上。但从symbian 9.1开始,symbian支持这个通过用EPOCALLOWDLLDATA函数在MMP文件上。支持这个的主要的原因是为了把非symbian应用程序移植到symbian里。但symbian不鼓励用这个函数。替代这个symbian提供一些别方法实现这个:

1.用客户端-服务端的架构
2.用TLS(Thread Local Storage 本地线程存储)

给WSD用TLS:
每个DLL将会有TLS (Thread Local Storage本地线程存储)基于每个线程每个DLL.

在DLL里的全部的共用或静态数据可以被组合为一个结构,当线程创建以后这个特殊的结构将在heap里被创建。线程的TLS将被赋值为这个结构的指针。因为这个DLL::settls() 应用程序编程接口以被使用,为了 存取/修改任何共用数据,我们需要以下步局:

用DLL::TLS() 得到结构的指针
用指针定位实际的数据
线程被破坏的时候被TLS指向的结构可能被删除而且TLS可能被调零。

每个用这个技术DLL的将有一个函数.这个函数将照顾到创建包括所有在heap的共用数据和安装DLL的TLS的结构。这个代码可能和以下的这个差不多:

void InitLibrary()
        {
        //allocate the structure containing global data on heap
        GlobalData* p = new GlobalData();
        if ( p )
                {
                Dll::SetTls( p ); //Set this Dll's Tls with this structure
pointer
                }
        else
                //Panic the thread
        }

这样的DLL可能多带一个照顾空闲空间的函数。当需要使用这个DLL的时候,线程将调用这个函数。这个函数的功能看起来是这样的:

void CleanupLibrary()
        {
        //Get the Dll's Tls
        GlobalData* p = (GlobalData*) Dll::Tls();
        if ( p )
                {
                //Do some global variable specific cleanup activity if
required
                Cleanup( p );
                delete p;  //Deallocate the memory now
                }
        }

特别的是别的被导出的API的DLL将得到DLL的TLS,而且可以用共用变数。这个大部分的时候象这样被独立:

//defined in some header.
typedef struct
        {
        int globalVal1;
        ...
        char globalValn
        }GlobalData;

//some .c or .cpp file
EXPORT_C void FunctionOne()
        {
        //Get the Dll's Tls
        GlobalData* p = (GlobalData*) Dll::Tls();
        FunctionOne_r ( p );
        }
//re-entrant version of above function
void FunctionOne_r( GlobalData* p )
        {
        //use all those variables required pointed by the structure p
        p->globalVal1 += 10;
        p->globalValn =  ‘C';
        }

假设MyDLL.DLL是一个连接2个或更多的名字是LIBRARYONE.DLL和LIBRARYTWO.DLL的DLL,那时这个MYDLL.DLL的线程将会有2个TLS为了上面的2个DLL。当MyDLL.DLL用LIBRARYONE.DLL里的功能的时候这时的TLS将会和用LIBRARYTWO.DLL时的TLS不一样。

为WSD用客户端服务端架构

因为EXE不能被共享,Symbian OS支持EXE里的可写的共用静态数据。服务端作为一个线程来运行,可能是一个独立的线程/程序,和他的客户端分开,或有可能是兼服务端或客户端,服务端可能被嵌入到一个同样的程序象2个不同的线程。当服务端运行的象一个分开的程序的时候,可能这个是一个EXE。因为EXE可以有自己的WSD,在一般策略是把全部的共用数据放到服务端上,然后在暴露一些API例如客户端接口来让客户端修改这些共用可变数据。

文章来源:开发视界




有其他问题请加入Symbian开发群参与讨论:群 ①:623041已满,群②:36865776已满 请加群③:76404484
Top
 


All times are GMT+8, the time now is 7-9-2010 03:13

CopyRight © Symbianx.cn 2007 Powered By Discuz! 5
Clear Cookies - Contact Us - Symbian OS系统[S60,UIQ]开发中文翻译论坛 - Archiver

本站原文版权归原文作者所有,本站译文版权归本站所有,如需转载请注明原文和译文出处,否则追究法律责任