2009年12月28日星期一

Revised P2PSim-0.3 is ready for download now

The original P2PSim ver 0.3 can not be compiled successfully with g++ 4.3. I have fixed the problem and provided the download of my revision on http://www.fosfer.info. The download link can be found within the “Open download” category. Only  the compiling bug is fixed in this revision, while no any other code is touched. So it’s expected to work as same as the original version. If this download violates any copy right, please let me know.

P2PSim-0.3修正版提供下载

我在http://www.fosfer.info上提供了修正后P2PSim-0.3版本的下载。我只是修正了P2PSim 0.3的版本无法在新的G++ 4.3上无法编译通过的问题。该版本的功能实现上未作任何修改,保持了和原版本的一致。下载在该站的Open download下。http://www.fosfer.info目前还在建站中,今后的发展目标是成为我和朋友们的工作室的主页,能提供各位有用的文档、资料以及代码。

2009年12月27日星期日

P2Psim分析笔记(7)-RPC机制

这是分析笔记的最后一篇,这篇介绍了p2p仿真中最常用的RPC机制在P2Psim中的实现。这篇对P2Psim的仿真机制的最后一部分进行了说明。P2Psim中,不同的P2P协议实现都是基于这个机制来模拟peer间的通讯。
    RPC(Remote Procedure call)的本意是,一台电脑通过网络来来执行另外一台电脑上的函数,函数执行的结果再送回本机。这个机制有很多实现和变形,比如xmlrpc, Soap, 我在这里不扯开了,大家google一下就知道了。因此按照RPC的概念,P2P中peer之间的相互访问都可以看作是RPC操作。因此在P2P的仿真中,仿真软件都不约而同实现一套简化了的rpc接口,这样在应用层的p2p协议只要用这套接口就可以不必理会底层网络通信的细节了。这篇中我给出了RPC实现的说明图。
    图中,不同p2p协议通过调用Node中的doRPC()和asyncRPC()来发送数据给远程的peer。前者是同步的,也就是说,在没收到对方peer的回复或者超时前,本peer就一直死等。后者是非同步的rpc调用。在非同步的方式下,peer调用asyncRPC()函数会立刻得到返回,然后协议得到一个该次请求的句柄,可以理解成取货凭证。然后peer隔一段时间在通过rcvRPC()函数,用这个句柄来查询一下,这个远程调用是不是返回结果了,或者失败了(比如网络丢包)。这好比拿着取货凭证去取货一样。同样如果这个peer收到别人发送来的rpc请求,他也要提供处理相应请求的处理函数。当收到这个请求的时候,相应处理函数被调用。关于请求,我们迟点再分析。我们先逐个分析doRPC()和asyncRPC()的实现。
    当Protocol类的对象调用了Node类中的doRPC()时,Node类首先通过_maktrunk函数把这个请求打包(为了更像一个网络报文?不知道),然后依次调用_doRPC_send()函数在这个报文上面加一个channel(libtask里面的多任务间的数据通信机制)把这个请求的trunk发给Network类的send()接口。Network根据拓扑算出这个报文到达对方peer的延迟,然后生成一个NetEvent事件放入事件队列里。当doRPC()发送了这个数据包后,立刻调用_doRPC_receve()来在这个报文的等待这个数据包上的channel上是否有数据过来,如果有,则从channel中取出这个rpc是否成功的信息再依次返回给doRPC()函数和上层的调用代码。如果成功,这个rpc请求中的返回值指针指向的数据结构已经被对方peer给改写了。以这种方式模拟rpc结果数据的返回。
    当Protocol类的对象调用了Node类中的asyncRPC()接口的时候,机制稍微不同一点。asyncRPC同样调用_maktrunk()封装出一个trunk包,然后让_doRPC_send()函数发送出这个包,但是返回的rpc句柄(取货凭证)被存放到_rpcmap结构中暂时保存起来。然后asnycRPC()函数就立刻返回这个句柄给上层protocol的调用代码了。然后呢,上层代码在之后的事件里面可以时不时通过rcvRPC()接口来查询一下_rpcmap结构里面这个凭条对应的请求有没有得到响应或者是否出错了。
   以上讲完了rpc请求发送的基本逻辑流程,接下来说一下rpc请求被接收的流程。上篇我说了,当EventQueue处理NetEvent事件的时候调用其execute()会导致Node中的Packet_handler()接口被调用。这个接口函数会区分,该消息对应的是rpc请求还是rpc应答。如果是应答的话,他就这个报文通过其channel(记得在_doRPC_send()中建立的channel么?)发送过去。 如果_doRPC_receive()正在等待channel上的数据,这个数据就会让_doRPC_receive()返回,完成同步RPC。如果这是一个异步的rpc,无所谓,这个数据就存在channel里面了,等peer调用rcvRPC()去查询和收取。
  如果这是一个请求事件,那Packet_handler()就建立一个新task来运行receive()函数。这个函数中,这个事件中rpc的请求类型会被拿出来分析,对应不同的事件类型,protocol里面的不同事件处理函数会被调用。也就是说Protocol对象实例中的相应RPC处理函数“被”执行了。根据执行结果,receive()函数构造出RPC响应数据包,然后再通过Network类的send()结构最终又变成NetEvent事件插入到事件队列EventQueue里面去了。至于RPC的响应如何处理,上一段已经说过了。

