自己动手写XFS Manager

  刘永胜     2005年于广州


      既然没有人知道XFS Manager的源代码,咱们就自己写一个吧。

      因为XFS Manager 2.0和XFS Manager 3.0差别不大,下面就以XFS Manager 3.0为准,自己动手开发一个XFS Manager 3.0。

      整个XFS Manager是用C语言写的,也可以使用C++来写。以下提到的一些Windows API函数可以在Windows 的 MSDN中查到详细的使用方法。

      XFS Manager(即XFS Manager 3.0,以下同样简称)安装后默认有三个目录:XFS/SDK/Include、XFS/SDK/Lib、XFS/SDK/Doc(一般在C:/Program Files/Common Files目录下)。 其中Include中放所有的头文件;Lib放三个Lib文件;Doc放着整个CEN/XFS(即WOSA/XFS)的文档。另外有三个DLL拷贝到系统目录/WINNT/System32目录下面,分别是MSXFS.DLL(基本的XFS API and SPI函数,在Include目录下的XFSAPI.H和XFSSPI.H中定义)、XFS_SUPP.DLL(一些支持函数,在Include目录下的XFSADMIN.H中定义)、XFS_CONF.DLL(配置函数,在Include目录下的Xfsconf.h中定义)。

      到现在我们知道了XFS Manager包括哪些内容,下面就看看它是怎样实现的。

      我们先从比较简单的地方入手,先实现XFS_SUPP.DLL。XFS_SUPP.DLL比较简单,是一个导出一些函数接口的简单DLL,只是实现WFMCreateKey、WFMOpenKey等函数,主要是封装了一些对Windows 注册表的操作,它的实现只是简单的调用Windows API函数中以Reg开头的Reg*****等函数(RegCreateKey、RegOpenKey等)。

      其中在XFS Manager中有几个问题。

      一个是WFMEnumKey这些枚举函数,在循环的第一次是正确的,但第二次取出来的值有误,如果使用该函数就要特别注意了。可以用API 的RegEnumKey来代替WFMEnumKey。

     另外一个是XFS Manager只提供对字符类型的注册表进行操作(WFMQueryValue等),并没有提供双字节、二进制类型的操作,但是很多厂商都用到了双字节和二进制类型的注册表配置项,说明一个是他们自己直接使用了Windows  API 中的RegQueryValueEx函数,或者他们自己拥有XFS Manager的源代码,对其进行了扩充与修改。可能很多人需要查询双字节(即DWORD类型)的注册表键值,下面给个例子:

  DWORD    dwKeyValue = 0;
  DWORD  cchKeyValue = 256;
  DWORD   dwKeyType = REG_DWORD;
    
  l_hrRet = RegQueryValueEx(l_hKey, p_pcKeyName, NULL, &dwKeyType, (LPBYTE)&dwKeyValue, &cchKeyValue);
  if (ERROR_SUCCESS != l_hrRet)
  {
        // Error Handle
  }
  else
  {
        // Success。DwKeyValue中存放着结果
  }

     调用打开某个注册表项时尽量使用WFMOpenKey,不要用RegOpenKey,因为在XFS Manager2.0和3.0版时注册表项不同,如果使用RegOpenKey则需要指定不同的路径,并且路径还很长。

     这样看来XFS_SUPP.DLL应该是比较简单的,下一步我们要看看XFS Manager中有关内存管理的几个函数是怎样实现的(即WFMAllocateBuffer、WFMAllocateMore、WFMFreeBuffer,还有一个WFSFreeResult)。
