一种Java程序注入故障的方法和***
技术领域
本申请涉及计算机软件技术领域,尤其涉及一种Java程序注入故障的方法及***。
背景技术
软件开发过程中,往往需要对开发的程序进行可靠性验证。一般可以通过向待验证的程序引入故障以检测待验证程序是否产生异常情况。通过引入故障,可以加速***失效,从而在较短时间内模拟***错误,并获取***的失效数据,从而达到评测待验证程序的容错性能以及稳定性。故障注入技术一般包括基于硬件的故障注入、基于软件的故障注入以及基于仿真的故障注入。
一般的基于软件的故障注入方法可以通过动态修改进程所使用寄存器中的数据实现。计算机***中,进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有***资源,是一个动态的概念。它不只是程序的代码,还包括当前的活动,通过处理寄存器的内容来表示。当进程正在运行时,状态通常储存在寄存器中。现有技术为了评测软件***中对于某些进程失效后整个***的可恢复性、容错性和可用性,提供了一套基于计算机***寄存器的故障注入工具,该模块可应用于x86与x64系列的Linux***。
操作***中一般需要运行多个进程,进程与进程之间需要共享CPU资源。而操作***则负责进程间的调度与切换,当***需要对进程进行调度时,需要确保在进程切换过程中不能有数据的损坏。因此,操作***会先将需要换出的进程的当前状态(主要是寄存器内容)保存至内存中,并恢复需要换入进程的状态。然而,这些操作均需要在***时钟中断请求的参与下完成。时钟中断请求是时操作***中任务调度、进程切换的资源占用统计依据,是整个操作***内核工作的脉搏。也就是说,当***时钟中断出现时,操作***可能进行进程的切换操作,随后完成现场恢复操作。因此,当***时钟中断时,在***进行现场恢复操作之前,通过软件的方式可以修改内存中备份的寄存器数据,随后操作***可以将修改过的寄存器内容恢复至进程寄存器中,从而达到软件故障注入的目的。
对于Java程序,上述方式无法实现故障注入。这是因为,用Java语言编写的软件***,需要由Java虚拟机解释执行,而操作***无法直接执行。
在Java程序开发过程中,当程序员编写完Java源代码后,Java源代码可由Java编译器编译为可被Java虚拟机执行的字节码文件(Class文件)。进而,由Java虚拟机解释执行所述字节码文件,可以实现程序的运行。在Java虚拟机启动后,解释执行Class文件之前,Java虚拟机还需要完成对Class文件的加载、验证、准备、解析以及初始化这5个阶段。在正常情况下,Class文件被Java虚拟机加载一次。
现有技术中针对Java程序的一种故障注入工具,在Java虚拟机启动之前,使用字节码修改软件对编译完的Class文件进行修改,从而达到静态注入Java程序故障的目的。该工具需要预先收集相应的故障信息,并对class文件进行扫描匹配,然后注入相应的故障代码,启动Java虚拟机执行相应的字节码文件。
然而,该工具只适用于Java虚拟机启动之前对class字节码文件进行修改,当Java虚拟机启动后,该故障注入工具无法完成故障注入。在测试环境中,测试人员若想要复现已经出现的故障场景,需要将正在执行中的Java虚拟机关闭,然后修改字节码文件,重新启动Java虚拟机,这时,Java虚拟机已经无法恢复原有的故障上下文信息。特别是出现故障时的***报错,在需要重现该错误时,由于重新启动Java虚拟机,之前报错时的各种参数和现象已经可能不会再重现了。
发明内容
本申请实施例的目的是提供一种Java程序注入故障的方法及***,以实现Java虚拟机启动后的故障注入。
为解决上述技术问题,本申请实施例是这样实现的:
一种Java程序注入故障的方法,包括:
客户端接收输入的故障代码,并将所述故障代码传送至总控端;
Java虚拟机启动后,加载原始Class文件;
所述总控端监控所述Java虚拟机所在Java进程的运行情况,取回所述Java虚拟机加载的原始Class文件;
所述总控端根据所述故障代码编辑所述取回的原始Class文件,生成编辑后的Class文件;
总控端将所述编辑后的Class文件上传至所述Java虚拟机以替换所述原始Class文件;
所述Java虚拟机解释执行所述编辑后的Class文件。
一种Java程序注入故障的***,包括:
客户端,用于接收输入的故障代码,并将所述故障代码传送至总控端;
总控端,用于监控所述Java虚拟机所在Java进程的运行情况,取回所述Java虚拟机加载的原始Class文件;根据所述故障代码编辑所述取回的原始Class文件,生成编辑后的Class文件;将所述编辑后的Class文件上传至所述Java虚拟机以替换所述原始Class文件;
Java虚拟机,启动后用于加载原始Class文件,并用于解释执行所述编辑后的Class文件。
由以上本申请实施例提供的技术方案可见,本申请实施例通过在Java虚拟机启动后,监控所述Java虚拟机运行情况并取回所述加载的原始Class文件,通过编辑生成编辑后的Class文件以替换所述原始Class文件,可以实现在Java虚拟机启动后不需要重启启动Java虚拟机的情况下诸如指定类型的故障,并可以保持报错时的上下文数据。
附图说明
为了更清楚地说明本申请实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本申请中记载的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动性的前提下,还可以根据这些附图获得其他的附图。
图1为本申请一个实施例的JVMTI交互模型;
图2为本申请一种Java程序注入故障的方法实施例;
图3为本申请一种Java程序注入故障的***实施例。
具体实施方式
本申请实施例提供一种Java程序注入故障的方法及***。
为了使本技术领域的人员更好地理解本申请中的技术方案,下面将结合本申请实施例中的附图,对本申请实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本申请一部分实施例,而不是全部的实施例。基于本申请中的实施例,本领域普通技术人员在没有作出创造性劳动前提下所获得的所有其他实施例,都应当属于本申请保护的范围。
程序员编写完Java源代码后,Java源代码可以由Java编译器编译为可被Java虚拟机解释执行的字节码(Class文件)。在Java虚拟机启动后,完成Class文件的加载、验证、准备、解析以及初始化等工作,进而解释执行Class文件。正常情况下,Java虚拟机启动后,只能加载一次Class文件。
本申请Java程序注入故障的方法一实施例中,包括如图1所示的JVMTI交互模型。
如图1所示,包括客户端、总控端和目标Java进程。其中,目标Java进程可以包括代理(Agent)、JVM工具接口(JVM(Java虚拟机)Tool Interface,JVMTI)、Java程序(JavaApplication)。
JVMTI可以与Java程序共同运行在同一Java虚拟机中,JVMTI负责监听维护Java程序。当其它程序需要与JVMTI进行通信时,可以创建JVMTI代理,即Agent。创建的Agent与目标Java虚拟机可以在同一进程上执行,Agent可以作为JVMTI与外界交互的媒介。
客户端是用户交互的接口,用户所有的操作行为可以在客户端完成。用户通过客户端编辑故障注入代码后,还可以通过客户端将故障代码上传至总控端。
总控端可以与客户端进行通信,将来自客户端的指令信息进行解析,得到故障代码中的参数。另一方面,总控端可以监控Java进程的执行状态,并可以与Agent进行交互,进而可以向Java进程中的JVMTI发送控制信息。具体的,Java虚拟机启动后完成对Class文件的加载后,总控端可以从Java虚拟机中取回加载的Class文件,然后对Class文件进行修改,修改后将其回传至Java虚拟机,并替换原始Class文件。在该过程中,一方面,总控端可以与客户端进行通信,将来自客户端的指令信息进行解析,得到故障代码。另一方面,总控端可以取回Java虚拟机启动后加载的Class文件。进而,总控端可以根据故障代码对取回的Class文件进行编辑,生成编辑后的Class文件。生成编辑后的Class文件之后,总控端可以将该编辑后的Class文件上传至Java虚拟机,以替换原始Class文件。
图2示出了本申请Java程序注入故障的一方法实施例,如图2所示,包括:
S210:客户端接收输入的故障代码,并将所述故障代码传送至总控端。
如前所述,客户端是用户交互的接口,用户所有的操作行为可以在客户端完成。具体的,客户端可以包括操作控制台和及通信模块。其中,所述操作控制台可以作为用户提交故障代码的交互接口,接收用户下达的操作指令。所述通信模块可以将故障代码上传至总控端。
所述操作控制台可以包括脚本编辑器。所述脚本编辑器可以为用户提供故障注入脚本的编辑功能,辅助用户以编辑故障注入脚本的方式编辑故障代码。具体的,所述脚本编辑器可以提供修改Class名称、方法名称以及操作行为的操作;这样,用户可以利用所述脚本编辑器将欲修改的Class名称、方法名称以及操作行为制作为注入脚本。
所述客户端还可以包括脚本校验模块,用于验证用户编辑的脚本是否符合规范。
所述总控端可以采用Socket方式与所述客户端进行通信,将来自所述客户端的指令信息进行解析,得到所述故障代码。采用Socket编程技术,在总控端开启监听端口,可以实现多次修改同一正在运行的Java虚拟机的Class文件。这样,当有新的故障注入请求时,客户端通过Socket连接的方式将故障注入脚本发送至总控端,总控端可以解析来自客户端所提交的故障注入脚本。
S220:Java虚拟机启动并加载原始Class文件。
本步骤与现有技术类似,不再赘述。
需要说明的是,S220与S210不存在严格的先后顺序,S220可以在S210之前或之后执行,当然S220也可以与S210并行执行。
S230:所述总控端监控所述Java虚拟机所在Java进程的运行情况,取回所述Java虚拟机加载的原始Class文件。
如前所述,Java进程可以包括代理和Java虚拟机。换句话说,所述Agent可以与所述Java虚拟机在同一进程上执行。JVMTI与Java程序可以共同运行在所述Java虚拟机中。所述JVMTI可以监听维护所述Java程序。所述Agent可以作为JVMTI与外界交互的媒介。JVMTI相关的接口调用信息可以传送至所述Agent。
总控端可以设计为伺服进程,监控所述Java进程的执行状态,并与所述Agent进行交互。通过所述Agent,总控端可以发出与所述JVMTI相关的控制信息。
具体的,本步骤中,所述总控端可以通过JVMTI监控所述Java虚拟机的的运行情况,并在监控到所述Java虚拟机加载原始Class文件后,取回所述原始Class文件。进一步的,所述总控端可以包括字节码取回模块,通过该模块,可以取回所述Java虚拟机加载的原始Class文件。更具体的,本申请中可以定义一个抽象类Transformer,该类可以实现ClassFileTransformer接口,其Transform方法给出了Class字节码的编辑过程。在Transform方法中,使用字节码取回模块取回所述Class文件。通过使用ClassPool类的get方法,可以获取指定类名所对应的字节码文件,并返回该字节码文件的类别类的CtClass类型的引用。CtClass类提供了修改class名称、增加成员变量、增加实例方法、获取指定方法等一系列接口。
S240:所述总控端根据所述故障代码编辑所述取回的原始Class文件,生成编辑后的Class文件。
所述总控端可以根据S210中接收的故障代码编辑所述取回的原始Class文件。具体的,所述总控端可以包括字节码编辑模块,通过调用Javassit软件来实现对所述原始Class文件的编辑。Javassist是一个开源的分析、编辑和创建Java字节码的类库,是由东京工业大学的数学和计算机科学系的ShigeruChiba(千叶滋)创建。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
关于Java字节码的处理,目前还含有一些其他类似工具,如BCEL,ASM。例如,Javassist与ASM特点对比如下:
1、javassist基于源码级别的API,比基于字节码的ASM简单;
2、基于javassist开发,不需要了解字节码的一些知识,而且其封装的一些工具类可以简单实现一些高级功能,如HotSwaper。
3、ASM比javassist性能更快,灵活行也更高。
根据接收的故障代码对原始Class文件进行编辑,可以得到编辑后的Class文件。所述编辑后的Class文件已经具有了注入的故障代码。
更具体的,根据上述S230中定义的抽象类Transformer,在需要对该类的某个方法进行修改时,可以调用getMethod方法获得CtMethod类对应的实例method。CtMethod类提供了一系列修改方法的功能,如在指定行***代码、在方法执行前***代码、增加异常处理信息、添加局部变量、重置方法体等。当编辑完Class文件对应的字节码后,调用CtClass类的toBytecode方法重新生成该类的字节码文件,即可以完成字节码的修改工作。
Instrumentation接口是java.lang.instrumentation包的核心部分,该接口给出了类的转换与操作等方法。addTransformer方法可以注册ClassFileTransformer,而redefineClasses方法则可以重新定义Class。总控端可以保存Instrumentation接口的引用,这样,当有新的需要修改的Class时,需要重新实例化该类对应的Transformer子类,并注册该Transformer,随之调用retransformClasses方法。
Agent可以设置一个静态方法:agentmain方法。将这一个静态方法打成Jar包,并在MANIFEST.MF中将Agent-Class的值设置为Agent类名,并将Can-Redefine-Classes以及Can-Retransform-Classes的值设置为true。在程序执行过程中修改Class字节码文件,需要将Agent attach到指定Java进程中。需要通过调用VirtualMachine类的attache方法,并返回进程对应Java虚拟机VirtualMachine类的实例对象,随之调用该实例对象的loadAgent方法完成Agent的attach任务。当attach成功后,Java虚拟机会调用对应agentmain方法,达到在程序执行过程中修改Java虚拟机字节码的目的。
S250:总控端将所述编辑后的Class文件上传至所述Java虚拟机以替换所述原始Class文件。
本步骤中,具体的,总控端可以调用Java进程中的Agent,将编辑后的Class文件通过Agent上传至Java虚拟机,这样,原始的Class文件可以被替换为编辑后的Class文件。
进一步的,JVMTI通信模块中可以引用Transform类,总控端使用该引用,通过Agent与Java虚拟机中的JVMTI进行通信,可以将字节码编辑模块生成的Class文件上传至Java虚拟机。
S260:所述Java虚拟机解释执行所述编辑后的Class文件。
编辑后的所述Class文件,包含了注入的故障代码,从而在所述Java虚拟机解释执行所述编辑后的Class文件,可以完成故障注入,进而可以实现评测试待验证程序的容错性能以及稳定性或可靠性。
上述步骤执行之后,本申请实施例还可以包括:
S270:所述总控端将故障结果数据回传至所述客户端。
故障注入完成后,所述总控端可以将故障结果数据回传至所述客户端,以提示用户。具体的,总控端可以定义一个数据传输类TransferProtocol,该传输类可以由目标类名(targetClass)、目标方法(targetMethod)、***脚本(codes)、故障类型(faultType)、故障结果(faultResult)等组成。
并且,本申请通过在Java虚拟机启动后,监控所述Java虚拟机运行情况并取回所述加载的原始Class文件,通过编辑生成编辑后的Class文件以替换所述原始Class文件,可以实现Java虚拟机启动后不需要重新启动Java虚拟机的请情况下注入指定类型的故障,并可以保持报错时的上下文数据。
进一步地,使用本申请上述实施例,可以在Java程序字节码指定位置***代码段、调整控制语句执行逻辑、修改方法参数、迫使方法提前返回、抛出异常。
另外,现有技术中针对Java程序的一种故障注入工具,需要预先收集相应的故障信息,建立相应的故障库,从而在对class文件进行扫描匹配后,注入相应的故障代码,启动Java虚拟机执行相应的字节码文件。现有技术需要预先扫描Java文件,无法满足向字节码文件中注入任意故障代码的目的。该工具无法达到向正在运行中的Java虚拟机注入故障的目的。相对于上述现有技术,由于本申请不需要建立故障库,可以向字节码文件中注入任意故障代码。
本申请实施例提供还提供一种***,可以实现上述的方法步骤,且该***可以通过软件实现,也可以通过硬件或者软硬件结合的方式实现。以软件实现为例,作为逻辑意义上的***,是通过服务器的CPU(Central Process Unit,中央处理器)将对应的计算机程序指令读取到内存中运行形成的。
具体的,本申请一种Java程序注入故障的***,可以如图3所示,包括:
客户端31,用于接收输入的故障代码,并将所述故障代码传送至总控端;
总控端32,用于监控所述Java虚拟机所在Java进程的运行情况,取回所述Java虚拟机加载的原始Class文件;根据所述故障代码编辑所述取回的原始Class文件,生成编辑后的Class文件;将所述编辑后的Class文件上传至所述Java虚拟机以替换所述原始Class文件;
Java虚拟机33,启动后用于加载原始Class文件,并用于解释执行所述编辑后的Class文件。
在一个实施例中,优选的,所述客户端可以包括操作控制台和及通信模块,其中:
所述操作控制台作为用户提交故障代码的交互接口,接收用户下达的操作指令;
所述通信模块将故障代码上传至总控端。
在一个实施例中,优选的,所述总控端可以采用Socket方式与所述客户端进行通信。
在一个实施例中,优选的,所述客户端还可以包括脚本校验模块,用于验证用户编辑的脚本是否符合规范。
在一个实施例中,优选的,所述总控端可以通过JVMTI监控所述Java虚拟机的的运行情况,并在监控到所述Java虚拟机加载原始Class文件后,取回所述原始Class文件。
在一个实施例中,优选的,所述总控端设计为伺服进程。
在一个实施例中,优选的,所述总控端通过调用下面任一软件来实现对所述原始Class文件的编辑:
Javassit,BCEL,ASM。
在一个实施例中,优选的,所述总控端通过调用Java进程中的Agent,将编辑后的Class文件通过Agent上传至Java虚拟机,以替换所述原始Class文件。
在一个实施例中,优选的,所述总控端还可以包括回传模块,用于将故障结果数据回传至所述客户端。
在20世纪90年代,对于一个技术的改进可以很明显地区分是硬件上的改进(例如,对二极管、晶体管、开关等电路结构的改进)还是软件上的改进(对于方法流程的改进)。然而,随着技术的发展,当今的很多方法流程的改进已经可以视为硬件电路结构的直接改进。设计人员几乎都通过将改进的方法流程编程到硬件电路中来得到相应的硬件电路结构。因此,不能说一个方法流程的改进就不能用硬件实体模块来实现。例如,可编程逻辑器件(Programmable Logic Device,PLD)(例如现场可编程门阵列(Field Programmable GateArray,FPGA))就是这样一种集成电路,其逻辑功能由用户对器件编程来确定。由设计人员自行编程来把一个数字***“集成”在一片PLD上,而不需要请芯片制造厂商来设计和制作专用的集成电路芯片。而且,如今,取代手工地制作集成电路芯片,这种编程也多半改用“逻辑编译器(logic compiler)”软件来实现,它与程序开发撰写时所用的软件编译器相类似,而要编译之前的原始代码也得用特定的编程语言来撰写,此称之为硬件描述语言(Hardware Description Language,HDL),而HDL也并非仅有一种,而是有许多种,如ABEL(Advanced Boolean Expression Language)、AHDL(Altera Hardware DescriptionLanguage)、Confluence、CUPL(Cornell University Programming Language)、HDCal、JHDL(Java Hardware Description Language)、Lava、Lola、MyHDL、PALASM、RHDL(RubyHardware Description Language)等,目前最普遍使用的是VHDL(Very-High-SpeedIntegrated Circuit Hardware Description Language)与Verilog。本领域技术人员也应该清楚,只需要将方法流程用上述几种硬件描述语言稍作逻辑编程并编程到集成电路中,就可以很容易得到实现该逻辑方法流程的硬件电路。
控制器可以按任何适当的方式实现,例如,控制器可以采取例如微处理器或处理器以及存储可由该(微)处理器执行的计算机可读程序代码(例如软件或固件)的计算机可读介质、逻辑门、开关、专用集成电路(Application SpecificIntegrated Circuit,ASIC)、可编程逻辑控制器和嵌入微控制器的形式,控制器的例子包括但不限于以下微控制器:ARC625D、Atmel AT91SAM、MicrochipPIC18F26K20以及Silicone Labs C8051F320,存储器控制器还可以被实现为存储器的控制逻辑的一部分。本领域技术人员也知道,除了以纯计算机可读程序代码方式实现控制器以外,完全可以通过将方法步骤进行逻辑编程来使得控制器以逻辑门、开关、专用集成电路、可编程逻辑控制器和嵌入微控制器等的形式来实现相同功能。因此这种控制器可以被认为是一种硬件部件,而对其内包括的用于实现各种功能的装置也可以视为硬件部件内的结构。或者甚至,可以将用于实现各种功能的装置视为既可以是实现方法的软件模块又可以是硬件部件内的结构。
上述实施例阐明的***、装置、模块或单元,具体可以由计算机芯片或实体实现,或者由具有某种功能的产品来实现。
为了描述的方便,描述以上装置时以功能分为各种单元分别描述。当然,在实施本申请时可以把各单元的功能在同一个或多个软件和/或硬件中实现。
本领域内的技术人员应明白,本发明的实施例可提供为方法、***、或计算机程序产品。因此,本发明可采用完全硬件实施例、完全软件实施例、或结合软件和硬件方面的实施例的形式。而且,本发明可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、CD-ROM、光学存储器等)上实施的计算机程序产品的形式。
本发明是参照根据本发明实施例的方法、设备(***)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式处理机或其他可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。
这些计算机程序指令也可存储在能引导计算机或其他可编程数据处理设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令装置的制造品,该指令装置实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。
这些计算机程序指令也可装载到计算机或其他可编程数据处理设备上,使得在计算机或其他可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。
在一个典型的配置中,计算设备包括一个或多个处理器(CPU)、输入/输出接口、网络接口和内存。
内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器(RAM)和/或非易失性内存等形式,如只读存储器(ROM)或闪存(flash RAM)。内存是计算机可读介质的示例。
计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存(PRAM)、静态随机存取存储器(SRAM)、动态随机存取存储器(DRAM)、其他类型的随机存取存储器(RAM)、只读存储器(ROM)、电可擦除可编程只读存储器(EEPROM)、快闪记忆体或其他内存技术、只读光盘只读存储器(CD-ROM)、数字多功能光盘(DVD)或其他光学存储、磁盒式磁带,磁带磁磁盘存储或其他磁性存储设备或任何其他非传输介质,可用于存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括暂存电脑可读媒体(transitory media),如调制的数据信号和载波。
还需要说明的是,术语“包括”、“包含”或者其任何其他变体意在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、商品或者设备不仅包括那些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种过程、方法、商品或者设备所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排除在包括所述要素的过程、方法、商品或者设备中还存在另外的相同要素。
本领域技术人员应明白,本申请的实施例可提供为方法、***或计算机程序产品。因此,本申请可采用完全硬件实施例、完全软件实施例或结合软件和硬件方面的实施例的形式。而且,本申请可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、CD-ROM、光学存储器等)上实施的计算机程序产品的形式。
本申请可以在由计算机执行的计算机可执行指令的一般上下文中描述,例如程序模块。一般地,程序模块包括执行特定任务或实现特定抽象数据类型的例程、程序、对象、组件、数据结构等等。也可以在分布式计算环境中实践本申请,在这些分布式计算环境中,由通过通信网络而被连接的远程处理设备来执行任务。在分布式计算环境中,程序模块可以位于包括存储设备在内的本地和远程计算机存储介质中。
本说明书中的各个实施例均采用递进的方式描述,各个实施例之间相同相似的部分互相参见即可,每个实施例重点说明的都是与其他实施例的不同之处。尤其,对于***实施例而言,由于其基本相似于方法实施例,所以描述的比较简单,相关之处参见方法实施例的部分说明即可。
以上所述仅为本申请的实施例而已,并不用于限制本申请。对于本领域技术人员来说,本申请可以有各种更改和变化。凡在本申请的精神和原理之内所作的任何修改、等同替换、改进等,均应包含在本申请的权利要求范围之内。