无命名

P2Psim分析笔记(6)- 仿真中的事件机制

    这几天由于P2Psim的分析进度不是很快,所以一直也就没有更新分析笔记,希望大家见谅。在前几篇中,我已经大体介绍了P2Psim的初始化过程。对初始化的分析,到上篇就告一段落。由于我是一边分析一边写笔记,所以有些细节方面可能存在遗漏和错误。如果各位在自己的分析中发现,不妨联系我。在每篇笔记的图片中,有我的邮件地址的水印。

    接着之前的分析,本篇主要介绍了P2Psim的运行机制。这部分的理解,是以后用P2Psim来添加自己的p2p协议的基础。这篇的介绍不涉及太多细节,因为在我的分析中,发现Node类的实现中有太多的细节需要说。所以在这里我只是给大家一个总体的介绍。如果大家去做代码分析,我建议大家分析sillyprotocol,而不要一上手就去看Chord协议。我个人的感觉是,Chord协议涉及太多协议本身的机制,它的机制本身的复杂性,很大程度上会干扰大家对仿真中的消息机制的分析。而sillyprotocol几乎没有什么代码和任何协议机制。因此是一个很好的切入点。

   按照惯例,我给出了分析的简单图例。由于这图是用office的onenote画的,虽然画起来很方便,但是onenote的画图功能实在太弱了,所以不同意义的先只能用颜色区别,而无法用线形来区别,大家只能凑合着看了。

   图中左上角的EventQueue是我介绍的切入点。在之前,我介绍过,他的运行机制就是在不停的调用他的advance()函数从消息队列里面取出事件,然后每次取出消息就去kick一下消息发生器类EventGenerator,然后再对取出的每个event对象,调用其execute()函数来完成相应的操作。那么EventQueue中消息的来源主要是两个: EventGenerator类或其子类;和Node类。

   先看EventGenerator类。这个类在把自己注册到EventQueue实例里面的时候就把仿真结束消息(exit)和各个peer加入仿真的消息(join)加入了队列。前者是SimEvent类型的消息,后者是P2PEvent消息。SimEvent类型的消息只有一个用途,就是结束仿真。P2PEvent事件的用途就是通知各个peer在不同的时候做不同的事情,比如join,crash和lookup这些。不同于其他的仿真软件,如OverSim,P2Psim中P2PEvent事件是由EventGenerator做中心控制的,而不是由各个peer自己控制的。换而言之,EventGenrator控制了全体peer的行为模式。这点简化对于homegeneous的仿真而言是好的,写代码就容易多了。如果要做geterougenous的仿真就困难一点了,不过也不是大问题。

   第二个事件的发生源是Node类。Node类封装peer作为网络结点的角色。对上层p2p协议,Node提供了RPC调用接口。然后把对应的RPC请求和应答转换成网络报文的传输。在仿真中,报文的发送和接受都被转换成NetEvent网络事件。当Node要发网络数据包的时候,他就从Topology对象提供的接口计算出报文到达的延时,然后生成一个报文到达的NetSim事件,放到EventQueue里面,等待EventQueue到时候进行处理。

   介绍完事件的发生源,再说说事件的处理。之前说过,EventQueue在取出Event时候,会调用其execut函数来处理事件。对于不同类型的事件,其execute机制不同。 最简单的是SimEvent的,立刻就通知系统结束仿真了,具体代码各位自己去看。其次是P2PEvent。如果消息对于的操作是EventGenerator让peer join或者crash,他只是把protocol中对应的_alive进行置位和清位。如果是其他操作,如chord中的lookup,这类。他会对应找到protocol对象中的函数调用来完成处理。比如lookup事件, P2PEvent 就把Event中对应的node对象找出来,调用他的lookup()函数.

   如果处理的是NetEvent事件,它的execute会根据事件的类型进行区别对待。之前我提到,Node会把RPC操作转变为网络报文传输,因此NetEvent对应不同网络报文也分两类:请求和应答。如果这个事件对应的是请求,它就让系统为对应的接收结点生成一个新task来跑Node::receive()函数,让node来接收这个“网络数据包”。否则,他就把这个数据包发送到对应的channel去,让等待接收RPC回应的node的()来接收这个回应。

