【转】Series 60应用开发初探之三:内存管理

原文:http://www.sf.org.cn/Article/symbiandev/200511/14310.html

 


Symbian系统
的内存是通过内存管理单元(MMU)来管理的。系统采用虚拟文件系统(VFS)将不同的硬件设备抽象成普通的文件格式并加以管理。移动终端一般没有硬盘,但将RAM设置为C:、ROM设置为Z:、CF卡设置为D:。Symian不支持虚拟内存技术,但采用了交换技术,MMU将物理的RAM分成4k个页,采用分页存储管理技术进行管理。
1.Chunk
块(Chunk):Chunk是映射到进程线性地址空间的一块RAM区域,一个进程初始化时通常拥有一到三个块,它们分别用作:
a.堆栈块,它包含进程所有的堆栈,这个块在进程生命周期中是一定存在的。
b.代码块,当程序需要载入RAM的时候(不是烧在ROM里面)需要这个块。
c.数据块,当进程拥有静态数据的时候这个块存在,注意,这里的静态数据指除机器指令(代码)以外的数据,不仅是指编码时声明为static的数据。
Chunk 分为局部和全局两种,前者仅所属进程可见,其它用户不能访问,后者相当于unix中的共享内存,可以被其它用户线程共享。和unix共享内存类似,全局chunk必需有一个名字来唯一标识其本身。如何使用全局块请参见Rchunk.
2.堆(Heap)
前面说到每个进程必有一个堆栈块,其具体情况是:每个线程都拥有一个chunk用于放它的栈,如果该线程是主线程,则这个chunk中还包含有堆,程序的内存请求就是在这个堆中分配的。当一个线程被创建时,有三种使用堆的情况:
a.自己建一个。
b.使用父线程的堆。
c.使用一个有明确标识的全局堆。
堆的创建由RThread::Create()完成,其原型是:
TInt Create(const TDesC& aName,
          TThreadFunction aFunction,
          TInt aStackSize,
          RHeap* aHeap,
          TAny* aPtr,
          TOwnerType aType=EOwnerProcess);
当aHeap参数为NULL时,该线程使用父线程的堆。例如:
RThread t;
_LIT(KTxtShared1,"Shared1");
... ...
TInt r=t.Create(KTxtShared1,
              threadEntryPoint,
              KDefaultStackSize,
              NULL,
              NULL);
当线程要新建一个堆时,必须先使用User::ChunkHeap() 或 UserHeap::ChunkHeap() (其实是一个啦,User 是从 UserHeap继承来的),然后将新创的堆作为参数传给线程的Create:
_LIT(KTxtShare,"Share");
_LIT(KTxtShared1,"Shared1");
...
RHeap* pH=User::ChunkHeap(KTxtShare,0x1000,0x100000);
RThread t;
TInt r=t.Create(KTxtShared1,
              threadEntryPoint,
              KDefaultStackSize,
              pH,NULL);
HOHO,上面这个堆就这样有了自己的chunk! User.ChunkHeap会自动调用Open()函数来增加一个引用,这个Open只有用User::ChunkHeap()才会被调用,当线程不再使用它创建的堆时,可以调用 Close()来释放.
小心了!如果是使用操作系统自动建立的堆,那么千万不要调Close,因为此时这个堆所属的Chunk中还含有线程的栈,如果你把它释放了,也就意味着线程没有栈了,下场就是线程崩溃,死翘翘.
堆切换:线程可以通过User::SwitchHeap()来进行堆切换,在进行这个调用之后,所有的内存请求都从新堆中分配。千万小心!如果进行了切换,那么释放内存的时候一定得切回来再释放,原则是从哪个堆请求的就切回哪个堆去释放,例如:
Thing* mything = new Thing; // Allocate on current heap
RHeap* oldHeap = User::SwitchHeap(newHeap);// Change heaps
... // Use new heap
User::SwitchHeap(oldHeap); // Change to old heap
delete mything; // before deleting.
为什么这样?KAO,不用说了吧。
堆Walk:什么是堆walk?好象怪怪的,其实是这样的:每个堆都维护着两个链:已占用的和空闲的。堆必须在需要的时候检查这两条链是否正常,检查呢就是顺着链表一路走下去,所以就叫堆Walk.Walk由TheapWalk::Walk来完成,不过Walk要调用一个叫Info()的纯虚函数,Info()负责报告两个链中每个节点的状态,当两个链表遍历完成或碰到一个坏节点时,Walk终止。
3.内存映射
和许多操作系统一样,Symbian系统 中每一个进程有其独立的虚拟线性地址空间,其值为0 - 0xFFFFFFFF(32bit),有关物理RAM怎样映射到进程空间,请参考操作系统理论方面的资料,在此不作讨论。在这个线性地址范围中,0 - 0x003FFFFF保留为系统用,0x00400000 - 0xFFFFFFFF为进程空间。此区间中0x00400000 - 0x3FFFFFFF为进程数据区,用户进程所有的数据就放在这儿,其中静态数据从低端(0x00400000)开始存放,共享代码段从0x20000000存放,什么叫共享代码呢?这在多个进程同时运行的时候,其中大部分代码都是相同的,为了节省内存,这把这些代码编译后产生的指令放在某一地址范围,这样所有进程实例均使用同一段内存,而不是每一个实例都进行一次拷贝,这样就大大减少了内存使用量。其实动态库就是一个共享代码的实现,无论win下的dll还是unix下的so,都是干的同一件事情。对于共享代码,所有进程都有着相同的内存映射图,由线性内地址到物理内存的映射是由MMU来完成的。
0x50000000 - 0x5FFFFFFF这个范围用于映射ROM中的代码。
0x80000000 - 0xFFFFFFFF这个范围是home section用的(home section怎么译?),这个段是用来干什么的呢?在系统进行进程调度时,经常需要进行进程上下文切换,此时被挂起的进程(例如时间片用完或等待IO)需要被切换出去,某一个等待中的进程会被调度执行,在这种情况下,挂起的进程上下文(现场)就会保留在他的Home section里面,等待下一次调度,需要注意的是,由于进程调度只能是操作系统才能完成的,所以交换出去的上下文对用户进程而言是不可存取的。此外,内核数据和堆栈也在Home Section之内,但对于用户进程这是不可见的。在home section里还有从ram加载进来的代码段,它们存放在home section顶端,这些加载进来的库对于用户进程是只读的,所以,用户进程能调用这些代码。
CleanUp机制是Symbian内存管理中很重要的概念,编码是应该严格遵循,因为NOKIA已经有汉化的文档,偶就不再说了(反正也没人家说得地道),请参阅
60系列开发人员编程术语 
请注意文档中对异常处理和双相构造的描述,这是和standard c++不同的,尤其是双相构造,确实是一个很好的思想,令在下犹如滔滔江水……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值