XFS(Extensions for Financial Services),扩展金融服务。这几年在国内各商业银行及金融设备供应商之间开始变得热门起来。本文的内容来自笔者在学习XFS时的一些笔记,有一些理解不太透彻或正确的地方,还望各位专家批评指正。
要了解XFS,就不能不提WOSA(Windows Open System Architecture)和CEN(European Committee for Standardization)。
WOSA,Windows开放式系统架构,是微软公司提出的一种在Windows操作系统下的软件架构。WOSA是一种使基于 Windows 的桌面应用程序不需为每个平台重写应用程序就能连接到多计算环境的接口,允许基于 Windows 的应用程序连接到多种计算环境,而不用为每个平台重写应用程序。像我们经常用到的ODBC之类的都是WOSA的一部分,而XFS就是其中用于金融扩展服务的那部分,是微软公司针对金融行业提出的一种软件架构。最初是由微软、几家大的金融设备厂商和银行一起制定的一套技术规范。
CEN,欧洲标准协会(欧洲标准化委员会)。在这个委员会中,有CEN XFS Workshop这么一个工作组,是BSVC(Banking Solution Vendors Council,这是由超过20家金融设备厂商组成的一个委员会)的附加部分。现在,正是位于比利时的这个小组在管理和维护着这套规范。
前面讲的都是XFS的一些背景,那么XFS到底什么呢?一套基于C的WIN32 API。这是一套为了既方便应用开发者,又方便服务提供者的技术规范,定义了各类金融外围设备的驱动接口。采用这个接口的应用,都可以运行在任何能够提供符合这个规范的接口的金融设备上,而不论是哪家厂商。
上图就是XFS的体系结构图,我们可以看到,XFS实际上就是由API、SPI两套接口和一个管理器组成,这套规范主要就是API和相应的SPI这两套分别针对应用和底层服务开发的接口定义,另外还有一些其他的支持服务。习惯上,我们把应用叫做AP,把XFS Manager叫做管理器,而把底层的用来驱动硬件设备的那层叫做SP(即Service Providers)。
在这个架构中,应用调用API来操作设备,而API则是由管理器提供的。也就是说,应用所有的操作都需要通过管理器才能实现。管理器匹配API和相应的SPI,然后发送请求到相应的SP,这些SP可能是不同的厂商提供的。SP则提供SPI,SP是设备的管理者,向管理器和AP提供设备的操作和状态信息等,并通过Windows消息向管理器和AP提供这些信息。
管理器由3个DLL(动态链接库)组成,3个DLL分别提供不同功能。
名称
|
描述
|
安装路径
|
MSXFS.DLL
|
提供基本的API和调用SPI函数
|
系统目录,如c:/winnt/system32
|
XFS_SUPP.DLL
|
提供一些支持函数,比如内存分配之类
|
系统目录,如c:/winnt/system32
|
XFS_CONF.DLL
|
提供配置函数,比如读写配置信息
|
系统目录,如c:/winnt/system32
|
这3个DLL是随XFS的规范一起发布的,当然,也有一些软件厂商自行编写。
API有下面3种
– 基本功能:诸如 StartUp/CleanUp、Open/Close、Lock/Unlock和Execute,这些都是所有的XFS设备和服务类都支持的。
– 管理功能:诸如设备初始化、复位、挂起和恢复,用来管理设备和服务。
– 特定指令:用来查询设备或服务的信息、执行设备的特殊功能,它们被作为GetInfo/ Execute的参数送给设备。这些特定的指令有一套单独的规范,每类服务(或设备)一套。
SPI,基本和API相似,除了一些由管理器处理的指令。
在XFS中,我们可以从函数名称上也可以看出哪些是API,哪些是SPI,函数命名规则如下:
函数类型
|
函数调用者
|
函数提供者
|
API 函数
WFS…
|
应用
|
XFS管理器
|
SPI函数
WFP…
|
XFS管理器
|
SP
|
支持/配置函数WFM…
|
SP、应用
|
XFS管理器
|
上面是XFS的比较抽象的视图,下面这个例子可以帮助我们更好的理解它。
在这个架构中,SP是直接驱动物理设备的,实际上,在SP和物理设备之间还可能有两层或者更多的独立的层次。可以看到,某个厂商提供的SP并不需要兼容其他厂商的设备。
上面是比较大致的介绍了XFS的架构,那么XFS是怎么样运作的呢?下面我们从几个关键点上来说明XFS的运作原理。
前面我们已经知道在AP调用了某个API后,是管理器来将该API(WFS_XXX)匹配至相应的SPI(WFP_XXX),调用相应的SP的。那么管理器是如何做到这一点的呢?配置信息,是配置信息中定义了AP和SP之间的关系。针对这些配置信息,管理器还提供了配置函数(XFS_CONF.DLL)用来处理这些配置。
在XFS中,配置信息是基于WIN32 注册表的。XFS的注册表信息有两个逻辑分组,分别位于HKEY_LOCAL_MACHINE和HKEY_USERS下(注意,这是XFS3.0的配置信息,针对2.0,配置信息均位于HKEY_CLASSES_ROOT/WOSA/XFS_ROOT下)。我们来分别看一下这两组信息的结构
'>1
class 逻辑服务的类别,必须按照XFS的标准来赋值,参见标准的服务类定义文档(在XFS3.0中,即编号cwa14050-02的文档)
² provider
提供逻辑服务的SP的名称,这个名称可以自行确定,但必须和下面讲到的SP的键名称一致
在下面的结构中,XFS_MANAGER有下面几个可选择的值:
n
TraceFile 包含流水数据的文件名,如果这个值没有设置,那么缺省的文件为 C:/XFSTRACE.LOG
n
ShareFileName XFS管理器内存管理时用到的内存映射文件名。
n
ShareFileSize XFS管理器内存管理时用到的内存映射文件大小。
PHYSICAL_SERVICES的键就完全取决于厂商。
而在SERVICE_PROVIDERS下面,每个独立的SP都对应一个键,这些键的名字就是SP的名字,每个键下面都必须包括下面3个值:
– dllname
包含SP的DLL的名字
– vendor_name
SP供应商的名字
– version
SP的版本号
现在我们就可以来看一下,当AP调用了某个API的时候,管理器是如何知道该调用哪一个SP的。管理器接收到AP的调用后,会根据注册表中该逻辑服务的provider的值来确定应该调用那个SP,然后再根据SP的键下dllname的值找到要调用的DLL(如果这个DLL还没有装载的话,此时会先装载DLL),这样通过配置信息,管理器就知道AP的某次请求应该转发给哪个SP。
在XFS中,API和SPI这些接口都是以函数的形式表现出来的,这些函数由3种类型同步、异步和立即式函数。所谓同步函数,就是当调用函数时,程序会一直阻塞着直到函数完成所有操作才会返回;异步函数则当调用该函数时,函数马上返回,但却会在不确定时间之后完成操作,这时才能知道操作是否成功;而立即式函数,顾名思义,就是函数调用马上就会返回,并且知道操作是否成功。
API每个函数都有两个版本(例如,应用调用锁定服务时可以用异步的 WFSAsyncLock
或者同步的 WFSLock函数)。每个XFS API函数都以 异步、同步或立即3种方式之一来操作。
SPI只有异步式的接口,因此所有的SPI函数都是异步或立即式操作的。
我们来看看这3种函数在调用的时候具体有些什么不同。
同步函数用于不确定完成时间的操作,但应用想以连续的方式去处理函数。直到操作完
成前,XFS管理器不会返回控制权给应用,因此,同步函数是作为阻塞方式提交的。每个来自应用的同步调用都会被XFS管理器翻译成相应的SPI异步函数,再提交给SP。 在NT系统中,调用请求的应用会一直阻塞到请求完成。一个线程同时可能只有一个阻塞式的请求。
• 应用调用XFS管理器
• XFS管理器将请求翻译成对应的异步SPI,产生一个请求序列号以跟踪请求。提供自身的句柄来接收完成消息。调用相应的SPI。
• SP接收该请求,同时马上返回给管理器。
• XFS仿真同步式处理。
• 某个时段,SP处理了请求。
• 完成操作,SP发送一个完成消息给管理器。这个消息包含了一个指向 WFSRESULT数据结构的指针。里面包括请求序列号,状态码和其他的一些相关数据。
• XFS管理器将消息解包,将相关信息返回给应用。
异步方式适用于不确定完成时间的操作,允许应用以基于消息方式的WINDOWS事件驱动。
• 应用调用XFS管理器
• XFS管理器产生一个序列号,该序列号指向此请求。调用相应SP。
• SP接收该请求,同时马上返回给XFS管理器。
• XFS返回该序列号给应用,表明请求已经接受并正在处理。
• 同时,SP处理请求。
• 完成后,SP抛出一个完成消息给指定的窗口,这个消息包含了一个指向 WFSRESULT
数据结构的指针。里面包括请求序列号,状态码和其他的一些相关数据。
立即式函数,这些函数既不是异步,也不是同步。通常,这些函数不会用在和服务与设
备通信的函数。因此,无论成功或失败,操作会立即完成。它们有两种处理方式。
– 完全由XFS管理器处理,马上返回给应用。如,WFSStratUp和 WFSSetBlockingHook。
– 管理器传递给SP的立即式SPI。SP处理请求,立即返回给管理器,管理器便马上返
回给应用。如 WFSCancelAsyncRequest和 WFMSetTraceLevel。
上面我们了解了同步、异步和立即式函数,并进一步分析了其调用过程,那么在函数完成后,是如何将信息返回的呢?从上面的分析中我们看到了WFSRESULT这样的一个结构,对于要操作设备的函数调用,基本上操作完成的信息都包含在这样的结构中,无论操作成功还是失败。我们已经知道,这个结构是通过消息来传递的,那么SP是如何将这些消息发送出去的呢?
下面我们就要来了解XFS的消息机制。
先看看两个API函数,WFSRegister和WFSDeregister(还有另一套异步版本),是用来注册和取消注册在有异步事件发生的时候接收WINDOWS消息的函数,无论是在处理请求还是其他时候。使用这种方式,可以简单的监控设备的状态,而不再需要轮询。
有下面4中事件类型
n
SERVICE_EVENTS
n
USER_EVENTS
n
SYSTEM_EVENTS
n
EXECUTE_EVENTS
对前
3
种类型,如果某类事件被监控,有某个事件发生时,会有一个消息发送给每个注册了这类事件的句柄(
WFS_SYSE_LOCK_REQUESTED
事件除外,这个消息只被发送给锁定该设备的那个应用)。事件会在如下情况下产生:
Ø
服务状态改变,比如,打印机挂起。
Ø
设备需要人为操作(
USER_EVENTS
),比如,打印机需要装纸或更换墨盒。
Ø
产生了一个系统事件,比如,产生了硬件错误、版本确认失败、网络故障或没有足够的磁盘空间。
EXECUTE_EVENTS和其他的3类事件不同,消息只会发送给调用了WFSExecute的应用,尽管其他的应用也注册了EXECUTE_EVENTS事件。
注意,应用必须要确保注册了这些事件,否则,当事件产生时,将不会被报告,对设备的操作(WFSExecute)也不会正常的完成。
应用可以通过逻辑“或”来在一次调用中注册多个事件类:
hr=WFSRegister(hService,USER_EVENTS|SERVICE_EVENTS,hWnd);
当应用不想再收到一个或多个事件消息,它可以调用 WFSDeregister函数来取消之前的注册。WFSDeregister和WFSRegister的逻辑是对称的。应用可以通过参数列表针对每个窗口注销一个或多个事件类。要完全注销(每个窗口的每个事件类),则在参数列表中使用NULL事件类和窗口句柄。
尽管WFSDeregister是即时生效,但应用的消息队列中还可能有消息在等待,所以,一个强壮的应用应该做好在注销了事件后还要接收消息的准备。
我们已经知道,消息总是通过一个指向WFSRESULT数据结构的指针来描述事件要向应用传递的信息的。在应用使用完这些数据后,该如何处理这些指针呢?
XFS的内存管理机制会给我们答案。
XFS为动态的分配和释放内存规定了一个协议。通常的策略是SP分配它们需要使用的内存,应用使用后负责释放。这通过一个标准的结构(WFSRESULT)来实现,SP向应用传递消息时总是通过这个结构。
绝大部分的SP的函数调用都是异步的,它们的返回结果都是通过一个完成消息,该消息中包含一个指向 WFSRESULT 结构的指针,在该数据结构中包括了返回状态和其他的一些可选数据。SP用内存管理函数(下面会描述)为该结构分配内存。该结构的解析(拆包和释放)按如下方式进行:
n 异步API函数
u 通过完成消息接收这个结构的应用负责解析。
n 同步的 WFSExecute,WFSGetInfo,WFSLock函数
u 作为异步调用时,XFS管理器将此结构作为参数,向应用传递信息,应用负责解析。
n 其他的同步函数
u 管理器从该结构中剥离相关数据,然后解析该结构,将相关数据返回给应用。
在前面我们知道管理器中有XFS_SUPP.DLL这么一个动态链接库,它提供了一些内存管
理的函数。
有4个函数:WFMAllocateBuffer, WFMAllocateMore, WFMFreeBuffer和WFSFreeResult。用这些函数,可以支持常见的两种内存分配原则。
Ø 线性分配原则
Ø 链式分配原则
线性分配用来为单层或连续的数据结构分配内存。这样结构返回时放在一块WFMAllocateBuffer函数分配的内存区内。
链式分配提供了一种有效的手段去管理复杂的数据结构。如果SP不知道要使用的内存空间大小,可以先用WFMAllocateBuffer分配一定大小的内存空间,后面需要的时候可以调用 WFMAllocateMore 来请求新的内存空间,新的空间会自动连接到最初分配的内存块。新的内存块由WFMAllocateMore返回,通常新的内存块和最初的是不连续的。
SP可自由选择它分配的内存粒度,当应用或管理器要调用WFSFreeResult来释放整个数据结构时,这对它们是透明的。应用必须确保释放了返回给它的 WFSRESULT。注意,即使SP返回了一个错误,WFSRESULT结构仍然可能被返回;如果没有 WFSRESULT返回,指向该结构的指针为空。
SP也可以用这些函数对自己用到的”私有”内存进行管理,它可以用 WFMFreeBuffer来释放已经分配的内存。
为了避免在应用、SP和管理器间出现内存冲突,应用和SP必须使用XFS 管理器提供的这些工具来管理和XFS相关的内存。
下面的例子说明了应用如何为WFSRESULT和附加数据分配内存。注意,WFMAllocateMore是自动连接的,这样应用可以仅使用一个函数来释放所有的内存。
WFSRESULT *lpResultBuffer;
//SP allocates a WFSRESULT buffer structure
result=WFMAllocateBuffer(sizeof(WFSRESULT),ulMemFlags,&lpResultBuffer);
.
.
.//SP allocates additional memory
hr=WFMAllocateMore
(evenMoreMemory,lpResultBuffer,&lpResultBuffer->lpBuffer);
…
当应用已经从WFSRESULT和其他相关结构中得到了需要的数据,必须要释放这些内存,如下:
…
//application deallocates the structure when it finished with it
//free both the result buffer and any other additional buffers
hr = WFSFreeResult(lpResultBuffer);
注意:当应用调用一个异步或立即式(也就是非同步的)函数时,使用了一个指向内存对象的指针,那SP在将控制权返回给应用之前要确保不再需要访问该对象。这样,应用就可以马上释放该内存。
在这篇文章中,我们从XFS的背景、架构和运作原理的角度大致了解了XFS是什么,能做什么和怎么做到的,并没有去深入学习具体的API和SPI以及一些稍微复杂的如独占式设备访问、VDM的概念。笔者只是希望这篇文章能够给各位研究XFS的同仁在入门的时候有一些帮助,可以帮助大家了解XFS整体的运作,至于具体的一些细节则不在本文所考虑的范畴内了。