无命名

2009年12月23日星期三

P2Psim分析笔记(5)-EventGenerator and Observer

    本篇笔记,主要是跟踪taskmain()函数中EventGenerateor::parse(event_file)。本篇主要涉及EventGenerator的构造流程,其中对churnEventGenerator类和ChornObserver类进行分析。下面的图示给出了EventGenerateor::parse()函数的调用流程。

    图中EventGenerateor::parse()的调用很简单,首先读取eventfile 里面关于eventgenerator和observer的名字。生成了一个generator实例,然后通过thread()把对应的eventgenerator的任务个激活。最后再生成observer实例。这里我分析了ChurnEventGenerator 和ChurnObserver两个类。按照我的理解Generator主要用于生成对应protocol的node的初始化事件,比如node什么时候开始活过来,以及对eventqueue类实例化和进行激活等操作。observer 的设计本意是用来统计和记录仿真中的各个事件。在P2Psim的设计中,observer的实例化中,observer把自己注册到各个node对象中,这样每次node发消息的时候,在消息队列处理消息的时候,都会把这个消息上的observer拿出来,调用一下他们的kick()来修改observer中的统计信息。

   图中,首先EventGenerator::parse()调用工厂模式生成了eventgenerator实例。我们以churnEventGenerator为例。churnEventGenerator在构造对象的时候,首先读取了所需的参数。图中列出了所有参数,这些参数的具体用途以后再分析(目前我也不清楚,呵呵)。然后实例化了消息队列,并把自己注册为消息队列的observer,这是一个特殊的observer(具体的用途目前也不清楚,估计是每次消息队列处理消息的时候,都能被调用一次)。

  在消息队列的实例化中,消息队列eventqueue,建立了一个特别的channel,然后就调用自己的thread()触发了run()函数变成一个并发任务。run函数首先会在这个channel上等待一个go消息(这个消息会由eventGenerator的实例触发,见图中黄绿色箭头),当收到这个go消息,run()就循环调用advance()函数来处理消息队列里面的每个消息。在处理每个消息的时候,每个消息上的observer会被kick()一下,然后消息队列会执行event上的execute()函数来处理这个消息对应的事务。

      在EventGenerator::parse()生成eventgenerator后,第二件事情是调用了这个实例(churnEventGenerator为例)的thread函数,然后这个消息发生器对象就开始作为一个并发任务开始运行其run()函数了。在其run()函数中,我们可以看到generator的主要工作了:首先生成了一个仿真结束消息,然后从network实例中要来了所有的node信息,把他们都维护在本地的数据结构里面,并把wellknown结点告诉他们,这样他们一开始就能知道他们到那里去找access point。然后设置每个node加入p2p网络的事件。当然,wellknown结点是最早加入的(不然别人找不到他了)。最后给eventqueue发一个go消息,eventqueue就开始处理消息了,整个仿真就开始了(但是由于没有调用yield(),当前任务还没进行切换,所以在物理上那些任务一个都跑不起来)。

    再回到EventGenerator::parse(event_file)函数,最后一步是通过工厂模式建立了observer对象实例。以churnObserver为例,他的工作也很简单,从Network对象实例里面得到所有的node信息,把自己的指针加入他们,这样他们发送消息在消息队列处理时observer就会被触发了。

总结: eventGenerator 是注册在eventQueue上的observer,而普通observer是注册在node上的。

无命名

2009年12月22日星期二

P2Psim分析笔记(4)-Topology 和Network

  在上篇的分析理,taskmain的第三步是让Topology类来分析topology_file,从而启动了Network对象。这次,我主要介绍这部分的详细流程。主要内容包括对Topology类以及子类Euclidian拓扑类的机制分析,Failure Model类以及Network类的交互。搞清楚这些,基本就把P2PSim的启动机制搞清楚了。按照惯例,我在这里给出了所涉及的流程的大体流程图。
