补充一下有关XFS Manager的内存管理部分

既然提到了XFS Manager的内存管理,在这里就多说一下Windows操作系统的内存管理。

很多从事Windows操作系统平台开发的软件人员,经常使用内存分配函数却不知道它们是怎么实现出来的,比如,C/C++中经常会使用malloc()和new()来分配一块内存出来,在这块内存中进行各种操作,最后调用free()和delete()将分配的内存释放掉。

那么到底我们在调用C/C++语言的这几个函数时,计算机帮我们做了什么呢?下面我就讲讲这方面的知识。如果了解这些,对于写出一个稳定的ATM软件是很有帮助的。为什么很多人总是说一些ATM软件不稳定,较大一个原因是写ATM软件的人没有更深入去理解软件,只是为了满足需要用一门编程语言拼凑出来一个ATM软件,能够跑起来使用,但是怎样才能跑的更好,就难以更进一步的把握了。

无论是Windwos,还是Unix、Linux等操作系统,我都研究了下,对于现代32位、64位操作系统来说,它们的内存管理基本上差不多,我们下面来拿最常用的Windows操作系统来讲。

在Windows操作系统中内存管理一般可以认为由低级到高级分为4层,每一层都是调用更低一级的一层来实现的,最低级的一层调用CPU硬件来实现的。这样算来,如果包括CPU硬件,一共是5层了。

对于CPU硬件这层来说,拿最常见的Intel X86系列CPU来讲,CPU内部已经从硬件上支持很大程度的内存管理功能,包括虚拟内存分页机制,内存切换等,操作系统需要与CPU进行紧密的配合,才能实现现代操作系统的虚拟内存分配。有关CPU的硬件手册,每个CPU厂商一般都会提供,感兴趣可以看看,其中Intel的CPU手册有三卷,一共2000多页,看起来要多花点时间,我看完后没事还经常当作手册查看下,主要讲CPU的架构、模块说明和怎样操作CPU之类的东西。在这里先不理会CPU一级的内存管理,直接关注Windwos的内存管理机制。

前面已经说了,Windows的内存管理分为4层。其中最底层的是由驱动程序VMM.VXD(不同的Windows版本名字可能不同)提供的内存管理,因为它是驱动程序,运行在CPU的ring0特权级别,可以操作所有的计算机硬件资源,所以与CPU进行配合来完成内存管理的责任都由它来承担了。VMM.VXD驱动程序用来配置大块的内存,并且来操纵CPU的页pages(在Intel CPU中一页的大小是4K的内存)。我们写的应用程序一般不会直接调用VMM.VXD。这一层提供的部分函数是:_PageReserve/_PageFree/_HeapAlloc/_HeapFree等,注意都有下划线的。

稍上一层的是KERNEL32.DLL提供VirtualXXXX()等函数,象VirtualAlloc/VirtualFree等函数。这些函数以VMM.VXD为基础,也用来配置大块的内存,以页page为单位,即一调用WirtualAlloc,则必然是分配4K的整数倍大小的内存,不可能分配象20个byte等大小的内存。显然,一次只能分配4K大小倍数的内存,对于应用程序一点用都没有,因为我们不可能想申请一个256字节的内存的时候,操作系统给我4K的内存,那太浪费了。

再上一层的是KERNEL32.DLL提供的HeapXXXX()等函数,象HeapAlloc/HeapReAlloc/HeapFree/HeapCreate等函数,它们内部调用了下一层的VirtualXXXX()等函数。

它们就象我们在C/C++中使用的malloc和new等函数了,可以分配很小的内存块。这些函数包括了Windows进行内存管理的核心,实现了记录你每次申请的内存大小、内存的大块切割成小块、小块内存合成大块、有关的调试信息等相关信息。

最上一层是LocalXXXX()和GlobalXXXX()等函数,象LocalAlloc/GlobalAlloc等函数,它们内部调用了下一层的HeapXXXX()。它们主要的作用是为了与原来16位的DOS等内存分配机制兼容的。在32位的Windows操作系统中已经可以不需要直接在新写的应用程序中调用它们了。

到现在,我们已经知道了Windows的4层内存分配机制。我们接着看看C/C++中的malloc和new怎么来实现的。对于C++程序来说,很多编译器一般经过以下几个步骤:
先将C++代码进行预处理、接着将预处理完成的代码用C++到C的转换器转换为C代码、最后用C编译器将最终的代码编译出来。

从中可以看出很多C++程序是由C编译器编译出来的。当然,这个过程只是一部分C++编译器的处理方式,很多编译器并不是转换成C来编译的,而是直接编译的。

再来看看C++中的new,从上面说到的C++程序被一部分编译器最后转换成C来编译,这样很容易想到,C++中的new操作最终也会转换为C的malloc来实现最终的内存分配,确实,很多编译器是这样来实现的。

可以先总结一下。C++中的new一般分为两步走,首先会先调用C的malloc分配一个适当大小的内存,然后再调用对象的构造函数,将分配的内存初始化。对于malloc来说,它可以直接调用Windows操作系统提供的HeapAlloc()函数,它们的参数基本上是一样的,从而就实现了内存分配。用图形表示就如下:
new--->malloc--->HeapAlloc--->VirtualAlloc--->_PageReserve/_HeapAlloc。

这个只是一般的编译器所采取的措施,如果你自己写一个Windows平台下的编译器,也可以采取这种方式。但是象微软的VC++及Borland的编译器一般都是自己实现的malloc,也就是它们的malloc并不是直接调用了操作系统提供的HeapAlloc()等函数,而是自己重新写了个类似于HeapAlloc()的函数,这个就另当别论了。

在这个链中最被我们感兴趣的是由malloc直接调用的HeapXXXX()等函数。HeapXXXX()等函数是真正的内存管理核心,就像前面所提到的一样。至于VirtualXXXX()等

函数只是为了在内存中一次切一个4K字节的大块而已,至于怎么管理更小块的内存,全部在HeapXXXX()等函数中实现。

因为HeapXXXX()等函数的实现还是有些复杂,一般人对它的实现不是很感兴趣,在这里就不多说了,有兴趣可以找我交流。

最后,来看一种C程序中分配内存的情况:
char* p = malloc(0);

对于这个语句,注意它分配的内存大小是0。这时候,编译器是否会分配内存或者直接忽略掉本次内存申请呢?

其实这一句最终操作系统也会分配内存的,原因是操作系统对于每次的内存申请都保留一定的信息,象上面提到的申请的内存大小等信息,这些信息在Windows和Linux/Unix下面一般都是16个byte,即2个字节左右,并且在软件的Release版和DEBUG版不同。一般Release版的内存保留信息只是一个DWORD类型的大小信息和前后内存块的指针等,而DEBUG版包含的信息会多一些,可能包括内存大小、线程号(哪个线程分配的)、前后内存块指针、校验位等。
所以如果调用了malloc(0),则在DEBUG版操作系统还是要分配2个字节左右的内存的,用来保存一些信息,注意的是,该块信息中的内存大小那个信息填0。同样,Release版也会分配内存的。

操作系统内部会保留一个链表,这个链表中的每一项都对应一个内存信息结构,它记录着你每次申请的内存和系统剩下的内存,以满足你的内存申请和释放的需要。

好了,到这里,我们已经大概对Windows的内存分配有了些认识,如果想清楚的了解Windows的内存管理是怎样用程序代码写出来的,还需要更深入的了解一些知识。不过,真正想自己实现一个Windows一样的内存管理,也不是很困难的事,还算比较简单,只是要花些时间而已。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=300902

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值