XFS Manager的内存管理部分在我看来还是比较简单,并且采取的是效率不是很高的内存管理策略,不过这并不是什么问题,毕竟做为给ATM等金融外设软件来用,也不需要多么高的效率。内存分配是操作系统实现的一个较重要部分,操作系统正好是我感兴趣的,下面就将XFS Manager内存管理部分拿出来解析解析,把玩一下。

      XFS Manager是使用Windows的内存映射文件来处理内存,这里稍微解释一下什么是内存映射文件。一般了解软件的都知道,现代的操作系统包括Windows、Unix、Linux、OS/2等流行的操作系统都使用一种叫做虚拟内存的机制,通过这种机制,应用程序的编写者可以认为计算机的内存可达到4G(2的32次方,针对32位的CPU来说)。大家都知道,实际上,计算机的内存一般只用256M左右就够了,离4G差得远的很,所以操作系统就把一部分硬盘的空间拿来当做内存用,模拟出来4G的虚拟地址空间,有关虚拟内存的原理,在操作系统原理的书中一般有讲,可以参考一下,这里不多说。

     为什么要提到虚拟内存了,是因为Windows的内存映射文件机制是微软在实现虚拟内存时顺便将其中的有关硬盘换页的部分概念提取出来,重新命名叫做内存映射文件机制,专门来处理操作系统进程间通讯的问题,象所谓的管道、剪切板、一部分跨进程消息、Socket套接字等进程间通讯的底层实现都是通过内存映射文件来实现的。到这里,就知道了内存映射文件在Windows里面的重要性了。
     到目前,我们只是了解了内存映射文件的基本概念,到底XFS Manager为什么要使用它来处理内存分配呢?这个问题没有确切答案,我想一个是内存映射文件能够跨进程,再一个重要的是Windows提供API函数可以自由的操作内存映射文件,不需要进入操作系统内核级的编程,也有可能微软里面写XFS Manager的人正好对内存映射文件比较感兴趣。无论怎样,既然微软使用了内存映射文件来处理XFS Manager的内存,我们也用它来实现吧,但是XFS Manager也可以不按照微软的办法,这一点要清楚,无论采取哪种方式,最终都可以达到目的。
      在XFS Manager中有关内存分配的函数有4个,分别是:WFMAllocateBuffer()、WFMAllocateMore()、WFMFreeBuffer()、WFSFreeResult()。其中WFSFreeResult()是提供给ATMC上层用来释放WFSRESULT结构的,它的实现很简单,直接做一些简单处理,然后再调用WFMFreeBuffer()来最终释放内存,所以可以认为XFS Manager的内存分配函数只有前三个,它们跟C语言里面的malloc()、realloc()、free()基本对应,了解C语言的人一看就知道它们对应的是干什么的,功能分别是:申请一块内存、在原有基础上再多分配一块内存、释放指定的内存。
      通过对XFS Manager进行反汇编及操作系统内核级的跟踪调试,我找到了XFS Manager调用的各个Windows系统API,与我想象的差别不大。

      下面整体的讲讲XFS Manager的内存分配实现步骤:
      在ATMC上层调用XFS Manager的API函数WFSStartUp()时,这时会将第一篇文章提到的几个XFS Manager的DLL文件装入操作系统中,同时XFS Manager会先分配一个内存映射文件,可以查看在Windows的注册表中HKEY_LOCAL_MACHINE/SOFTIWARE/XFS/XFS_MANAGER项下有几项(专指XFS3.0的注册表,XFS2.0也类似,只是在HKEY_CLASSES_ROOT/WOSA/XFS下面):ShareFilename、ShareFilesize、ShareMapAddr,这三项是安装XFS SDK开发包时写入Windows注册表的,实际上它是提供给内存映射文件的API CreateFileMapping()当作参数的,大概的意思是在哪个地方分配多大的内存保留下来,注意,此时分配的内存块是一个完整的大块,是整个WOSA/XFS能够最大分配的内存大小,一次分配的内存不能超过注册表中指定的大小。有了这个保留下来的大块的内存,以后你的ATMC和SP每调用一个WFMAllocateBuffer()和WFMAllocateMore()就会在这块大内存中切割下来一小块,从而也就知道,如果一直调用这两个函数,最终会把WOSA/XFS保留的那一大块内存分完,就会出现内存不足的错误,所以要记住使用完一块内存后就要调用WFMFreeBuffer(),它将你申请的一块内存还给WOSA/XFS的那块大内存。这好比在图书馆借书,图书馆一共进了1000本书,每个人来拿几本看,但是要看完就还,不然别人再借就没了;你一次也不能借太多,不然你一次借1001本,肯定失败,因为总共才1000本。

      创建完内存映射文件后,调用Windows的API  OpenFileMapping()和MapViewOfFile(),到此时,就可以操作该块大内存了。

      XFS Manager中的WFMAllocateBuffer()和WFMAllocateMore()函数在大块内存中切割下来一小块需要的内存,是利用Windows的API函数VirtualAlloc()/VirtualFree()等来进行的,你可以认为ATMC和SP每调用一次WFMAllocateBuffer(),最终会由Windows的VirtualAlloc() API来在那块大内存映射文件中切割一块内存下来。调用WFMAllocateMore()也是一样由VirtualAlloc()来完成,多的个步骤是要将得到的内存地址与最初的WFMAllocateBuffer()返回的地址联系起来,从而可以实现释放时只需释放WFMAllocateBuffer()申请到的地址,不需要释放WFMAllocateMore()的地址,因为根据WFMAllocateBuffer()返回的地址可以查到 WFMAllocateMore()的各个地址,从而可依次释放。

      当然,不是简单的切割下来即可,因为我们最终还要释放以前申请的内存,所以每次调用WFMAllocateBuffer()和WFMAllocateMore()时还要在系统中保留下来分配的每一块内存的相关信息。该信息大概有几项:分配的内存地址、分配的大小、上一个空闲块、下一个空闲块、其他调试信息等。系统里面会有一个链表,链表的每一项都是一个这样的结构。这样当你调用WFMFreeBuffer()时,系统可以根据你传入的地址指针,在链表中根据内存指针地址查找那一项符合,如果找到,因为分配的大小已知,则可将该块内存重新放回整块大内存中去。

      当最终ATMC程序整个退出时,它会调用WOSA/XFS的函数WFSClearUp(),这时XFS Manager会调用Windows的API UnmapViewOfFile()和CloseHandle(),将内存映射文件从操作系统中清除,从而也可以看出如果你调用WFSClearUp()成功,则使用XFS Manager的几个内存分配函数不会造成内存泄漏,因为无论如何,都会被操作系统进行清理。

      至此,XFS Manager的内存管理实现写完了。写的比较仓促,有时间我再慢慢修改。下一次将讲讲XFS Manager是怎样实现同步异步、API到SPI的映射及各种资源管理的问题,这部分讲完就可以写一个完整的XFS Manager了。至于怎样写符合WOSA/XFS的驱动SP,那就更加简单了。

后记:现在发现内存部分有误,下次再改。



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

你可能感兴趣的:(WOSA/XFS,manager,windows,api,include,dll,微软)