无命名
   
    这部分的调用是taskmain函数中的Topology::parse(topology_file)引起的。在pase函数中,首先从topology_file里面读出了topology的名字,例如Euclidian拓扑, 以及对应的failure模型。至于failure模型,好像是用来制定数据传输中的丢包策略的。这个目前不是我的兴趣所在,所以我也懒得去仔细分析了。如果在topology_file里面不指定的话,在这里会默认提供一个无丢包的nullfailure模型。
   在生成topology和failure模型中,他们的构造函数都不会做什么特别的工作。然后这两个对象top和fm被作为参数来构造Network的对象实例。在Network对象的构造中,topology和failure模型会被保存到Network对象里面。以后只能有Network的实例来访问了。最后Network对象构造中会调用thread来让run函数作为一个task来跑。这个函数中,Network实例开了一个channel(libtask中task进行通信的机制),然后就不断从这个channel读取来自topology对象的Node(也就是peer)信息。然后存到自己的_nodes成员中,这个成员是IPaddress 和Node指针的一个map。
  最后,Topology::parse()调用了topology对象的parse完成了topology_file的剩余部分的解析。这里要注意toplogy对象(top指针 )是指向Topology类的一个子类的对象,比如Euclidian类的对象。 接下来,我们拿Euclidian拓扑来举例,这个对象按照
        IPaddress  x,y
的格式来解析peer在空间中的位置,以及用ipaddress来标识peer。解析出每条记录,都通过工厂模式,构造出一个Node类的子类的对象,除了保存在自己的_nodes成员中(也是一个IPaddress 到node的map),通过channel发送给Network对象的run函数。图中的黄绿色箭头表示出这个联系。

2009年12月20日星期日

P2Psim分析笔记(3)-taskmain函数流程

    在上篇里,我们知道P2PSim是基于libtask的,libtask的第一个任务是通过taskmain函数来启动的。换而言之,taskmain是p2psim的主程序。下面的图里面给出了taskmain的主要流程。



    根据图示,taskmain先把-开头的参数解析出来,然后让Node类去解析protocol_file,然后让Topology类去负责解析topology_file。Topology在解析topology_file后调用Network类生出了第一个并发任务,这个任务负责根据topology建立peer的vector。随后调用了yield放弃cup让Network任务去充分处理topology对应的Peer生成工作。
    然后,taskmain让EventGenerator类根据event_file生成了对应的事件发生器(EventGenerator)的对象,这里采用了工厂模式,根据配置文件生成对应的事件发生器。同时还生成对应的观察者对象。 生成的发生器对象也是一个从Threaded派生的家伙,因此此刻第三个并发任务也开始运行了。
    最后,taskmain遍历了所有的node对象,让他们完成初始化。随后完成所有工作退出。

2009年12月19日星期六

P2Psim分析笔记(2)-程序架构简述


这是我整理P2PSim这个仿真软件包的第二篇笔记。第一篇是P2PSim在g++ 4.3下的编译,在第一篇里面介绍了P2PSim 0.3在编译和按照中的碰到的问题以及解决方式。本篇主要给出P2PSim的实现架构,帮助需要进行代码分析的朋友对P2PSim能有个大体的构架了解。在些这篇时候,作者也还未完成对整体代码的通读,所以对全局的掌握还不充分,如有错误,请各位指出和包涵。



