在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的工程能力真不是盖的…