POX事件系统分析

在pox.py中,系统启动的main()中有如下的代码:
<br /> def main ():<br /> setup_logging()<br /> _monkeypatch_console()<br /> try:<br /> if doLaunch(): #读取参数,启动POX各个component<br /> post_startup() #POX启动完成后启动of_01<br /> core.goUp()<br /> else:<br /> return<br />
在core.py的实例化中,有core = POXCore()。之前的代码中有这个函数的定义class POXCore (EventMixin)。可以看出,POXcore这个类是revent.py中class EventMixin的一个Subclass,是处理事件最高层的模块。另外,如果看后面的代码可以知道,POX的启动是由doLaunch()函数完成的。

另:看到个有趣的现象,python中,如果import某个库文件,则会执行该库文件中除了def之外的可执行语句(一般情况是实例化)[应该是跟编译/解释器设计有关系哈~这样的话,就知道如何把一个个小程序凑成一个巨大的应用了~lol]

—————————————–【分割线之:组件启动】—————————————–

如果要处理事件,需要有handler函数的实例或者函数指针。如果得到handler函数,就必须搞到+管理各个组件的实例。而管理组件正是pox启动时必须做的一件事。这也就是事件系统分析中要分析pox启动顺序的原因。

在POX启动过程中,组件随同启动,并绑定至系统中。这是如何做到的呢?在pox.py的doLaunch函数中,对于加载组件时的操作定义如下:
<br /> for name in component_order:<br /> cname = name<br /> inst[name] = inst.get(name, -1) + 1<br /> params = components[name][inst[name]]<br /> name = name.split(":", 1)<br /> launch = name[1] if len(name) == 2 else "launch"<br /> name = name[0]</p> <p> r = doImport("pox." + name)<br /> if r is False: return False<br /> if r is True:<br /> r = doImport(name)<br /> if r is False: return False<br /> if r is True:<br /> print "Module", name, "not found"<br /> return False<br /> name = r<br /> #print ">>",name</p> <p> if launch in sys.modules[name].__dict__:<br /> f = sys.modules[name].__dict__[launch]<br /> if f.__class__ is not doLaunch.__class__:<br /> print launch, "in", name, "isn't a function!"<br /> return False<br /> multi = False<br /> if f.func_code.co_argcount > 0:<br /> if f.func_code.co_varnames[f.func_code.co_argcount-1]<br /> == '__INSTANCE__':<br /> multi = True<br /> params['__INSTANCE__'] = (inst[cname],<br /> len(components[cname]),<br /> inst[cname] + 1 ==<br /> len(components[cname]))</p> <p> if multi == False and len(components[cname]) != 1:<br /> print name, "does not accept multiple instances"<br /> return False</p> <p> try:<br /> f(**params)<br /> ......<br />
这些代码完成了模块的加载+初始化工作,其中:
**

line6:launch = name1 if len(name) == 2 else “launch”</p> </b>
第6行定义了组件的默认launch函数名称。
**

line9:r = doImport(“pox.” + name)</p> </b>
第9行到第16行代码从待加载模块列表中获取模块名字。然后调用doImport函数,利用python内建函数__import__()动态加载模块。关于这个内建函数,可以参考《dive into python》中16.6的内容:

内建 __import__ 函数与 import 语句的既定目标相同,但它是一个真正的函数,并接受一个字符串参数。

这和函数的返回值就是被调用的模块,这个变量的用法和一般import后的用法一样。

**

line20:if launch in sys.modules[name].__dict__:</p> </b>
第20行中:这行代码检查了类中是否存在相应的launch函数。sys.modules 是一个字典,它包含了从Python 开始运行起,被导入的所有模块。用sys.modules[name]可以获取name模块的引用。而这个“.__dict__”很有特色。类的实例会有一个__dict__的特性字典,其中是该类的所有特性。比如看下面的例子:
<br /> >> class RichardZhao:<br /> ... def website():<br /> ... self.url = "richardzhao.me"<br /> ... print "richardzhao.me"<br /> ...<br /> >> print(RichardZhao.__dict__)<br /> {'website': <function website at 0x9c0c02c>,<br /> '__module__': '__main__', '__doc__': None}<br /> >> f = richardzhao.__dict__['website']<br /> >> f<br /> <function website at 0x00000000025817B8><br /> >> f()<br /> richardzhao.me<br /> >><br />
**