P2PSim简述
    这是一个比较简单的p2p模拟软件包,是MIT的一个老兄搞的玩也。在2006年出了最后一版0.3后再没更新了。需要的朋友可去http://pdos.csail.mit.edu/p2psim/下载。国内不少paper用这个软件包来做仿真,网上也有不少的代码分析笔记,比较详细的是kikiKind在CSDN上的p2psim学习笔记,但是写到后来就晦涩难懂了,完全不是给旁人看的。他给出了p2psim中类的继承关系图,以及关于vividi这个错误的fix。那个类图的资料在P2Psim的官网上很难找到链接了,所以这是kikikind朋友的最大贡献。但是他给出的贴图却是错误的,把第一张图贴了两遍,第二张图给漏了。然后所有网络转载都一样错。此外我在g++ 4.3上碰到更多的错误,他都没有提及,因此不是很全面,此外他在对libtask以及main函数的分析都很粗略,让人很难跟。综合来说,这个笔记是他写给他自己看的,而不是给网友看的。我个人不是很满意,所以我在这把他所遗漏和错误的地方都补上。希望对各位有用。在本篇笔记里面,我从便于各位读者理解学校的顺序来组织内容,而不是从代码分析的学习次序来说明。


    首先是P2Psim的实现基础。这个软件包的实现是基于单进程多任务方式工作的。他本质上是基于一个叫LibTask的C语言软件包来实现在单个进程内进行多个任务的调度和切换。这个包在他的源代码的/libtask目录下。 这个包是MIT开发的,目前在google code的网站上有登记,地址是http://code.google.com/p/libtask/,可以在这个网站上查到这个库的基本api的说明。可以直接看这个说明来理解这个库的实用,而不需要去看/libtask目录下的代码来分析到底这个库在做什么。此外,在P2PSim里面,作者把这个库封装到了Threadmanager和Thread两个类。前者Threadmanager用来封装全局的task的pool的,而后者封装了各个task的api。这样P2PSim就实现了C++这级的多任务调度。
    其次,我把kikikind老兄的类继承图重新贴一遍,来帮助大家理解程序架构。在这个图里面,我们看到从threaded类一共派生出EventQueue, Network,和EventGenerator三个类,这三个类就是程序在运行时候的3个主要并发任务。















至于图中的其他部分,等以后的具体分析的时候再慢慢说明。
    因此,综上所述,P2Psim是一个根据libtask所提出的架构所写的一个程序,因此在/p2psim目录下,main.c程序里面没有main函数,而是taskmain函数,这个函数会被libtask作为第一个task而调用启动,功能类似main函数,连调用参数都差不多。然后在这个taskmain中,间接建立了上述其他几个并发任务。
    到这里,读者应该对p2psim的大体构架有个简单认识了。 下次我打算把taskmain函数里面的初始化顺序介绍一下。

2009年12月18日星期五

我的小服务器

朋友做了一个工控机的板子,我要了一块来,自己加上了迅驰1.2G CPU,再从笔记本上拆了一个1G内存和老的移动硬盘 30G IDE,就算搭起了一个最简陋的服务器。此外我从破DVD光驱上拆了一块铁皮底板,打了几个洞,把主板和硬盘固定起来,现在可以不用在桌子上“摆地摊”了。打算用这个服务器做个人web server。在朋友那里测试的结果,板子加硬盘的功耗只有30W,相当低功耗了。符合绿色环保理念。


2009年12月17日星期四

一位亿万富翁的素食原则(zt)

一位亿万富翁的素食原则


旺度居士


全球500强之一的AT&T公司亚太地区前总裁温先生今天视察了我们公司。他现在是一个拥有数亿资产的香港出版公司董事长,一位慈眉善目的中年人。

中午由我做东,请他吃饭。亿万富翁要吃什麽呢?我有些茫然。珠海虽然颇多山珍海味,但要让温董吃得满意,究竟需要怎样的排场?

谁料温董轻言轻语∶“方便的话,我就吃素!”於是,我们顺便进了怡景湾大酒店旁的西餐厅。

温董、刘先生、我及LD一行四人,点了四份商务套餐。我们的三份,有荤有素;温董的那份,还真是全素。

席间,我向温董请教吃素的益处。温董的一番素食理论,听得我心服口服┅┅

(温董的话)

简单原则

一谈到吃素,就会被许多人问及“修行”的事,总是被弄得很茫然,不知道应该回答什麽,每每想起前一段时间的一次对话,只涉及到一些简单原则,比谈“修行”容易多了。起因是去日本交流,这天中午他们知道我是素食者。

“你什麽都不吃吗?”(每个人都忙著劝别人吃,有人客气的问。)

“我刚才吃了一个苹果。”(我礼貌的回答。)

“他是一位素食者。”我的同事放了个炸弹。客气的寒暄显然不如这个话题有趣,大家如同看斑马一样一起盯著我。

“你信仰什麽宗教吗?”(为什麽每次都要问同样的问题。)

“没那麽复杂,只是生活方式略有不同。我的原则很简单,尽量不伤害别人。”

“可动物不是人!”

