全部博文(95)
分类: 服务器与存储
2009-05-08 14:48:34
如何使用RPN
**********************************************************************************************************************
注 :该教程参考了如下内容 :
A)官方文档 :
B)abel 兄的大作 :
作者 :ailms
最后修改 :2006/11/17 17:35
**********************************************************************************************************************
一)前言
RPN 代表逆波兰式(Reverse Polish Notation)。逆波兰式最早于1920年由Jan Lukasiewicz 发明,最神奇的地方是用它来表示数学表达式,
完全不需要括号。而且 RPN 不像普通的数学表达式那样,操作符在操作数的中间,而是在操作数的右边。例如 3+2 用 RPN 表示就是 3,2,+ ;
3+(2X5) 用 RPN 表示就是 3,2,5,x,+ 最后运算的部分(加法部分)的操作符放在最后,乘号放在前面,表示先执行 2 x 5 ,在把结果和3相加。
在 RRDtool 中,RPN 还可以用来表示 if-then-else 关系。这点在绘图中很有用。例如你要看 eth0 接口在一天当中流量 ≥ 10Mb/s 的部分,“隐藏”
其他低于 10Mb/s 的部分,则可以用到这个功能。
二)操作符
什么是 RPN
A)RPN 是 Reverse Polish Notation 的缩写,是用于表示算术运算和逻辑运算的一种语法格式。
B)RRDtool 的 CDEF 语句中就经常使用 RPN 来对 DEF 取出来的数据进行运算。
C)RPN 的特点是操作数和操作符出现的顺序和运算的顺序一致,这样就不需要使用括号了
D)RPN 的格式是
E)RPN 需要提到堆栈的概念(stack)。堆栈是用来存储操作数和操作符的。
F)当堆栈中压入(push)一个操作符时,就从堆栈中取出(pop)所需要的操作数进行计算(根据操作的不同pop出不同数量的操作数)。
结果再返回(push)堆栈,最终整个 RPN 应该只返回一个值,或者说堆栈中只有一个元素
G)在 CDEF 中书写 RPN 操作符,要一律以大写的格式出现
H) RPN 中,如果某个部分的运算结果非 0,则被认为是 true ,只有 0 才被认为是 false
三)RPN 操作符的分类
A)布尔操作符 :GT、GE、LT、LE、EQ、NE、
B)特殊值比较符 :UN、ISINF、
C)条件操作符 :IF
D)比较操作符 :MIN、MAX、LIMIT
E)算术操作符 :+ 、-、*、/、%、SIN, COS, LOG, EXP, SQRT、FLOOR, CEIL、ATAN、ATAN2、DEG2RAD, RAD2DEG
F)数据集操作符 :所谓数据集(sets),就是指多个数据。SORT、REV、AVG、TREND
G)特殊值 :UNKN、INF、NEINF、PREV、COUNT
H)时间操作符 :NOW、TIME、LTIME
I)堆栈操作符 :POP、DUP、EXC
四)RPN 操作的结果
A)布尔操作符 :从堆栈中 pop 出两个元素,并根据比较结果返回 0 (false) 或者 1 。任何同 UNKNOWN 或者 INF 、NEINF 比较的都为 0
B)特殊值比较符 :从堆栈中 pop 出1个元素,并同 UNKNOWN 或者 INF、NEINF 比较。结果为 0 或者
C)条件操作符 :从堆栈中pop出3个元素,如果最后pop出的那个元素不为0(条件部分为 true),则第2次 pop 出的那个元素被重新入栈(then部分);
否则第一次pop出的元素重新入栈(else部分)。结果为 then 部分或者 else 部分返回的值,不一定为0或者1
D)比较操作符 :
对于 MIN/MAX 操作符来说,从堆栈中 pop 出两个操作符,并把较大/小的那个重新入栈。如果其中有一个 unknown ,则结果为 unknonw
对于 LIMIT 操作符来说,先从堆栈中 pop 出2个操作数,作为边界的定义;再 pop 出1个操作数,比较该操作数是否落在前面定义的范围内。
如果是则把最后 pop 出的那个元素重新入栈;否则把 UNKN 值入栈;注意,是 UNKN ,不是 0
E)算术操作符 :根据操作符 pop 出所需数量的操作数,并把算术运算的结果重新入栈
F)数据集操作符 :
对于 SORT、REV 来说,先从堆栈中 POP 出一个元素,该元素的值就是下面要 pop 出的元素的数量。然后对堆栈从上到下的若干个元素
由第一次 pop 的出的那个元素的值决定)进行排序/反向排序。结果再重新入栈。
注意 :由于堆栈的特点是后进先出,所以要操作的元素是从SORT操作符往左方向计数。例如v1,v2,v3,v4,3,SORT是对 v2~v4 排序,
不是对 v1~v3 排序。 这点在书写 RPN 时要特别注意。
注意 :SORT 操作是最小值在堆栈的最顶部;REV 则相反,最小值是在堆栈的最顶部。
对于 AVG 操作来说,同样是先 pop 出1个元素,并按照指定的数量对后续的若干个操作数进行操作,但结果只有一个数值,并入栈。
G)特殊值 :
UNKN 表示压入一个 UNKN 值;INF、NEINF 分别表示把 INF、NEINF 值压入堆栈
H)时间操作符 :
TIME 返回当前所提取的记录的 timestamp ,注意 TIME 直接返回当前记录的 timestamp ,不用任何参数
NOW 返回当前时间,同样 NOW 不用任何参数
I)堆栈操作符 :
POP :弹出堆栈的最顶部的那个元素
EXC :交换堆栈顶部的第一个和第二个元素的值
五)如何阅读 RPN
A)首先按照从左到右的顺序,找出第一个 RPN 操作符,并根据上一节的内容,对相应的操作数进行操作
B)操作结果分成两种 :
如果是一个值,直接替换掉该部分 RPN
如果是多个值(数据集操作,但 AVG 操作只返回一个值),则结果可能为多个数值。则把这若干个数值用 ‘,’ 隔开,替换原来那部分 RPN
C)如此循环,一直到整个 RPN 只返回一个值为止
六、RPN 实例
A)布尔型操作符 :2,1,GE 表示 2>=1 ;
B)特殊值比较符 :mydata,UN 表示 mydata == UNKNOWN
C)条件操作符 :mydata,UN,0,mydata,IF 表示如果 mydata 等于 UNKNOWN ,则返回 0;否则还是返回 mydata 本身
D)比较操作符 :mydata,20,MAX 表示返回 mydata,20 这两个数值中较大的一个;alpha,0,100,LIMIT 表示测试 alpha 的值是否小于等于0,大于等于100;
E)算术操作符 :1,2,- 表示 1-2=
F)数据集操作符 :
v1,v2,v3,v4,v5,4,SORT 表示对 v1~v4 进行正向排序,结果堆栈中还是有5个元素;
v1,v2,v3,v4,3,AVG,+,2,/ 表示对 v4,v3,v2 进行求平均值,并把结果入栈。假设v2~v4的结果为 k ,则为 v1,k,+,2,/ 也就是返回 (v1+k)/
G)特殊值 :mydata,0,GT,UNKN,mydata,IF 表示如果 mydata 大于 0则返回 UNKNOWN ,否则还是 mydata
H)时间操作符 : TIME,`date –d “2006-10-01 10:
I)堆栈操作符 :POP 就立即弹出第一个元素
七)如何表示 AND、OR 关系
A)我们知道 RPN 表达式的值除非0,否则都认为是 true
B)我们可以利用 加法操作和乘法操作来实现 OR 和 AND 的逻辑关系;如果两个 RPN 表达式的值相加不等于0,就一定为 true ;
如果两个 RPN 表达式的值相乘不等于0,就一定为 true
C)AND 关系的例子 :例如要比较某个值(15,9)是否在特定范围内可以用 :
15,10,GT,15,20,LT,* ,结果就是(15>10)*(15 < 20)= 1 * 1 =1 ,所以为 true
9,10,GT,9,20,LT,* ,结果就是 (9 >10)* ( 9 < 20)= 0 * 1 =0 ,所以为 false
D)OR 关系的例子:同上例如要比较某个值(7,15)小于10,或者大于20:
7,10,LT,7,20,GT,+ ,结果为 (7 < 10)+ (7 > 20) = 1+0 = 1,所以为 true
15,10,LT,15,20,GT,+ ,结果为 ( 15 < 10) + ( 15 > 20) = 0 + 0 =0 ,所以为 false
E)不过使用 + 需要注意一个地方 :相加的双方都必须是正数,否则可能出现问题,例如一个正数(-5,true)和另外一个正数(5,true)相加为0(false)
如果是按照 OR 的关系,应该是 true 的,但结果变成0(false),所以在使用 + 来表示 OR 的关系时,要注意该问题
F)使用 * 则没有该问题了,正数 * 负数 = 负数 (true)。所以如果遇到 OR 关系的时候,可以转换为 AND 关系来计算。
例如要表达 (x < a) OR ( x > b) 的关系,可以改为 (x >a) AND ( x < b ) ,诀窍就是把比较操作符调反方向,把 + 改为 *
八)实例
实例1:例如要看 eth0的总流量,可以用如下的定义
CODE:
DEF:value1=eth0.rrd:eth0_in:AVERAGE \
DEF:value2=eth0.rrd:eth0_out:AVERAGE \
CDEF:value3=value1,value2,+ \
AREA:value3#ff0000:”total”
实例2 :假设我们要把 eth0 和 lo 的流入流量相加,得出总的流入流量
CODE:
DEF:value1=eth0.rrd:eth0_in:AVERAGE \
DEF:value2=lo.rrd:lo_out:AVERAGE \
CDEF:value3=value2,UN,0,value2,IF \
CDEF:value4=value1,value3,+ \
AREA:value4#00ff00:”total in”
由于 lo.rrd 一直没有数据插入,所以一直都是 NaN ,如果直接把 value1 和 value2 相加,由于 value2 是 UNKNOWN,
所以相加的结果也是 UNKNOWN 。图表上将什么都不显示,所以需要对 value2 进行判断,如果 value2 的值 UNKNOWN (value2,UN),
则返回0,否则返回 value2 本身。然后把这个值赋予变量 value3 ,最后把 value1 和 value3 相加,才得出真正入流量
实例3 :只看 eth0 中流量大于 10Mb/s 的部分,其余不看
CODE:
DEF:value1=eth0.rrd:eth0_in:AVERAGE \
DEF:value2=eth0.rrd:eth0-_out:AVERAGE \
CDEF:value3=value1,1000000,GT,value1,UNKN,IF \
CDEF:value4=value2,1000000,GT,value2,UNKN,IF \
AREA:value3#00ff00:”traffic_in \>
AREA:value4#ff0000:”traffic_out \> 10Mb\/s”:STACK
实例4 :只绘制特定时间段(在 2006/11/29 10:30 ~ 2006/11/29 12:30)的数据
CODE:
DEF:value1=eth0.rrd:eth0_in:AVERAGE \
DEF:value2=eth0.rrd:eth0_out:AVERAGE \
CDEF:value3=TIME,$(date –d ‘2006-11-29 10:
CDEF:value4=TIME,$(date –d ‘2006-11-29 12:
AREA:value3#00ff00:”traffic_in” \
AREA:value4#ff0000:”traffic_out”:STACK