line21:f = sys.modules[name].__dict__[launch]</p> </b>
第21行就用上面的特性获取了初始化函数的实例(或者说生成了一个函数指针?)。
**

line39:f(**params)</b></p> 而第39行就是执行初始化函数的语句。

到了这个时候,我们看一个组件。就拿l3_learning为例。launch函数如下:
<br /> def launch ():<br /> core.registerNew(l3_switch)<br />
这个函数看起来很简单,只是调用了POXcore中registerNew的函数,将自己注册进pox系统中。至于register函数的作用,看函数定义中的注释就可以了解了。因为registerNew函数的调用我顺便也把register函数的定义写上来。
<br /> def registerNew (self, __componentClass, *args, **kw):<br /> """<br /> Give it a class (and optional __init__ arguments),<br /> and it will create an instance and register it<br /> using the class name. If the instance has a<br /> _core_name property, it will use that instead. It<br /> returns the new instance.<br /> core.registerNew(FooClass, arg) is roughly equivalent<br /> to core.register("FooClass", FooClass(arg)).<br /> """<br /> name = __componentClass.__name__<br /> obj = __componentClass(*args, **kw)<br /> if hasattr(obj, '_core_name'):<br /> # Default overridden<br /> name = obj._core_name<br /> self.register(name, obj)<br /> return obj</p> <p>def register (self, name, component):<br /> """<br /> Makes the object "component" available as<br /> pox.core.core.name.<br /> """<br /> #TODO: weak references?<br /> if name in self.components:<br /> log.warn("Warning: Registered '%s' multipled<br /> times" % (name,))<br /> print "components:", name<br /> self.components[name] = component<br /> print "raise ComponentRegistered", name<br /> self.raiseEventNoErrors(ComponentRegistered,<br /> name, component)<br />
registerNew函数对新的组件进行处理,然后调用register函数,在core初始化时声明的component dict中加入新注册的组件object。这样,组件就注册进pox.core中了。随后,core会raise一个ComponentRegistered事件。

注册阶段结束了?还没有,有一个问题还没有解决:组件的event_handler是如何与events绑定在一起的呢?
如果要解决这个问题,要结合着revent.py和组件代码来看上面这个函数。在上面的两个函数中,有一条语句很有意思:
**

line12:obj = __componentClass(*args, **kw)</p> </b>
这条语句是在registerNew()这个函数中,它的作用是获得一个component class的实例。于是,class中的类会实例化(插一句嘴,这条语句就是registerNew和register的区别:前一个函数会在执行时获得目标类的一个实例,而后一个则不会出现这种类似初始化的操作)。在l3_learning的实例化中,会执行:
<br /> def __init__ (self):<br /> #for each switch, we map IP addresses to Entries<br /> self.arpTable = {}<br /> self.listenTo(core)<br />
**

self.listenTo(core)</p> </b>
正是这一句,开始了事件与处理函数的绑定。

—————————————–【分割线之:事件绑定】—————————————–