“我们喝著牛奶长大,它辛苦种地让我们吃,当它哭著求你不要杀它和它的小孩,你是否下得了手?你是否忍心?小时候大家都玩过老鹰捉小鸡的游戏,那表示它们的母亲不希望它的小孩受到伤害。它们打不过你。但你不会利用你的强大去欺负别人吧,”

“那你肯定反对我们吃肉。”

“我不会把刀架在你脖子上的,这也是一个简单的原则,如果你喜欢吃苹果我逼著你吃梨,那也是暴力。你快乐著你的快乐,我幸福著我的幸福,於是天下太平。只是你要了解自己在做什麽,结果是什麽,并且肯承担,那你有权选择你想要的生活,不一定要和我一样嘛。”

“可是你不伤害别人,别人也会伤害你。”

“你总不能因为别人偷了你的自行车你就去偷别人的自行车吧。”

“养那麽多鸡不就是给人吃的麽。”

“好像在南北战争时期就有人说∶黑人天生就是奴隶,他们的儿子、儿子的儿子天生就是奴隶。”

“我很佩服你,面对这麽多美食能忍得住。”

“我不会虐待自己的。他们对我没有吸引力,我不需要忍的啦。我刚才已经吃了一个苹果,香甜而且多汁,(席间已经有人为我点了一盒牛奶),瞧,我还有牛奶,并且每个人都很关心我是否够吃,够营养,小小的善意就得到了很多回报,这世界真的很公平的。”

“你真的适合信一个什麽教。”

“这跟宗教没有关系,只是一些简单的原则,我不会啃著鸡腿给我的儿子讲动物是我们的朋友,这样做的结果是他不仅没有学会仁慈反而学会撒谎,因为你心口不一。有很多事情是我们在幼儿园里就学过的,比如说∶大家都是好朋友、撒谎不是好孩子、自己的事情自己做、有礼貌、帮助别人、好好学习、天天努力向上、讲卫生、爱劳┅┅,这些都是极其简单的原则,就是要认真地去做。总不能长大了还不如一个小孩。”┅┅┅┅

时间久了,很多对话记不住了,不外乎把这些简单原则应用到生活和工作上,但是这些被重新提及的简单原则已经跨越午餐成为延续到下午的话题,也许还会延续到他们的生活中。许多人连小孩会的事都不会做,小孩懂的事都不懂,却谈论著这个经典那个法门,不知道爱的人满嘴都是感动,自私的人却在谈著奉献。我真的不知道什麽是“修行”,那麽复杂的事还是交给那些“大修行的人”吧,让我简单的活著,多做事少说话,这也是一个简单的原则。

我为什麽成了素食者

当您真正了解肉类是污秽不洁的,又具传染病的尸首时,您能再面无惧色的狼吞虎咽吗?

在我逐渐弃肉而吃素的过程中,我没作过任何所谓的"牺牲",所有食物没勉强放弃过一样。我虽相信,人在生活中善自保重身体,是信仰的一部份,但我个人在弃绝肉食中,与信仰毫无关联。

我不吃任何肉食和任何维他命丸,也健康的活了半辈子。我在美国旅行多次,常在饭馆和别人家中吃饭,桌上虽也有肉类,但丝毫不曾影响我的饮食与情绪。弃肉吃素的经过,未曾使我作难,也没有勉强的感觉。让我慢慢道来吧猪肝的脓包里竟有一窝煮熟的小虫,牛的一整叶肺给结核菌蚀烂了;动物的病菌会寄生在人体上。

....在旅行时,有一天吃肝咬了两三口,觉得味道不对劲,再用刀子一切,真把我吓了一跳,脓包里竟有一窝小虫,早己煮熟了。从那天起,每逢看见肝我就反胃。

....我本来最爱吃碎牛排,就是用绞肉机绞碎的。听别人说,商人常搀杂各种劣等的零碎物,所以每次我都亲自挑选一块漂亮的牛肉,叫他当面绞碎。我觉得这样作很精明。

....有一天,我发现自己并不精明。因为排在前面的一位老兄,等著绞他的猪肉。同一个绞肉机,既不消毒,也不洗,它内部的构造我更清楚。这时我才领悟到前面的人留下半磅猪肉给我,而我的半磅混合肉要留给后面的那位顾客。从此,碎牛排再也不能逗引我的胃口了。

....我虽不再吃碎牛排,但是牛肉仍是我所喜爱的,直到一件事震撼了我,我才全然断绝牛肉!!

