可写静态数据(WSD):问题解释和解决方法(转) --- TLS

来源:互联网 发布:nodejs php java 编辑:程序博客网 时间:2024/06/03 21:16

程序和线程的占用的内存, 在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的内存(RAM)! 如果有上百个的这样的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
                }
        }

全部的共用或静态数据可以被组合为一个结构, 举例:
//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例如客户端接口来让客户端修改这些共用可变数据。

 

转自: http://www.sf.org.cn/Article/lumen/200603/17252.html

原创粉丝点击