poll 大概会注册以下event事件的回调
virEventPollHandleWakeup #addEvent updateEvent rmoveEvent。所以一般都是第一个.这个用到管道,fd是管道。
virNetDaemonSignalEvent #注册信息处理的,使用管道。fd是管道。
virNetSocketEventHandle #所以和libvirtd建立的连接都会使用注册这个event.
virNetlinkEventCallback #和内核建立连接,比如修改vf vlanid的
virDBusWatchCallback #消息总线,这个没看明白,看了下代码,猜测是和网卡相关的。
udevEventHandleCallback #libudev库,监控物理机设备的变化
qemuAgentIO #qemu agent socket
qemuMonitorIO #libvirt 和 qemu monitor之间的连接
下面以qemuMonitorIO为例讲解。
qemuMonitorIO是在什么时候注册回调的呢,基本只要是起了qemu进程都会去注册qemuMonitorIO. 创建,热升级,热迁移。
流程:qemuProcessLaunch -> qemuProcessWaitForMonitor -> qemuConnectMonitor -> qemuMonitorOpen -> qemuMonitorOpenInternal -> qemuMonitorRegister
最终是qemuMonitorRegister注册的qemuMonitorIO回调,以下是qemuMonitorRegister 讲解。
bool
qemuMonitorRegister(qemuMonitorPtr mon)
{
virObjectRef(mon);
//一个fd是mon->fd,事件是读,错误和HANGUP, 回调函数是是 qemuMonitorIO
if ((mon->watch = virEventAddHandle(mon->fd,
VIR_EVENT_HANDLE_HANGUP |
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_READABLE,
qemuMonitorIO,
mon,
virObjectFreeCallback)) < 0) {
virObjectUnref(mon);
return false;
}
return true;
}
从代码可以看是添加了对mon-fd的监听,并且只匹配HANGUP ERROR READABLE事件,回调是qemuMonitorIO。这里有个HANGUP,这个刚开始我也有疑惑,这是个什么事件,后面细看了下代码原来是HUP事件,HUP 是 HANGUP 缩写。真是学习了。
注册完后怎么执行这个事件了。virNetDaemonRun 里有个while死循环, while (!dmn->quit) 。这个不退出,就会一直在循环。这个dmn->quit是什么时候变成true的了,这个就是上面那个 virNetDaemonSignalEvent 回调做的,他监听的是管道,进程收到信号后会把信号传给管道,virNetDaemonSignalEvent会读取管道,匹配到相应的func操作。 daemonShutdownHandler -> virNetDaemonQuit -> dmn->quit = true
static int daemonSetupSignals(virNetDaemonPtr dmn)
{
if (virNetDaemonAddSignalHandler(dmn, SIGINT, daemonShutdownHandler, NULL) < 0)
return -1;
if (virNetDaemonAddSignalHandler(dmn, SIGQUIT, daemonShutdownHandler, NULL) < 0)
return -1;
if (virNetDaemonAddSignalHandler(dmn, SIGTERM, daemonShutdownHandler, NULL) < 0)
return -1;
if (virNetDaemonAddSignalHandler(dmn, SIGHUP, daemonReloadHandler, NULL) < 0)
return -1;
return 0;
}
言归正传:这些注册的event在哪里监听并触发的了,上面说到那个while(!dmn->quit) 死循环了,然后是 virEventRunDefaultImpl -> virEventPollRunOnce -> poll(fds, nfds, timeout) -> virEventPollDispatchHandles
static int virEventPollDispatchHandles(int nfds, struct pollfd *fds)
{
size_t i, n;
VIR_DEBUG("Dispatch %d", nfds);
/* NB, use nfds not eventLoop.handlesCount, because new
* fds might be added on end of list, and they're not
* in the fds array we've got */
for (i = 0, n = 0; n < nfds && i < eventLoop.handlesCount; n++) {
while (i < eventLoop.handlesCount &&
(eventLoop.handles[i].fd != fds[n].fd ||
eventLoop.handles[i].events == 0)) {
i++;
}
if (i == eventLoop.handlesCount) //这个表示事件循环完毕,退出循环
break;
VIR_DEBUG("i=%zu w=%d", i, eventLoop.handles[i].watch);
if (eventLoop.handles[i].deleted) {
EVENT_DEBUG("Skip deleted n=%zu w=%d f=%d", i,
eventLoop.handles[i].watch, eventLoop.handles[i].fd);
continue;
}
if (fds[n].revents) {
virEventHandleCallback cb = eventLoop.handles[i].cb;
int watch = eventLoop.handles[i].watch;
void *opaque = eventLoop.handles[i].opaque;
int hEvents = virEventPollFromNativeEvents(fds[n].revents);
PROBE(EVENT_POLL_DISPATCH_HANDLE,
"watch=%d events=%d",
watch, hEvents);
virMutexUnlock(&eventLoop.lock);
(cb)(watch, fds[n].fd, hEvents, opaque); #调用cb, 比如qemuMonitorIO.
virMutexLock(&eventLoop.lock);
}
}
return 0;
}
从以上代码可以看到cb里传入的hEvents其实是poll函数赋值的,int hEvents = virEventPollFromNativeEvents(fds[n].revents) 。所以需要各个cb函数去选择和匹配不同的处理。
基本整个事件event注册到poll解触发,都贯通了。