....事情是这样的,我的邻居从牛群中挑了一只最棒的母牛,供应他自己的牛奶。某天,卫生员来检验,说只牛有结核病,应予销毁。邻居说他不相信,置之不理,后来,另外的检验员又来检查,报告的情况相同。

....我的邻居仍不肯相信,他勉强的把牛送往一个较大的屠场,获得许可,观看切割。出现在他眼前的,是一整叶被结核菌蚀烂了的肺!

....回到家里,他极为烦恼。他得的那笔代价还不错,比同量的肥料价钱贵多了。但是,他一直在想,那只牛身上的其他部分,是不是健康无病菌呢?是不是可以食用?

此事过后不久,我领著班上的学生去远足,路过该屠房,大家看著各种动物的尸体,在吊车和输送带上运送著。我就问那位作向导的政府验肉员∶『请问老兄,如果一头牛害结核病,一叶肺烂坏了,您们怎样处理呢?』

....『我也请问你,你的苹果上有个烂斑,你怎麽办?你还不是把它削掉,然后吃下去吗?我们也是一样作。』....我注意到学生们脸上吃惊的表情。上次我曾向他们讲过那只病牛的故事。等出了屠场,我问他们削苹果和割牛肉是否相同。

....『不同,开玩笑!』异口同声的否定著。他们说∶『病肺的血液会周流全身。』於是我又向他们指出另外的不同点∶动物的病菌会寄生在我们的人身上,而苹果的酶菌只会活在果菜上面,再者,它也不会周流回圈。就算把苹果的烂疤吃下去,也不致于害病。

....这样一来,过去使我讨厌的某些肉食,越发使我讨厌了。

鸡场里,那些垂头丧气、屁股潮湿的劣等鸡都进了市场,加★工场。

....过去我爱吃鸡。但参观附近的一个养鸡场之后,这方面的食欲也没有了。我看到,养鸡人天天巡视鸡房,把病鸡和少下蛋的鸡挑出来,送去市场。那些垂头丧气、屁股潮湿的家夥,都进了加工厂。使我吃惊的是,至少在那时候,根本就没有任何检验工作。胃好像告诉我,别再把死鸡送进我的皮袋里去了!

★某些山涧里,有百分之九十的鱼的染患癌症。

┅┅鱼,我还是吃,有时还开玩笑的说∶「鱼一定是乾净的,它们起码每天洗一次澡呢!」....某次,同朋友去亚利桑那州某山涧钓鱼,我搞不清到底是甚麽毛病,但我知道所钓到的鱼中,将近二分之一是有肿瘤的,或在内部,或在外部,看了令人倒胃口。查阅有关资料,我才从政府报告得知,有些山涧里,鱼癌流行,尤其是鳟鱼。其染病率有的高达十分之九。

★那些小鱼

┅┅还有,一些装罐的小鱼,如沙丁鱼等,根本就没有清洗过,连五脏,带粪便,一股脑儿的装进铁盒里去.................

请珍爱生命

看见报上的一幅图画,一口烧热的油锅中弓身著一条鳝鱼。图画的插图大意是说,下油锅的鳝鱼极力弓起身体,厨师不解,拿出鳝鱼用刀剖之,才知其腹内怀有一条小鳝鱼,它是为了保护腹中的小生命,努力的弓起了腹部。

听友人讲起一件他目睹的很悲惨的事。一条有黑缎般光亮皮毛的雄性狗,离开刚下狗娃的花狗准备到街对面不远处的一家肉食小店去拾一些骨头。大约是被爱情及爱情的结晶冲昏了头脑,它从北向南穿过十字路口时,没注意到一辆微型客车正从东风驰电掣般开来,“21712;”的一声,被车撞了个正著。车子几乎连速度都未减一下,就开跑了。车子刚刚离开,狗就在车子喷出的废烟中,一个鹞子翻身站起来,撒腿往肉食店跑去。在它被撞倒的路中间,有滩红色的血慢慢向四周流动和凝固,象一个心的形状。血中间漂浮著几根黑亮的毛。

黑狗迅速地跑到小铺子,用嘴拾起一根粗大的带肉的骨头,转身又飞一样奔回它的花狗和小狗娃的身旁,并将拾来的食物喂给了它们。这一系列行为在不过10分钟内全部完成,而且,当它把捡到的骨头转给花狗时它就无力的垂死般地倒在了花狗的身旁。谁也不会想到,从路上站起来跑掉时,“身手敏捷”的黑狗怎麽会在一瞬间死去。