回到revent.py。我们已经知道,POXcore类是EventMixin的subclass,并且,POXcore拥有了所有组件的实例。所有事件相关的处理都是在这个类中完成。回到刚才说的那一条语句(这个语句好直接啊~),self.listenTo(core),在revent.py中的定义如下:
<br /> def listenTo (self, source, *args, **kv):<br /> """<br /> Automatically subscribe to events on source.<br /> This method tries to bind all _handle_ methods<br /> on self to events on source. Kind of the<br /> opposite of addListeners().<br /> See also: addListeners(), autoBindEvents()<br /> """<br /> return autoBindEvents(self, source, *args, **kv)<br />
这个函数好简单直接啊…直接把参数传给了autoBindEvents()…看起来就像是为了方便使用而提供的API…不过,这也句也非常直接的指向了事件系统的核心之一:事件、处理函数绑定。
下面,我们来看下这个autoBindEvents函数:
<br /> def autoBindEvents (sink, source, prefix='', weak=False, priority=None):<br /> """<br /> Automatically set up listeners on sink for events raised by source.</p> <p> Often you have a "sink" object that is interested in multiple events raised<br /> by some other "source" object. This method makes setting that up easy.<br /> You name handler methods on the sink object in a special way. For example,<br /> lets say you have an object mySource which raises events of types<br /> FooEvent and BarEvent. You have an object mySink which wants to listen<br /> to these events. To do so, it names its handler methods "_handle_FooEvent"<br /> and "_handle_BarEvent". It can then simply call<br /> autoBindEvents(mySink, mySource), and the handlers are set up.</p> <p> You can also set a prefix which changes how the handlers are to be named.<br /> For example, autoBindEvents(mySink, mySource, "source1") would use a<br /> handler named "_handle_source1_FooEvent".</p> <p> "weak" has the same meaning as with addListener().</p> <p> Returns the added listener IDs (so that you can remove them later).<br /> """<br /> if len(prefix) > 0 and prefix[0] != '_': prefix = '_' + prefix<br /> if hasattr(source, '_eventMixin_events') is False:<br /> # If source does not declare that it raises any events, do nothing<br /> print "Warning: source class %s doesn't specify any events!" % (<br /> source.__class__.__name__,)<br /> return []</p> <p> events = {}<br /> for e in source._eventMixin_events:<br /> if type(e) == str:<br /> events[e] = e<br /> else:<br /> events[e.__name__] = e</p> <p> listeners = []<br /> # for each method in sink<br /> for m in dir(sink):<br /> # get the method object<br /> a = getattr(sink, m)<br /> if callable(a):<br /> # if it has the revent prefix signature,<br /> if m.startswith("_handle" + prefix + "_"):<br /> event = m[8+len(prefix):]<br /> # and it is one of the events our source triggers<br /> if event in events:<br /> # append the listener<br /> listeners.append(source.addListener(events[event], a, weak=weak,<br /> priority=priority))<br /> #print "autoBind: ",source,m,"to",sink ,"priority: ",priority<br /> elif len(prefix) > 0 and "_" not in event:<br /> print("Warning: %s found in %s, but %s not raised by %s" %<br /> (m, sink.__class__.__name__, event, source.__class__.__name__))</p> <p> return listeners<br />

先说下这个函数的作用:autoBindEvents将sink(ha 大专栏  POX事件系统分析ndler端)、source(event定义端)连接起来。具体的方式就是先在sink端将所有event的名字放在一个字典中,然后在sink中寻找带有“__handle__event”的函数,最后,如果两边有对应的event和handler,就建立连接。
**

line23:if hasattr(source, ‘_eventMixin_events’) is False:</p> </b>
第23行,如果事件的源端没有定义“_eventMixin_events”,则这个源端不能raise事件。插一句嘴,revent里面有一个dict+一个list,分别记录event和handler。在下一段函数中,会有用到。这些是在_eventMixin_init函数中完成的,具体代码可以参考下面:
<br /> def _eventMixin_init (self):<br /> if not hasattr(self, "_eventMixin_events"):<br /> setattr(self, "_eventMixin_events", True)<br /> if not hasattr(self, "_eventMixin_handlers"):<br /> setattr(self, "_eventMixin_handlers", {})<br />
**

line43:if m.startswith(“_handle” + prefix + “_”):</p> </b>
第43行,比较简单,判断函数开头是不是“__handle__event”形式。
**

line46:if event in events:</p> </b>
第46行,判断handler对应的event是否在events字典中。如果有,就可以建立连接了。
**

line48:listeners.append(source.addListener(events[event], a, weak=weak, priority=priority)):</p> </b>
第48行,很邪恶的跑到source端,执行addListener()函数,将a函数(handler)与event绑定起来。

