项目报告
基于纠错码的容错技术的研究
——EVENODD码的设计与实现
由于网络技术的迅猛发展,存储系统的规模变得越来越庞大。因此它对系统的可靠性提出了严峻的挑战。而采用EVENODD编码算法的布局策略可以同时容许两个数据块同时出错,可以很好的保证系统的稳定性。它已经被广泛应用在RAID(Redundant Arrays of Independent Disks)等技术中。本论文从EVENODD编码原理出发,详细介绍了EVENODD的编码和译码过程,以及从理论上对该译码的算法进行了分析证明,同时使用java编译技术实现了该编码过程的仿真。在本论文中还对该仿真软件的设计思路、开发过程、以及主要功能模块的实现都进行了详细的介绍。EVENODD码仿真软件的实现是理论运用于实际的又一典范。通过对其编码和译码核心算法的调用,可以实现图片、二进制文件等格式的备份和恢复。
EVENODD编码 ;容错技术 ;系统稳定性; java编译技术
目 录
总页数: 31 页
随着企业信息系统的普及和整个社会电子商务的发展,现代企业的运作越来越依赖于信息技术。越来越多的关键数据被存储在计算机系统中,这些数据的丢失和损坏将对企业造成难以估量的损失。同时企业对于数据可用性的要求也大为提高,因为即使是短时间的系统停机也将造成业务停顿和经济损失。一旦IT系统和数据遭到灾难性打击,企业将面临破产的威胁,因此数据资料的完好保存是企业在灾难后能够继续生存的保证。
容错技术是保证系统稳定性的重要手段。容错是指一个系统在发生故障时仍能正确完成指定任务的能力。在硬件失效或软件错误的情况下,仍能够继续完成指定任务的系统称为容错系统。容错技术是指系统对故障的容忍技术,也就是指处于工作状态的系统中一个或多个关键部分发生故障或差错时,能自动检测与诊断,并能采取相应措施保证系统维持其规定功能或保持其功能在可接受的范围内的技术。所有的容错手段都必须依赖于“保护性冗余”,即依赖于系统中冗余的部件和算法。所谓“冗余”指的是如果系统是无缺陷的,那么这些部件和算法是不需要的。然而,EVENODD码理论的提出为容错技术的发展做出了重要的贡献。它以一种简单的方式越来越受到人们的青睐,并在各种系统中广泛使用,尤其是磁盘阵列布局方案中。其核心运算就是依据一定的规则将数据简单相异或。因此对EVENODD编码的研究及其实现具有很强的现实意义。
容错技术在存储系统中有着广泛的应用。目前,已有的适用于存储系统的容错技术主要有三种:2D奇偶校验编码方案(二维奇偶校验),RS(Reed-Solomon)码以及EVENODD码。
-
-
- 2D奇偶校验编码方案
-
2D奇偶编码的码字结构为n×n的二维阵列,总共有N2个信息位,其中校验信息位为2N个,即水平校验和垂直校验,对矩阵的行和列分别进行校验计算。例如,假定信息位X是两个不同的组C1和C2两个组的成员,在C1组超过k个信息位出错,但是C2中少于k个信息位出错,那么X可以通过C2来恢复。每个码字不是一个组的成员,而是多个组的成员,进行容错计算。图1显示的是一个二维编码的策略和二维奇偶校验码。
单个的奇偶校验能够容忍单个码字出错,二维校验可以容忍任意的两个码字出错,而且如果增加一个full parity sever,可以容忍达到三个码字出错。二维编码策略是通过增加冗余,增加服务器的容错能力。冗余数据的增加,必定会导致编
row parity |
full parity parity |
column parity |
图 1 二维奇偶校验编码 |
码和译码计算量的增加,及数据信息位和校验信息位之间的比之变化。
Reed Solomon(RS)是一类有很强纠错能力的多进制BCH码,也是一类典型的几何码。它首先是由里德(Reed)和索洛蒙(Solomon)应用MS多相式于1960年构造出来的。它不仅可以纠正突发错误, 还可以纠正随机错误, 特别适用于纠正信号的突发错误。Reed Solomon code 适合传送信息符号,而不是比特。RS虽然在六十年代就提出来了,但是实际得到应用差不多在八十年代。
在1994年,在RAID-6层,也称为(P+Q redundancy):数据以块为单位分割,然后采用编码技术为纠双错RS. 设每列信息位分别为,其两列校验信息位,两个校验列的编码方程为:
本论文采用EVENODD码实现存储系统的容错仿真。利用随意的5张图片模拟存储系统中存储的数据,然后利用EVENODD编码技术,生成2个校验数据存于另外存储设备中(即两张校验图片)。随机破坏其中的一张或者两张数据,利用EVENODD的译码算法将这2张图片的数据恢复出来。整个仿真过程将在一个界面友好的应用软件中实现。
为了方便本文后面的叙述,先定义本文一些符号记法:<n>m = j 表示 j ≡ n(mod m) (0 ≤ j ≤ m+1)。例如:<7>5 = 2,<-2>5 = 3。
另外我们还对本原理做一些必要的假设:
- 设存在m+2列数据块,其中前m个数据块依次存有信息,校验信息将存储在最后两列数据块。这样将在所有的数据块中形成冗余以避免在重复写操作时形成瓶颈。
- 设m列数据块中每一个数据块只有m-1行。对于任意容量的数据块,可以预先分割成m-1行的块分别进行处理。为简单起见,在本文中假设每一个标识位大小为1bit(不过在一些应用程序当中,一个标识位可能大到512字节)。
- EVENODD码中,m必须是素数,否则EVENODD码不是MDS码。因此若m不是素数,可使用如下方法将它构成素数:若存储任意数量的数据块而非必要的素数,通过增加不带任何信息的数据块达到m列的数据块,其中m为素数,那些多余的数据块所有信息位都为0。
- 为了译码描述方便,EVENODD码增加一行信息位,数据全为0。例如:am-1,j = 0(0 ≤ j ≤ m+1,根据这个假设,数组大小现在是m×(m+2))。
EVENODD码的码字放在一个(m-1)*(m+2)的阵列中,m是素数,其中信息放在(m-1)×m的阵列中,最后两列为奇偶校验信息符。两列奇偶校验位是分别通过同一行的信息位或者给定斜率对角线的信息位异或而构成的。
EVENODD code 的编码,设aij表示位为第i行第j列上的信息符,则奇偶校验符按下列规则进行构造:
从几何上看,S是由第m列开始沿斜率为1的信息位的异或构成的。第一列的各奇偶校验位正好是这一行m个信息位的异或运算,第二列的各奇偶校验位是各自沿斜率为1的信息位的异或,再和S异或共同构成的。
例1 下面以m=5为例显示一个(7,5)EVENODD的编码,如表1
表1 EVENODD编码
a1 | a2 | a3 | a4 | a5 | a1+a2+a3+a4+a5 | S+a1+b5+c4+d3 |
b1 | b2 | b3 | b4 | b5 | b1+b2+b3+b4+b5 | S+a2+b1+c3+d4 |
c1 | c2 | c3 | c4 | c5 | c1+c2+c3+c4+c5 | S+a3+b2+c1+d5 |
d1 | d2 | d3 | d4 | d5 | d1+d2+d3+d4+d5 | S+a4+b3+c2+d1 |
其中 S=a5+b4+c3+d2
- 从EVENODD编码的结构可以看出,2个奇偶校验列是独立得到的,当m是素数时,满足Singleton bound,是一类MDS,如m不是素数时,不能保证EVENODD具有MDS的性质,例如下面的这种情况(6,4)的情况,表2,码字之间的最小距离是2,若1,3列丢失,不能恢复原信息符。
表2 (6,4)数组
a | b | a | b | 0 | 0 |
b | b | b | b | 0 | 0 |
a | b | a | b | 0 | 0 |
- 在两列冗余校验中,其中有一列的冗余校验和参数S进行以后,参数S(m-1,1),(m-3,2),…(0,m-1)可能是奇或偶,也是由于这个原因,M.Blaum称这类阵列码为EVENODD code。如果参数S忽略,则不能保证EVENODD code的MDS性质。例如下面的(7,5)情况表3,码字之间的最小距离就是2,若第2,6列出错,是不能恢复的。
表3 (7,5)数组
b | b | b | b | b | b | b |
b | b | b | b | b | b | b |
b | b | b | b | b | b | b |
b | a | b | b | b | a | b |
-
- EVENODD码译码算法
在这一节将介绍EVENODD码纠双列删错的译码方法。这两译码算法没有有限域的计算操作,只需要简单异或操作,软硬件实现简单。下面简单介绍一下EVENODD的译码算法,
EVENODD code译码算法(Two Erasure Decoding Algorithm)
假定数据块i和j损坏,0≤i<j≤m+1,有下面四种情况:
- i=m,j=m+1,两个校验数据块遭到损坏,要恢复校验数据块,这和编码方式一样,只需要重新构造一次就行。
i<m,j=m,一个冗余校数据块和一个信息盘遭到损坏,为了能够重新恢复i数据块,首先恢复出参数S,
假定然后通过下面的公式,i数据块的信息符就可以重建
i<m,j=m+1,这种情况,i信息符数据块可以根据平行校验数据块进行异或,得到恢复- i<m,j<m,遭到损坏的是两个信息符的数据块,不像前面三种情况那么简单
首先将最后两列校验数据块的信息符异或,恢复参数S
然后计算,,其中
然后按照下面的顺序恢复i,j数据块的各个信息符
例:假设拥有下列初始数组,如表4。我们现在将利用译码算法恢复第0和2个错误数据块的数据。
表 4 初始数据
? | 0 | ? | 1 | 0 | 1 | 1 |
? | 1 | ? | 0 | 0 | 0 | 1 |
? | 1 | ? | 0 | 0 | 1 | 1 |
? | 1 | ? | 1 | 1 | 0 | 0 |
第一步要做的就是找到公共因子S,即将这个数组最后两列相异或。我们可以很容易地得到S = 1 。这意味着,对角校验是奇校验。根据数组和公式(8)、(9),我们可以得到校验数组:
= 0 1 0 1 0, = 0 1 0 1 0
现在我们开始一个递归来恢复丢失的数据和,0 ≤ ≤ 3。我们让,这时计算过程如表5。
表5 恢复数据过程
因为s = 4 = m-1,所以算法停止。通过以上的运行过程我们可以发觉在整个算法运行当中m必须是素数。如果m不是素数,将会出现递归在两列数据未恢复完全之前停止。我们将在后面的证明中很好地利用m是素数的这个条件。例如:如果m = 4(不是素数),接下来的数组如表6,它的权值为2(the following array is in the code and it has weight 2):
表6 m不为素数时的数组
1 | 0 | 1 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 | 0 |
这表明如果m不是素数,代码的最小距离为2 (the code has minimum distance 2)
下面我们将对EVENODD译码算法可以恢复两个数据块的数据进行证明。
定理一:EVENODD译码算法可以恢复任意2个数据块出错的数据。
证明:如果只有一列出错,我们可以利用剩余的正确校验位数据相异或进行恢复。根据异或的性质我们可以很容易知道这是完全可行的。因此,假设第i和j列出错,。我们假设译码算法的四种情况:
(1) i = m , j = m+1,这种情形和编码过程完全一样,很显然此算法可行;
(2) i〈 m , j = m,注意公共因子S的校验值是由公式(4)得到的,它由对角线位(<i-1>m,0),(<i-2>m,1),……,(i,m-1)上的数值相异或得到。这是一条唯一不与第i列相交的对角线(此时第i列无效)。因此所有的对角线都有相同的公共因子S。经过分析公式(2)得到的过程,我们可以推出能够找到恢复第i列数值的公式(5),一旦第i列的值被恢复,这个算法就可以利用公式(1)得到第m列的值。
(3) i < m , j = m+1,这种情形和编码的过程相似,在这里不再赘述。
(4) i < j < m ,第一步通过公式(7)计算出共同因子S,也就是公式(2)和公式(7)是等价的,但是公式(2)中存在未知的变量i,j的值,因此不能使用公式(2)直接得到共同因子S,因此根据公式(1)和(3)可以得到下面的
式子(也就是公式(7)):
因为m-1为偶数,所以(m - 1)S≡ 0 mod 2,又因为最后一个虚拟的m-1行所有的值都为0,即=0,因此可以得到:所有水平校验列的值异或,也就是所有的0到m-1列的数据(每列是m-1行),所有数据位异或。
所有的垂直校验列的值异或(除掉共同因子S),从0到m-1(每列m行,但最后一行的值为0),因此也就是所有的0到m-1列所有数据位异或。
因此:
所以我们可以获得:
公式(11)证明了公式(3) 、(7)都能给出公共因子S的值。
下面我们必须证明这个算法只能唯一恢复第i列和第j列的数据。假设水平校验组和对角校验组已经分别由公式(8)、(9)获得。
第一步给s赋初值s = <-(j - i)-1>m 得到s。
第二步计算:
该公式是正确的,通过公式(2)和公式(9)可以得出。一旦我们首先计算出,我们可以通过公式(13)计算出
公式(13)的正确性,根据公式(1)和公式(8)可以得到。
第三步,参数s的值设置为,返回到第2步,根据算法可以得到:
对于本软件的设计可以概述为以下三个方面:
(1)编码算法的设计是本软件要实现的核心内容,通过EVENODD编码原理利用5张原始图片的数据生成2张冗余的校验数据。
(2)译码算法是体现运用EVENODD原理能够胜任将出错的两个数据块的数据恢复的关键,也是体现了本原理在现实运用中具有的高可靠性和高稳定性。
(3)界面设计是实现友好的人机交互界面不可或缺的重要部分,EVENODD编码的实现过程和结果都要通过该界面表现和保存。
通过对上述内容的设计我们必须达到以下目标:
(1)满足用户对EVENODD编码过程的直观理解;
(2)创建友好的人机操作界面;
(3)满足用户对5张简单图片的备份;
(4)满足用户利用备份图片和尚未被破坏的原图片还原出错图片
EVENODD编码的实现 |
图像处理 |
编码 |
译码 |
图2 功能结构图 |
软件功能图如图2:
- 图像处理
为了方便EVENODD码的演示,本软件所选用的输入数据就是图片。在正式进行编码之前必须对图像进行一些必要的处理以符合算法的接口标准。因此,图像处理功能模块又可以分成以下几个子功能模块,如:图像载入、图像保存、图像破坏、图像分割、图像表达等。
-
- 图像载入:从本地计算机中读取图片文件,可以选择本地计算机中的任何文件夹。
- 图像保存:将经过处理后的图片保存在当前的文件夹中或者其他文件夹中。
- 图像破坏:将图片的数据全部破坏供译码使用。它是配合软件的仿真而单独设计的。
- 图像分割:从以图像格式显示的图片中读取像素并存储在二维数组中,供以后处理使用。
- 图像表达:像素经过处理后是存储在二维数组中的,该子功能的作用是将二维数组以图片的格式表达出来,与图像分割功能作用相反。
- 编码
编码功能是整个软件进行的关键,通过原始图片生成2张冗余校验图片,为译码过程的顺利进行做准备。
- 译码
该功能是在实际生活中运用最广的一部分,它通过尚未有数据损失的原始图像和通过编码形成的尚未遭到数据破坏的校验数据将已破坏的数据恢复出来(已破坏数据块的数目≤2)。
在该软件的实现过程中,基于面向对象程序设计思想,在实现过程中,把每一个待处理的数据看作一个对象,那么每个对象在实际的应用中也就是一张图片(也可以叫做一个数据块)。.根据EVENODD的编码原理,处理的数组大小m必须为素数,在整个软件的实现过程中取m= 5。由于以上介绍的编码过程都是针对单个数组的,所以在算法处理过程中,必须从每个对象中抽取一列组成单个的数据,将每个4×5大小的数组看作一个基本处理单元,然后按照编码原理进行算法的设计。
-
-
- VENODD编码算法
-
- 算法描述
EVENODD编码算法是产生冗余校验数据的核心算法。在实现该算法过程中,假设接口为5个数据对象,由于在该算法中实际操作对象是一个4×5大小的数组,首先面对的问题是如何将对象中的数据读出来并且将他们组合成符合规格的数组。基于上述情况,设计能从对象数组中获取某列的方法getColumnData(pictureInComp[] obj,int i)成为必要。利用该方法可以分别在每个对象的数据中获取相应列的数据并返回,组成一个符合规格的数组。这个数组就是最小的处理单元。根据公式(1)可以得到该处理单元产生的水平校验位的值,根据公式(3)不难得出公共校验因子S ,再由公式(2)对角校验值也就很容易的得到了。将该过程置于一个循环中依次处理对象的每一列,每列产生的水平校验值和对角校验值依照相反的方式重新组合便形成了两个冗余对象即水平校验对象和对角校验对象。
- 设计流程图
编码算法的设计流程图如图3:
图 3 编码设计流程图
- 算法实现
编码算法的实现如程序清单3—1
程序清单3-1 EVENODD编码算法
protected void encode(){
int[][] dataCache , tempMatrix1 , tempMatrix2;
tempMatrix1=
new int[tempMemory[0].getColumn()][tempMemory[0].getRow()]; //暂存运算过程中水平校验位的数据值的转置数据
tempMatrix2=
new int[tempMemory[0].getColumn()][tempMemory[0].getRow()];/暂存运算过程中对角校验位的数据值的转置数据
int[][] temp=
new int[tempMemory[0].getRow()][tempMemory[0].getColumn()];//充当数组转置时的临时变量
dataCache = new int[tempMemory[0].getRow()][tempMemory.length];
for(int i = 0 ; i < tempMemory.length ; i++){
dataCache = super.getColumnData(tempMemory, i);//获得对象元素中的数据中的每一列,组成一个数组
tempMatrix1[i] = super.horiExclusive_OR(dataCache);//获取水平校验(公式1)
tempMatrix2[i] =
super.diagExclusive_OR(dataCache, encoding.getCommonFactor(dataCache));
// 获取对角校验数据(公式2)
}
//将修复的数据存入对象
super.matrixTransposition(tempMatrix1, temp);//恢复原数据格式将数组转置
horiRedun = new pictureInComp(temp);//水平冗余校验对象实例化
super.matrixTransposition(tempMatrix2, temp);
diagRedun = new pictureInComp(temp);//对角冗余校验对象实例化
}
-
-
- EVENODD 译码算法
-
- 算法描述
EVENODD译码算法是EVENODD码中的关键,是使该码能从理论运用到现实的基础。它与编码算法配合使用,使用的前提是已经由编码算法产生出了冗余校验值。EVENODD译码算法最多能使2个出错数据块恢复。它是在尚未破坏的数据块的数据的基础上运用译码原理进行操作的。由于每个数据块被破坏的几率是均等的,不同的数据块被破坏,处理的方法也相应有所不同。根据EVENODD译码原理,我们可以把该算法分为两部分来实现:
(1)根据错误数据块号找到对应的处理方法。
(2)实现每种情形的处理方法。
与encode算法思想一样,我们同样由待处理的7个不同对象(5个原数据块,2个冗余校验数据块),获得最小数据单元,然后根据译码原理对最小数据单元进行处理。最后将每一个最小数据单元恢复的结果存储到错误数据块相应的位置。
- 设计流程图
EVENODD译码设计流程图如图4:
图 4(b) EVENODD译码流程图
图 4(c) EVENODD译码流程图
图 4(d) EVENODD译码流程图
图
- 算法实现
译码算法的实现如程序清单3—2
程序清单 3-2 EVENODD译码算法
protected void decode(){
if(error1 != -1 && error2 != -1){//有两个数据块出错
int[][] dataCache , tempMatrix1 , tempMatrix2;
pictureInComp[] corectObj;//暂存正确数据块的数据
tempMatrix1 =
new int[tempMemory[0].getColumn()][tempMemory[0].getRow()];
tempMatrix2 =
new int[tempMemory[0].getColumn()][tempMemory[0].getRow()];
int [][] temp =
new int[tempMemory[0].getRow()][tempMemory[0].getColumn()];//充当数组转置时的临时变量
if(error1 == tempMemory.length - 2 && error2 == tempMemory.length -1){//两个校验数据块出错
corectObj = new pictureInComp[tempMemory.length - 2];
System.arraycopy(tempMemory,0,corectObj,0,tempMemory.length-2);
//将正确的对象数组拷贝到correctObj中
dataCache = new int[tempMemory[0].getRow()][corectObj.length];
for(int i = 0 ; i < corectObj.length ; i++){
dataCache = super.getColumnData(corectObj, i);//获得对象元素中的数据中的每一列,组成一个数组
tempMatrix1[i] = super.horiExclusive_OR(dataCache);//修复数据块error1;
tempMatrix2[i] =
super.diagExclusive_OR(dataCache, encoding.getCommonFactor(dataCache));//修复数据块error2;
}
/**将修复的数据存入对象*/
super.matrixTransposition(tempMatrix1, temp);
tempMemory[error1].read(temp);
super.matrixTransposition(tempMatrix2, temp);
tempMemory[error2].read(temp);
}else if((error1 >= 0 && error1 < tempMemory.length - 2) && error2 == tempMemory.length - 2 ){//原数据块和水平校验数据块的数据出错
int s = 0;
int m = tempMemory.length - 2;
tempMatrix1 =
new int[tempMemory[0].getRow()][tempMemory[0].getColumn()];
int[][] tempCache =
new int[tempMemory[0].getRow()][tempMemory.length];
dataCache =
new int[tempMemory[0].getRow() + 1][tempMemory.length];//存储增加一行的数据
for(int i = 0 ; i < tempMemory[0].getColumn(); i++){
tempCache = super.getColumnData(tempMemory, i);//获取对象数组中每一个元素对应列的数据
dataCache = super.addRow(tempCache);//增加一行
/**---计算公共因子---------*/
s = dataCache[(error1 - 1 + m)%m][m + 1];
for(int l = 0 ; l < m ; l++){
s = s ^ dataCache[(error1 - l - 1 + m)%m][l];
}
/**--------恢复第error1 个数据块数据--------*/
for(int k = 0 ; k < m - 1 ; k++){
tempMatrix1[k][i] = s ^ dataCache[(k + error1 + m)%m][m + 1] ;
for(int l = 0 ; l < m; l++){
if(l != error1){
tempMatrix1[k][i] =
tempMatrix1[k][i] ^ dataCache[(k + error1 -l + m)%m][l];
}
}
}
}
tempMemory[error1].read(tempMatrix1);//修复数据
/**------恢复第error2 个数据块的数据-------*/
corectObj = new pictureInComp[m];
System.arraycopy(tempMemory, 0, corectObj, 0, m);
dataCache = new int[tempMemory[0].getRow()][m];
for(int i = 0 ; i < m; i++){
dataCache = super.getColumnData(corectObj, i);
tempMatrix2[i] = super.horiExclusive_OR(dataCache);//水平异或
}
super.matrixTransposition(tempMatrix2, temp);
tempMemory[error2].read(temp);
}else if((error1 >= 0 && error1 < tempMemory.length - 2) && error2 == tempMemory.length - 1){//原数据块和对角校验数据块出错
int m = tempMemory.length - 2;
/**-------修复数据块error1-------------*/
corectObj = new pictureInComp[m];
dataCache = new int[tempMemory[0].getRow()][m];
for(int i = 0 , j = 0; i < tempMemory.length - 1; i++ ){//获取前m+1行的正确数据
if(i != error1){corectObj[j++] = (pictureInComp)tempMemory[i].clone();}
}
for(int i = 0 ; i < m ; i++){
dataCache = super.getColumnData(corectObj, i);
tempMatrix1[i] = super.horiExclusive_OR(dataCache);
}
super.matrixTransposition(tempMatrix1, temp);
tempMemory[error1].read(temp);
/**------修复数据块error2---------------------*/
System.arraycopy(tempMemory, 0, corectObj, 0, m);
for(int i = 0 ; i < m ; i++){
dataCache = super.getColumnData(corectObj, i);
tempMatrix2[i] =
super.diagExclusive_OR(dataCache,encoding.getCommonFactor(dataCache);
}
super.matrixTransposition(tempMatrix2, temp);
tempMemory[error2].read(temp);
}else if((error1 >= 0 && error1 < tempMemory.length - 2)&&(error2 >= 0 && error2 < tempMemory.length - 2)){//两个原数据块出错
int m = tempMemory.length - 2;
dataCache = new int[tempMemory[0].getRow() + 1][m + 2];
int[][] tempCache = new int[tempMemory[0].getRow()][m + 2];
tempMatrix1 =
new int[tempMemory[0].getRow()][tempMemory[0].getColumn()];
tempMatrix2 =
new int[tempMemory[0].getRow()][tempMemory[0].getColumn()];
for(int i = 0 ; i < tempMemory[0].getColumn() ; i++){
int s = 0 ;
int[] sh = new int[m];
int[] sd = new int[m];
int k = 0;
tempCache = super.getColumnData(tempMemory, i);
for(int j = 0 ; j < tempCache.length ; j++)
s = s ^ tempCache[j][m] ^ tempCache[j][m + 1];//求公因子 s :将所有校验位相异或
dataCache = super.addRow(tempCache);
/**--求sh、sd(公式 3-7、3-8)--------*/
for(int u = 0 ; u < m ; u++){
for(int l = 0 ; l <= m ; l++)//求sh
if(l != error1 && l != error2){sh[u] = sh[u] ^ dataCache[u][l];}
sd[u] = s ^ dataCache[u][m + 1];
for(int l = 0 ; l < m ; l++){
if(l != error1 && l != error2){
sd[u] = sd[u] ^ dataCache[(u - l + m)%m][l];
}
}
}
/**------修复数据--------------*/
k = (m - (error2 - error1) - 1) % m;
while(k != m - 1){
dataCache[k][error2] =
sd[(error2 + k + m)%m] ^ dataCache[(k + error2 - error1 + m)%m][error1];
dataCache[k][error1] = sh[k] ^dataCache[k][error2];
k = (k - (error2 - error1) + m) % m;
}
for(int j = 0 ; j < tempMatrix1.length ; j ++){//数据转移
tempMatrix1[j][i] = dataCache[j][error1];
tempMatrix2[j][i] = dataCache[j][error2];
}
}
tempMemory[error1].read(tempMatrix1);
tempMemory[error2].read(tempMatrix2);
}else{
System.out.println("error : fail to find the error disk !!");
System.exit(0);
}
}else if(error2 == -1 && error1 != -1){//只有一个数据块出错
int m = tempMemory.length - 2;
int[][] dataCache = new int[tempMemory[0].getRow()][m];
int[][] tempMatrix =
new int[tempMemory[0].getColumn()][tempMemory[0].getRow()];
pictureInComp[] corectObj = new pictureInComp[m];
int [][] temp =
new int[tempMemory[0].getRow()][tempMemory[0].getColumn()];//充当数组转置时的临时变量
if(error1 < tempMemory.length - 1 && error1 >=0){//出错数据块是水平校验位或者原数据
for(int i = 0 ,j = 0; i < m + 1 ; i++){
if(i != error1){
corectObj[j++]=(pictureInComp)tempMemory[i].clone();
}
}
for(int i = 0 ; i < corectObj.length ; i++){
dataCache = super.getColumnData(corectObj, i);
tempMatrix[i] = super.horiExclusive_OR(dataCache);
}
super.matrixTransposition(tempMatrix, temp);
tempMemory[error1].read(temp);
}else if(error1 == tempMemory.length - 1){//出错数据块是对角校验位数据块
System.arraycopy(tempMemory, 0, corectObj, 0, m);
for(int i = 0 ; i < corectObj.length ; i++){
dataCache = super.getColumnData(corectObj, i);
tempMatrix[i] =
super.diagExclusive_OR(dataCache,encoding.getCommonFactor(dataCache);
}
super.matrixTransposition(tempMatrix, temp);
tempMemory[error1].read(temp);
}else{
System.out.println(" error : fail to find the error disk !!");
System.exit(0);
}
}else{//错误数据块不能找到
System.out.println(" error : fail to find the error disk !!");
System.exit(0);
}
}
本软件的编码和译码功能是通过分别调用编码和译码算法实现的。因此在实现该功能时必须实现该算法的接口。
通过对“3.3.1 EVENODD编码算法”的介绍我们可以知道,编码算法的接口是一个pictureInComp对象数组。在编码功能的实现过程中,我们必须把输入的图片处理成为适合该接口的数据。因此后面的过程显得相当的必要。由于输入数据是图像的格式,首先我们必须利用图像分割功能将该处理图像的像素读出来并创建一个pictureInComp对象存入其中。从而使每个待处理图像组成一个对象数组。由于,每个对象数组的大小要远远大于encode算法中所要求的4×5数组。因此在调用该算法之前,我们必须依次从每个对象中取出4×5大小的数组并再次组成一个对象数组,调用encode算法进行处理。
经过编码算法处理后得到的数据将是一个带有4×5大小数据的对象,并不是一张完整的图片,因此我们必须将这些零碎的小数组重新组合到一个与原对象大小相同的对象中,直到图片对象的每个像素处理完为止。这样才能通过图像表达功能显示出与原数据大小相符的冗余图像。
在此值得说明的一点是,在图片处理过程中像素是由RGB值构成的,我们进行的对像素的编码实际上是对RGB值编码,编码后得到的冗余校验数据将重新组合成新的像素。
编码功能接口的流程图如图 5:
译码功能接口的实现与编码功能类似,他们处理的对象都是图片格式的。唯一不同点就是在调用译码算法之前,我们必须从复选框中得到出错图片的编号。并且在获取图片像素时,针对出错图片的处理方法是直接将出错图片的数据重新
赋为全部值是0的最大数组。3.3.2节介绍了EVENODD译码算法的设计,从上面的叙述我们可以知道,译码算法的接口有两种情况即decode(pictureInComp[],int)和decode(pictureInComp[],int,int)。因此在调用该方法是我们要针对不同的出错图片数调用不同的方法。
译码功能接口实现流程图如图6:
图
“基于纠错码的容错技术之EVENODD码设计与实现” 软件是基于java编译技术实现的,因此该软件可以在任意操作环境中运行。
在运行该软件时首先出现在用户面前的是一个十分友好的用户界面,如图7。
很明显,该软件主要分为菜单栏、快捷键栏和数据显示域三部分。其中快捷键栏中的快捷按钮功能在菜单栏中均能找到。下面我们对该软件的主要操作过程及测试结果做一个简单的介绍。
图7 软件操作主界面
首先我们在选择要打开的文件之前必须给打开的文件选择存放的区域,即在数据显示域的复选框中选择。否则系统将对弹出对话框对你的操作进行提示,如图8。在选择了显示的区域后我们按照以下的步骤可以顺利打开文件。单击“File” → 选择 “open”或者直接点击open按钮就会弹出一个文件选择对话框,如图9。在选择文件后,图片显示如图10所示:
图8 打开文件出错提示
图9 选择文件对话框
图10 打开图片显示效果
在对数据显示域data1、data2、data3、data4、data5都添加了数据后,我们就可以按照下列顺序生成冗余校验图片了。单击“Edit” →选择“Encode”或者单击encode按钮。注意:在执行该功能之前一定要将data1、data2、data3、data4、data5的5个复选框选中,否则系统会提示你选择复选框。执行完该功能后生成的冗余校验图片将显示在redundancy Data数据域中,如图11:
图11 编码结果
为了体现演示的效果,在执行译码操作之前我们必须将要译码的数据破坏。因此我们可以按照下列步骤执行该功能。选择将要破坏的图片对应的复选框,单击“Edit” →选择“Data_Destroy”或者单击destroy按钮。Data2数据域破坏后的执行结果如图12:
图12 data2数据域图片破坏
译码操作后的主要效果就是让图片恢复正常,它的主要操作步骤如:先选中要恢复的图片的数据域,然后单击“Edit” →选择Decode”或者单击decode按钮。在此值得注意的是选择恢复数据的复选框最多为2个。其执行后的效果图如图11。
该软件中还有保存、另存为、退出、帮助等功能块,这些功能都很直观在此不再赘述。
结 论
在这篇文章中我们对EVENODD码的理论进行了详细的阐述,可以肯定EVENODD码可以很好的实现将小于两个数据块数的出错数据恢复出来,并且我们可以相信它在维护各种系统的稳定性方面大有用武之地。EVENODD码的实现过程在这篇文章的后半部分也展开了说明,在理解了它的原理后,我们可以很快的在软件中将它实现出来。在整个课题实现期间,首先我们围绕着怎样使EVENODD码的原理变得更加通俗易懂,其次我们着重在它的实现过程上花了大量精力,从整体结构到每个功能模块,再到对应每个模块的的接口方法都经过了精心的规划和设计。我们希望让读者感觉到,即使是采用一些很简单算法依然能够实现很复杂的系统,真正困难的是系统结构的设计,并不是代码的实现。
参考文献
[1]Y.Daniel Lang.java编程原理与实践(第4版)[M].北京:清华大学出版社,2005。
[2]王宏,赵海滨.数字图像处理——java语言实现[M].沈阳:东北大学出版社,2005。
[3]Marc Loy,Robert Eckstein,Dave Wood,James Elliott Brian Cole.Java Swing(第2版)[M].北京:清华大学出版社,2004。
[4]Cay S.Horstmann Gary Cornell.最新Java 2 核心技术 卷Ⅱ:高级特性(V1.3)5E[M].北京:机械工业出版社,2003。
[5]冯丹,张江陵.适用于磁盘阵列的纠双错码分析 Analysis of Codes for Tolerating Double Disk Failures in RAID Architectures[J].电子学报 Vol.26,No.5,1998,(5):93—96。
[6]范剑波,徐利浩.p分布式存储系统可靠性的研究[J].计算机工程 Vol 27, No.6, 2001,(6):169-186。
[7]周杰,刘晓光,王刚.基于镜像和奇偶校验容许两个盘故障的磁盘阵列数据布局[J].计算机工程与应用2002,(18):82-85。
[8]周杰,王刚,刘晓光,刘璟.容许两个盘故障的磁盘阵列数据布局与图分解的条件和存在性研究[J].计算机学报 Vol.26,No.10 2003(10):1379-1386。
[9]董欢庆,李战怀,林伟.RAID2-VCR:一种能够承受三个磁盘故障的RAID结构[J].计算机学报 Vol.26,No.5,2006,(5):792-800。
[10]Mario Blaum,Jehoshua Bruck,Jai Menon.an efficient scheme for tolerating double disk failures in RAID architectures[J].IEEE TRANSACTIONS ON COMPUTER,VOL.44, 1995,(2):192-202。
[11]王刚,李中,刘晓光,刘璟.异构磁盘阵列RAID5结构数据布局研究[J].计算机工程与应用,2004,(29):54-57。
[12]Lihao Xu,Highly Available Distributed Storage Systems[J].California Institute of Techology Pasadena,California 1999 Nov.3。