负暄琐话

我的email: rot47('649@ 6(hF+`hd"w=92vhG{>}G3"@l M >:>6?4@56 \F')

囧囧ID:g9yuayon
[修改头像]
777261次访问,排名36好友9人,关注者11
g9yuayon的文章
原创 238 篇
翻译 4 篇
转载 48 篇
评论 790 篇
g9的公告
最近评论
alextooter:ff可是用的完全的自己内存管理。。

最近ff3非常好,值得试试看。
fferror:无比期待g9老大的model checking续集。
sprhawk:哈哈,太强了,最近刚听说有Erlang,了解一下。
看一下历史,比看语法有意思得多嘿嘿
lordchl:后面提示下不动点好了,跳得太大
lbaby:Do you mind if I jot down some notes on your chests ??


猛到无以复加
文章分类
收藏
    相册
    旅游
    计算机科学
    Lambda the Ultimate
    软件开发
    Reddit编程专栏(RSS)
    正在读的书
    存档
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 八卦一下模型检验(二)

    新一篇: 采访Joe Armstrong的podcast

    接着八卦前先回答老大们直指灵魂的问题。不就是系统状态遍历的问题么?干嘛非得用什么时序逻辑、模型一类的形式化手段啊?搞得比陈凯歌还深沉。做人不能这么无耻不是?找个真正的程序员,放出手里的蝴蝶不就搞定了?


    真正的程序员用蝴蝶编程

    嗯,很多程序的确可以靠程序达人强大的自觉和天才的排错能力搞定。问题是,模型检验的对象是高并发复杂系统(比如说1020个状态),目标是绝对可靠地查出系统的错误,既不错杀三千,也不放过一个。这些系统失败时的代价也高昂。奔腾94年的FDIV错误花掉Intel至少5亿美元。偏偏我们对并发系统编程也没有什么特别有效的手段,不然大家也不至于对Heisenbug津津乐道了。我们在这种情况下怎么能全靠自己的直觉?何况直觉有时相当不可靠。在这篇让人崩溃的论文问世前,谁能想到在一个异步多进程拥有可靠网络的的分布系统中,哪怕一个出错的进程就能所有进程无法通过消息传递对一个值达成共识呢?当我们需要确保设计无错的时候,形式推理非常称手的工具。有些老大可能不知道,我们只所以能放心使用常用的数据结构和算法,多少也因为那些算法经过了严格的证明。当初Purely Functional Data Structure这本牛书值得一篇博士毕业论文,也是因为证明纯函数的数据结构的正确和性能颇费周折。另外,模型验证还在发展中,我们需要回答很多本质问题,比如模型验证到底能解决多大规模的问题,到底能具备什么样的效率,到底能处理什么样的系统。这些问题都需要高效而严格的推理。这个时候,形式化工具就派上用场了。

     

    继续八卦。上次说到Pnuelli提出用时序逻辑描述系统的性质,打开了自动验证系统的方便之门。不过光有逻辑表达式没用,我们还需要系统。有了系统,我们才能给时序逻辑的断言赋予意义,才能进行推理和证明。比如说,LTL断言P -> F(S)本身并无意义,但如果我们知道这段公式描述的是公寓的电梯系统,P表示有人在一楼按了电梯按钮,而S表示电梯到达一楼,我们立刻知道这条断言的意思是当有人按了一楼的电梯后,一定会有电梯到达一楼,而且可以进一步根据电梯的设计来证明这条断言是否永远为真。接下来的问题自然是,怎么描述系统?显然直接上电路设计图或者程序代码并不现实。它们包含太多与系统验证无关的细节,实在不适合人肉推理。我们需要的是剥离了具体细节,但又能形式化描述系统本质的抽象东东,也就是切口所谓的模型。好比regex能匹配千奇百怪的字符串,但它的模型却是淳朴的有限自动机。把系统转换为这套形式化抽象描述的过程,叫建模。

     

    模型验证里与时序逻辑配套的模型,叫Kripke StructureKripke StructureCS里强大的工具,结构简单,却能简洁精准地描述各式并行系统。Kripke Structure由美国逻辑学家Saul Aaron Kripke1963年前后提出。K老师是早慧天才。小学四年级读完莎士比亚全套戏剧后,就开始追问诸如“如何知道自己真实存在”这类恐怖问题。关于“我”的讨论是K老师毕生兴趣之一。这点和台湾陈老师相反。按理说儿童5岁(还是三岁?)前没有自我意识,所以不会用“我”自称,而用名字代替。比如二狗想吃冰淇淋,5岁前只会说“二狗想吃冰淇淋”,而5岁后说“我想吃冰淇淋”。偏偏知天命多年的陈老师专爱在群众大会上水扁长水扁短,声音还拖得忒长忒绵软。只能说品味与人囧异。当K老师问他爹“how do I know I’m not dreaming?”的时候,当犹太牧师的老爹告诉他,这个问题嘛,笛卡尔已经讨论过了。于是K老师从12岁起读笛老师的大部头,开始哲学研究,16岁就写出了关于模态逻辑的论文,讨论模态逻辑完备性定理。在哈佛上大二的时候就在MIT教研究生逻辑。著名的K Logic便是以他名字命名。K老师毕业后继续走天才路线,在抽象数学,哲学推理,主观句式,语言哲学,维特根斯坦的思想等方面贡献卓著,写了大量俺肯定看不懂的著作,包括对nature of being这种终极问题的深入讨论。K老师颇有人类早期哲人风范,不同于当代入世颇深的大牛们,比如萨特,罗素,或者乔姆斯基。他研究哲学的强大动力完全出自不可抑制的好奇心,跟现实彻底脱节。用K老师自己的话说The idea that philosophy should be relevant to life is a modern idea.  A lot of philosophy does not have relevance to life

     

    Kripke Structure本质是不确定性有限状态机,描述有限状态间的转换。它出彩的地方之一是形式化地定义了从状态到原子命题的映射,使得单个状态可用命题逻辑公式来描述。下面是一坨用Kripke Structure描述mutex的例子

    Kripke Structure for Mutex

    这坨例子展示了Kripke Structure的要素:

    • 原子命题(Atomic Proposition)。所谓原子命题,就是说该命题的值不依赖于其他命题。换句话说,原始命题表达式里不包含任何逻辑操作符。例子里的NC0NC1, TRY0, TRY1, CS0, CS1都是原子命题。我们用原子命题来描述系统最基本的状态。比如NC0的意思是线程0没有进入critical section。原子命题的集合叫AP,也就是Atomic Proposition的缩写。

    • 状态。每个圆角方框代表一坨状态。上面例子有八坨状态,从S0S8。一个状态可以用多条原子命题描述。比如说状态S0满足两条命题:NC0NC1

    • 状态转移。状态转移用有向箭头表示。比如S0S1的箭头表示状态能够从S0转换到S1。反过来说,S1不能直接转移到S0,所以没有S1S0的箭头。状态间的转移也表示时间的改变。比如从S0S1表示我们假想的离散时间前进了一格。

    • 标签函数。我们怎么知道某个状态满足哪些命题嗫?这个就要靠标签函数了。标签函数把状态映射到AP的幂集,2AP。换句话说,任意状态可能满足AP中任意命题的组合。如果AP={P, Q, S},那一个状态可能满足的组合就是{F}{P}, {Q}, {S}{P, Q}, {P, S}, {Q, S}, {P, Q, S}

    有了这些基本概念,我们就能理解这托例子展示了mutex 系统里两枚线程获取及释放critical section的过程:

    • 状态S0表示两条线程都没有占用critical sectionNC0表示线程0non-critical sectionNC1表示线程1non-critical section
    • 状态S1表示线程0开始试着获取critical section,而线程1还在non-critical section
    • 状态S2表示线程1试图进入critical section 而线程0还在non-critical section
    • 系统可以保持S0的状态,也可以进入S1或者S2
    • 状态S3表示线程0和线程1同时试图进入critical section
    • 状态S4表示线程0进入critical section, 而线程1还在non-critical section
    • 状态S5表示线程0和线程1都试图进入critical section
    • 状态S6表示线程0non-critical section,而线程1进入了critical section
    • 状态S7表示线程0还在critical section的时候,线程试图进入critical section
    • 状态S8表示线程0试图进入critical section,而线程1还在critical section内。

    有了直观的解释,Kripke Structure的形式化表述就容易理解了。另外形式化表述也是必要的。除了研究Kripke Structure的性质以外,我们的代码也建立在形式化表达的基础上。

     

    Kripke Structure 被定义为在给定原子命题集合AP基础上的4-tuple K = I, S, R, L)。I是初始状态的集合。S是有限状态的集合。R是状态关系函数,R ` R % RR必须是完全函数。也就是说对状态集合里的任意状态s来说,R(s)必须是S里一个元素。换句话说,任何一个状态都必须有条向外的箭头。这同普通状态机不同。而L则是标签函数,把状态映射到AP的幂集,L : S -> 2AP。上面例子的形式表述就是:

    • I = {S0}
    • S = {S0, S1, S2, S3, S4, S5, S6, S7, S8}
    • R = {(S0, S0), (S0, S1), (S0, S2), (S1, S4), (S1, S3), (S2, S5), (S2, S6), (S4, S7), (S4, S0), (S3, S7), (S3, S2), (S5, S8), (S6, S8), (S6, S0), (S8, S1)}
    • L(S0) = {NC0, NC1}, L(S1) = {TRY0, NC1}, L(S2) = {NC0, TRY1}, L(S3) = {TRY0, TRY1}, L(S4) = {CS0, NC1}, L(S5) = {TRY0, TRY1}, L(S6) = {NC0, CS1}, L(S7) = {CS0, TRY1}, L(S8) = {TRY0, CS1}

    有了Kripke Structure描述的模型,就可以开始考察系统的性质了。任何一坨mutex系统都需要满足一些基本性质:

    1. 任何时候线程0与线程1都不能同时进入critical section。这是critical section的基本要求,不然基于加锁的多线程也没法玩儿了。用LTL描述一下:
      • 线程0进入critical sectionCS0表示。线程1进入critical sectionCS1表示。那两坨线程同时进入critical section自然是CS0 . CS1
      • 两者不能同时进入critical section,无非是对上面的陈述取反。所以我们得到 × (CS0 . CS1)
      • 表示“任何时候“的操作符是G,所以我们得到了最终的表达式:G(× (CS0 . CS1))

    这是所谓的安全特性(safety property),用来确定某些情况任何时候都不会发生。我们的mutex系统明显满足该性质,因为例子里的每坨状态最多有CS0CS1中的一项。

    1. 有了安全性质不够,还应该有活性性质(liveness property),也就是某些特性最终应该发生。比如说,当线程0要进入critical section,它最终一定能进入。这条也很重要,不然我们就遇到死锁或活锁了。形式化的过程就省略了,反正也不难。最终公式是G(TRY0 -> F(CS0))。我们的例子也满足这坨公式。线程0试图进入critical section的状态包括S1, S3, S5, S8。从这些状态出发,我们总能找到一条路径,到达包含CS0的状态。比如说S1->S4, S3->S7, S5->S8->S1->S4, S8->S1->S4

    2. 马斯洛爷爷说了,光有安全需求和生理需求是不够滴。人不能苟活着。我们还需要健康的激励,而激励的基石之一就是公平(fairness property):如果线程0无限次试图进入critical section,它就能无限次进入critical section。按需分配的共产狂想性质啊:GF(TRY0) -> GF(CS0)fairness也有好多种。我们这里谈的是强公平)。这坨公式其实也好理解。F(TRY0)表示TRY0在未来某个时候会发生。GF(TRY0)就表示从任何时刻算起,TRY0都能发生,也就是可以无限次发生的意思。这和big-O或者极限的定义里表达无限逼近的手段相似。显然我们的mutex系统也满足这条性质。只要TRY0出现也就是线程0试图进入critical section,系统肯定进入状态S1,而进入状态S1后,不管哪条路径都导致包含CS0的状态。也就是说,只要TRY0无限次出现,CS0也无限次出现。这样的路径也有个名堂,叫公路,也即公平路径(fair path)的简称。

    从上面简单例子可以看出模型检验的套路:

    1. Kripke Structure建立系统模型。
    2. 用时序逻辑公式描述我们期望的系统性质
    3. 证明这些公式在第一步建立的Kripke Structure上永远为真。上面的例子只给出了正确公式的演示。其实更绝妙的是当系统有错的时候,模型检验不仅能查出错误,还能给出生成错误的执行路线,也就是反例。这些反例往往比人肉查错来得短小。在某些情况下,模型检查甚至可以保证生成最短小的反例。

    对于简单例子,我们可以做如上人肉分析,再配上大批公式和所有希腊字母唬人。可惜真正的系统动辄成千上万甚至上亿状态(这也是为什么Dijkstra倡导的人肉证明行不通的原因之一),手工证明代价过于高昂。幸好Edmund M. Clarke, E. Allen Emerson, and Joseph Sifakis等人发明了一系列算法,让模型验证彻底自动化。算法的大体思路其实相当粗暴:遍历模型也就是Kripke Structure所有可能的执行路线,看他们是不是全部满足时序逻辑描述的性质。如果不满足,则打印出使验证失败的路径。真正有意思的是怎么遍历和组织数据。而这,才是模型检验魔力所在。我们交代了模型,下面就可以聊精彩的算法了。

     

     

     

     

     

    发表于 @ 2008年03月18日 10:34:00|评论(loading...)|编辑

    旧一篇: 汉英对照阅读

    评论

    #Googol 发表于2008-03-18 01:13:34  IP: 10.194.64.*
    感觉这个模型和以前大学里接触的数电设计模型很像啊,不过我忘了那个叫啥了……

    随着模型里状态集S大小的增多,约束本身也会爆炸式增长吧?比如最早只有n个按钮,那么描述这n个按钮间的约束就行了。如果增加了1个按钮,则至少会增加n条约束吧?不然就该有没有覆盖到的情况了……

    不过,这种暴力搜索的事情,确实应该让计算机来做,呵呵。

    最后,高速公路的翻译是:high-speed fair path……
    2008-03-18 14:57:57作者回复
    增加n条就是多项式增长了。是增加m倍,m是新增按钮带来的新状态。正是解决状态爆炸问题带来了很多让人兴奋的计算方法。
    #lbaby 发表于2008-03-18 03:04:37  IP: 222.128.6.*
    正在学习形式语言和自动机,被那坨随心所欲的状态与无穷尽的证明整得性欲下降。。。
    #Googol 发表于2008-03-20 03:31:57  IP: 10.194.64.*
    恩,一个是解决状态的爆炸增长,还有一个问题是模型的转化吧。感觉上应该是一个模块对应一个模型,而模块划分的好坏会直接影响到模型检验的复杂度。

    突然发现,这不就是个暴力自动单元测试么……
    #g9yuayon 发表于2008-03-20 08:43:39  IP: 199.246.40.*
    囧rz。Googol老大明察。模型的粒度的确影响模型检验的复杂程度(实际的,不是理论上的)。模型检验也能用于测试用例的自动生成。
    #wannabe 发表于2008-03-20 21:06:03  IP: 222.212.239.*
    负老师,谢谢你的文章。可以请教两个问题吗?(弹出对话框,只有一个|Yes|按钮)请问Petri net在描述并发系统的时候又是个什么角色?问题二:Dijkstra的人肉方法不仅用来验证程序,Dijkstra还借助他的goal-oriented logic来推导算法,有人称这为constructive logic。请问用安全形式化的bot logic(原谅我用词不准确)来自动的从specification推导implementation的工作目前进展得怎么样了?谢谢!

    另:楼上性欲受影响的那位,这是你的榜样:http://xkcd.com/230/ 哈哈。这个网站太好玩了,再次谢谢负老师。
    #gjWang 发表于2008-03-28 01:04:32  IP: 218.2.214.*
    lz讲解思路清晰,希望继续!
    #g9yuayon 发表于2008-03-28 09:12:58  IP: 199.246.40.*
    @wannabe, 不用客气哈。Petri Net我不熟。描述并发系统的方法有很多种。Petri Net是其中一种,用图形化的方法(一坨bipartite graph)描述系统,显式描述状态转移,而不是隐含状态抽象描述系统行为。挺流行的工具,很多人研究。contructive logic我就完全不知道了。从spec推impl的工作还有人做么?如果能从spec推出impl,spec自己就是编程语言了。无非是表达能力和形式的区别而已。我也不了解。
    #wannabe 发表于2008-04-01 10:59:00  IP: 222.212.224.*
    我想象一个spec会对应很多impl,但是需要“计算”出时间复杂性最小的那个,比如给出max subseq sum的定义,要能产生出O(n)的那个算法;给出sort的定义,产生quick sort...
    #lbaby 发表于2008-04-02 03:01:01  IP: 222.128.6.*
    恩,一个是解决状态的爆炸增长,还有一个问题是模型的转化吧。感觉上应该是一个模块对应一个模型,而模块划分的好坏会直接影响到模型检验的复杂度。

    突然发现,这不就是个暴力自动单元测试么……
    -----------------------------------------

    偶这几天正想呢,有没有一种方法消灭人肉测试,正在寻找答案中。。。

    ------------------------------------

    另:楼上性欲受影响的那位,这是你的榜样:http://xkcd.com/230/ 哈哈。这个网站太好玩了,再次谢谢负老师。
    ----------------------------

    XXOO时想到了一个二叉树误操作问题,二人讨论了起来。。。 --这是俺发的“为啥不能找一个学计算机的结婚”的原因

    #wannabe 发表于2008-04-03 01:01:31  IP: 222.212.239.*
    XXOO时想到了一个二叉树误操作问题,二人讨论了起来。。。
    -------------------------
    哈哈。不能再说了,否则负老师这里要被扫黄打黑办取缔了。
    #lbaby 发表于2008-04-03 03:48:03  IP: 222.128.6.*
    Do you mind if I jot down some notes on your chests ??


    猛到无以复加
    #fferror 发表于2008-04-18 22:38:14  IP: 222.240.167.*
    无比期待g9老大的model checking续集。
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © g9