OK,autoBindEvents函数把我们引到了事件绑定真正的核心:addListener函数~上代码~
<br /> def addListener (self, eventType, handler, once=False, weak=False,<br /> priority=None, byName=False):<br /> """<br /> Add an event handler for an event triggered by this object (subscribe).</p> <p> eventType : event class object (e.g. ConnectionUp). If byName is True,<br /> should be a string (e.g. "ConnectionUp")<br /> handler : function/method to be invoked when event is raised<br /> once : if True, this handler is removed after the first time it is fired<br /> weak : If handler is a method on object A, then listening to an event on<br /> object B will normally make B have a reference to A, so A can not<br /> be released until after B is released or the listener is removed.<br /> If weak is True, there is no relationship between the lifetimes of<br /> the publisher and subscriber.<br /> priority : The order in which to call event handlers if there are multiple<br /> for an event type. Should probably be an integer, where higher<br /> means to call it earlier. Do not specify if you don't care.<br /> byName : True if eventType is a string name, else it's an Event subclass</p> <p> Raises an exception unless eventType is in the source's _eventMixin_events<br /> set (or, alternately, _eventMixin_events must be True).</p> <p> The return value can be used for removing the listener.<br /> """<br /> self._eventMixin_init()<br /> if (self._eventMixin_events is not True<br /> and eventType not in self._eventMixin_events):<br /> # eventType wasn't found<br /> fail = True<br /> if byName:<br /> # if we were supposed to find the event by name, see if one of the<br /> # event names matches<br /> for e in self._eventMixin_events:<br /> if issubclass(e, Event):<br /> if e.__name__ == eventType:<br /> eventType = e<br /> fail = False<br /> break<br /> if fail:<br /> raise RuntimeError("Event " + str(eventType) +<br /> " not defined on object of type " + str(type(self)))<br /> if eventType not in self._eventMixin_handlers:<br /> # if no handlers are already registered, initialize<br /> handlers = self._eventMixin_handlers[eventType] = []<br /> self._eventMixin_handlers[eventType] = handlers<br /> else:<br /> handlers = self._eventMixin_handlers[eventType]</p> <p> eid = _generateEventID()</p> <p> if weak: handler = CallProxy(self, handler, (eventType, eid))</p> <p> entry = (priority, handler, once, eid)</p> <p> handlers.append(entry)<br /> if priority is not None:<br /> # If priority is specified, sort the event handlers<br /> handlers.sort(reverse = True, key = operator.itemgetter(0))</p> <p> return (eventType,eid)<br />
**

line44:handlers = self._eventMixin_handlers[eventType] = []</p> </b>
第44行:如果_eventMixin_handlers没有对应event的列表,则进行初始化。
**

line47:handlers = self._eventMixin_handlers[eventType]</p> </b>
第47行:获得_eventminin_handlers dict的对应evettype handler列表。
**

line55:handlers.append(entry)</p> </b>
第55行:将含有event handler信息的entry添加进handlers列表。
**

line58:handlers.sort(reverse = True, key = operator.itemgetter(0))</p> </b>
第58行:如果这个handler函数有优先级要求,则要依照优先级将所有event handler的顺序重排。如果没有优先级要求,则直接添加到队尾(默认最后一个调用)。

下面还有一个问题:handler在哪?这个问题关系到事件如何被处理。其实,从autoBindEvent的函数定义可以看到,最后addListener函数的执行对象是source。也就是说,handler列表在发起事件的组件中可以看到。下面来验证这个推断。先稍稍修改下revent.py中raiseEvent函数的代码:
<br /> def raiseEvent (self, event, *args, **kw):<br /> """<br /> Raises an event.<br /> If "event" is an Event type, it will be initialized with<br /> args and kw, but only if there are actually listeners.<br /> Returns the event object, unless it was never created<br /> (because there were no listeners) in which case returns None.<br /> """<br /> self._eventMixin_init()<br /> print "self : ", self<br /> print "handler: ", self._eventMixin_handlers<br />
在初始化的地方加入打印函数,把raise event的源打印到屏幕上。然后,将这个源端的handler列表也打印出来。
下面是执行结果:

self : <pox.openflow.connection_arbiter.OpenFlowNexus object at 0x91a86ec>
handler: {<class ‘pox.openflow.PacketIn’>: [(11, <bound method DHCP_Server._handle_PacketIn of <DHCP_Server.DHCP_Server object at 0x91a894c>>, False, 6)]}
self : [Con ID:1/DPID:1]
handler: {<class ‘pox.openflow.ErrorIn’>: [(None, <function also_finish_connecting at 0x8ed7ca4>, False, 8)], <class ‘pox.openflow.BarrierIn’>: [(None, «/b>function finish_connecting at 0x8ed7b1c</b>>, False, 7)]}

可以看到,前一个‘self’是openflow协议的实例,后一个‘self’是交换机实例。而他们均有‘_eventMixin_handlers’这个字典(不同)。具体可以参考字典的ID:

/Users/apple/git/pox/pox/lib/revent/revent.py(256)raiseEvent()
-> classCall = False
(Pdb) print self
<pox.messenger.MessengerNexus object at 0x10680abd0>
(Pdb) print id(self._eventMixin_handlers)
140225496904256
(Pdb) print self._eventMixin_handlers
{}
(Pdb) c
/Users/apple/git/pox/pox/lib/revent/revent.py(256)raiseEvent()
-> classCall = False
(Pdb) print self
<pox.core.POXCore object at 0x105c23e90>
(Pdb) print id(self._eventMixin_handlers)
140225496681200

