程序和线程的占用的内存在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例如客户端接口来让客户端修改这些共用可变数据。
文章来源:开发视界