yocto-sumo源码解析(十): ProcessServer.idle_commands
这一节开始介绍ProcessServer.idle_commands,前面我们知道ProcessServer.main就是不停调用idle_commands()以获取可用的套接字描述符或者是文件描述符来进行处理。
def idle_commands(self, delay, fds=None): nextsleep = delay #设置本函数的下次睡眠延迟 if not fds: fds = [] for function, data in list(self._idlefuns.items()): #遍历_idlefuns,逐个调用其中的函数,注李意这里一个技巧,拷贝构造了_idlefuns列表,如果不拷贝直接使用,那么循环体内的删除操作将会出错 try: retval = function(self, data, False) if retval is False: #函数返回值为False,也就是一个逻辑假的时候,从列表中删除这个函数,并设置nextsleep为None del self._idlefuns[function] nextsleep = None elif retval is True: #函数返回值为True,也就是逻辑真的时候,设置nextsleep为None,注意这里不删除所调用的函数,那么我们可以理解为,一个函数被持续调用,直至其返回值为假 nextsleep = None elif isinstance(retval, float) and nextsleep: #若返回值为浮点类型,那么认为这个函数返回的是一个时延,选择一个较小时延给nextsleep if (retval < nextsleep): nextsleep = retval elif nextsleep is None: #nextsleep为None,那么直接进入下一个函数的处理流程 continue else: #否则认为函数返回一个文件描述符列表 fds = fds + retval except SystemExit: raise except Exception as exc: if not isinstance(exc, bb.BBHandledException): logger.exception('Running idle function') del self._idlefuns[function] self.quit = True
#上面循环主要是做两件事:一件事是调用所有idlefunc,第二件是找到一个最小的nextsleep # Create new heartbeat event? now = time.time() if now >= self.next_heartbeat: #当前事件比心跳时间迟,认为丢失了心跳 # We might have missed heartbeats. Just trigger once in # that case and continue after the usual delay. self.next_heartbeat += self.heartbeat_seconds if self.next_heartbeat <= now: self.next_heartbeat = now + self.heartbeat_seconds #计算出一个新的心跳延迟,保障新的心跳延迟一定是在将来 heartbeat = bb.event.HeartbeatEvent(now) bb.event.fire(heartbeat, self.cooker.data) #触发新的心跳 if nextsleep and now + nextsleep > self.next_heartbeat: #睡眠以后可能错过新的心跳,因此这里缩短睡眠时间 # Shorten timeout so that we we wake up in time for # the heartbeat. nextsleep = self.next_heartbeat - now if nextsleep is not None: #在睡眠周期内找到可用的文件描述符,这里需要对select有一定了解 if self.xmlrpc: nextsleep = self.xmlrpc.get_timeout(nextsleep) try: return select.select(fds,[],[],nextsleep)[0] except InterruptedError: # Ignore EINTR return [] else: return select.select(fds,[],[],0)[0]
从上面的代码分析中,可以看出来,ProessServer.main()中的ready是从fds或者是xmlrpc中选出来的,回首前程往事:
fds = [self.sock] if self.xmlrpc: fds.append(self.xmlrpc)
只要sock就绪,那么就会进入到ready列表。
这里接下来看看_idlefuns这个列表到底是些什么函数呢?ProcessServer在构造函数中初始是置空的,但是提供了一个函数去注册idlefun:
def __init__(self, lock, sock, sockname): multiprocessing.Process.__init__(self) self.command_channel = False self.command_channel_reply = False self.quit = False self.heartbeat_seconds = 1 # default, BB_HEARTBEAT_EVENT will be checked once we have a datastore. self.next_heartbeat = time.time() self.event_handle = None self.haveui = False self.lastui = False self.xmlrpc = False self._idlefuns = {} self.bitbake_lock = lock self.sock = sock self.sockname = sockname def register_idle_function(self, function, data): """Register a function to be called while the server is idle""" assert hasattr(function, '__call__') self._idlefuns[function] = data
进而在ProcessServer.start中将该函数传递到configuration对象:
def _startServer(self): print(self.start_log_format % (os.getpid(), datetime.datetime.now().strftime(self.start_log_datetime_format))) server = ProcessServer(self.bitbake_lock, self.sock, self.sockname) self.configuration.setServerRegIdleCallback(server.register_idle_function)
......
cookerdata.py:
def setServerRegIdleCallback(self, srcb):
self.server_register_idlecallback = srcb
通篇检索yocto-sumo,系统共注册了下面函数:_process_inotify_updates,buildFileIdle,buildTargetsIdle,runCommands
下回再对这些函数进行分说。