可以发现,不同实例中的字典ID不一致。总结起来,事件的绑定经过这样一个流程:在源端初始化过程中,定义要raise的event类型。然后在监听端使用listenTo或者其它函数,使handler函数注册进_eventMixin_handlers列表。

—————————————–【分割线之:事件处理】—————————————–

最后,讨论下也是事件系统核心之一的事件处理。有了前面的分析基础,这部分也就变得相对简单。直接看revent.py中的raiseEvent函数:
<br /> def raiseEvent (self, event, *args, **kw):<br /> """<br /> Raises an event.<br /> If "event" is an Event type, it will be initialized with args and kw, but<br /> only if there are actually listeners.<br /> Returns the event object, unless it was never created (because there were<br /> no listeners) in which case returns None.<br /> """<br /> self._eventMixin_init()</p> <p> classCall = False<br /> if isinstance(event, Event):<br /> eventType = event.__class__<br /> classCall = True<br /> if event.source is None: event.source = self<br /> elif issubclass(event, Event):<br /> # Check for early-out<br /> if event not in self._eventMixin_handlers:<br /> return None<br /> if len(self._eventMixin_handlers[event]) == 0:<br /> return None</p> <p> classCall = True<br /> eventType = event<br /> event = eventType(*args, **kw)<br /> args = ()<br /> kw = {}<br /> if event.source is None:<br /> event.source = self</p> <p> if (self._eventMixin_events is not True<br /> and eventType not in self._eventMixin_events):<br /> raise RuntimeError("Event " + str(eventType) +<br /> " not defined on object of type " + str(type(self)))</p> <p> # Create a copy so that it can be modified freely during event processing.<br /> # It might make sense to change this.<br /> handlers = self._eventMixin_handlers.get(eventType, [])<br /> for (priority, handler, once, eid) in handlers:<br /> if classCall:<br /> rv = event._invoke(handler, *args, **kw)<br /> else:<br /> rv = handler(event, *args, **kw)<br /> if once: self.removeListener(eid)<br /> if rv is None: continue<br /> if rv is False:<br /> self.removeListener(eid)<br /> if rv is True:<br /> break<br /> if type(rv) == tuple:<br /> if len(rv) >= 2 and rv[1] == True:<br /> self.removeListener(eid)<br /> if len(rv) >= 1 and rv[0]:<br /> break<br /> if len(rv) == 0:<br /> break<br /> #if classCall and hasattr(event, "halt") and event.halt:<br /> if classCall and event.halt:<br /> break<br /> return event<br />

这段代码就是启动事件所必须的代码。这个函数在唤起一个事件的同时调用handlers列表,按照优先级等排序依次调用handler函数。如果中间遇到返回值是event halt的情况,停止循环。如果handler的once属性为真,则在运行完之后调用removelistener,将handler从列表中去除。
**

line38:handlers = self._eventMixin_handlers.get(eventType, []) </p> </b>
第38行:取handler
**

line41:rv = event._invoke(handler, *args, **kw) </p> </b>
第41行:执行函数,具体执行方式可以参考_invoke函数的定义:
<br /> def _invoke (self, handler, *args, **kw):<br /> return handler(self, *args, **kw)<br />
很显然,这个_invoke函数的返回值就是运行handler函数后的返回值。这样,事件的处理的某一步就完成了。
**

line58:if classCall and event.halt:
line59: break </p> </b>
第58行:如果有event.halt,那么这个事件的处理队列中止。

参考来源:
1、python中if __name__ == ‘__main__’: 的解析 http://www.cnblogs.com/xuxm2007/archive/2010/08/04/1792463.html

2、关于__dict__特性 http://caterpillar.onlyfun.net/Gossip/Python/PropertyNameSpace.html

P.s. 写死我了…但是,这些分析只是POX事件系统的基本原理,还有更多的细节没有看到。另外,看了这些代码之后,更佩服Stanford这帮人了,不管是Mininet、Nox还是Pox什么的,都是做工精良啊~Stanford的工程能力真不是盖的…

上一篇:logging模块


下一篇:日志字典模板