友人说,黑狗将骨头转给花狗时,它听清了它们相互间那种类似安慰的,狺狺的低语。与它们的声音不同,它们的眼睛都充满了那麽深深的哀痛,悲伤。尤其是黑狗的眼睛,似乎是含著泪光,充满对生命的留恋,它那麽固执地看著自己的爱侣,看著自己的孩子,连眼睛都不转一下。那种目光,即使铁石心肠的人看了都会心颤。

我还知道,几年前冰岛政府否决了原本拟定的再次允许捕鲸的计划,原因是“找不到能使鲸迅速了结痛苦的捕鲸枪”。

在引起我们兴趣的事件日益增多的日益刺激的今天,珍爱生命这件事显得书生意气。然而,假如阅读黑狗含泪的眼睛,鳝鱼竭力弓起的身体,以及听到冰岛政府人道的尊重生命的决定,心不猛烈的跳动,并向生命致以你最诚恳的敬礼,那麽,活著就失去了它最本质的快乐。不是吗?

我在这样一个阴郁的漫长午后,开著起亚嘉华行驶在珠海的情侣路上,一遍一遍的回想温董的话和这三个与生命相关的片断,它们就象挂在屋檐下风乾的萝卜条,让你记忆生命曾经是那麽饱满,丰润和微光闪烁。而珍爱生命,就象用泉水去浸泡萝卜干,无论是哪一种形式的浸泡,都会让人看见生命恢复原状的过程,一种世间最耐人寻味的过程,一粒种子到开放花朵的过程。

听了温董的一席话,我暗下决心,一定要成为一个素食者。

P2PSim在g++ 4.3下的编译

因为科研需要,需要找一个高效的p2p模拟器,能称得上高效的,并且支持简单拓扑的,也就p2psim了。但是p2psim在2005年已经停止更新了,而且没有多少文档,因此要自己摸索有些问题。
在安装中,我用的是Debian 5.03 lenny平台。g++是4.3版本。下载到的p2psim 0.3版本无法在g++4的版本上进行编译通过。网上最简单的做法是用老版本的linux。这是我不屑做的事情。经过一个下午的折腾,终于把所有编译问题都解决了,在这里做一个问题解决的总结。以便后来人。
我碰到的问题一共有6类。按照出现和解决的次序分别为:
1:hash_map STL错误, C++库更新造成的问题
2: strcmp未申明, g++对缺省include变严格了
3:类函数 Node:xxxx: C++语法版本
4:代码错误 vividi:  作者的低级错误
6:bighashmap.cc include问题: 还是觉得是C++语言机制问题
5:STL里的assert错误: 不清楚原因

1. hash_map STL错误
这个问题g++会提示用到了ext/hashmap这个文件,这个文件已经过时了,现在建议用unordered_hash.解决办法,在configure里面找到DEF定义,加入-Wobsolete 来抑制这个警告就可以了。然后重新configure 和make。

2: strcmp未申明
这个问题很容易解决,在公共包含的.h文件里面引入#include 就可以了。还是新版本的g++变严格了的问题。

3:类函数 Node:xxxx:
在很多.c文件里面,出现了static的类函数前面加上类名字。比如Node 类里面的abc函数是Node::abc(),我不知道这是那门子C++方言。反正在4的版本中g++不允许了。把这些类名去掉就好了。

4:代码错误 vividi
在vividtest.c里面vividitest类里面,有个半吊子成员量定义,用的类型是vividi,从来没被用过,看来是作者的低级错误。注释掉就好了。


6:bighashmap.cc include问题:
这个问题很混蛋,在ld做连接的时候,出现HashMap<>:xxxx()找不到的错误。HashMap是在bighashmap.cc里面声明的,被include在node.c里面了,但是其他地方都没有include. 作者的原意是把模板当作普通类,希望在node.o里面包括,然后连接到其他的.o里面去。真是疯了:C++的模板是源代码级别的展开,根本不可能这么搞的。我改成在所有公共的.h头文件里面包括这个cc文件。


5:STL里的assert错误:
在bighasmap.cc里面 HashMap类模板里面用了不少assert来做出错检查,但是g++觉得这是不允许的,所以统统注释掉就好了。然后就编译通过了。

总结g++ 3.x是个跑单帮的,啥方言都懂,现在到了4.x版本,完成原始积累,开始打高尔夫,说官话。