libvirt创建的qemu进程里面有一些fd的参数,这些文件是libvirt帮qemu打开的一些设备文件句柄等,比如:
qemu … -netdev tap,fd=24,id=hostnet1,vhost=on,vhostfd=25
因为需要libvirt帮忙先配置好后端以及处于安全考虑;但是qemu起来就是另外一个进程,给个fd号就能直接用了吗,显然不是,下面从代码角度分析下
[libvirt]
qemuOpenVhostNet
-qemuMonitorPassDevfd
–qemuMonitorSendFileHandle
—qemuMonitorJSONSendFileHandle
—-qemuMonitorJSONCommandWithFd
qemuConnectMonitor->qemuMonitorOpen
qemuMonitorOpenInternal调用virEventAddHandle把qemuMonitorIO注册给mon->watch作为callback,等monitor检测到qemu进程起来会调用这个函数把fd传过去,
而传fd用的是Linux系统调用sendmsg。对,就是那个socket通信中常用的sendmsg,他还有这个附加属性~
int virEventPollAddHandle(int fd, int events, virEventHandleCallback cb, void *opaque, virFreeCallback ff) { int watch; virMutexLock(&eventLoop.lock); if (eventLoop.handlesCount == eventLoop.handlesAlloc) { EVENT_DEBUG("Used %zu handle slots, adding at least %d more", eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT); if (VIR_RESIZE_N(eventLoop.handles, eventLoop.handlesAlloc, eventLoop.handlesCount, EVENT_ALLOC_EXTENT) < 0) { virMutexUnlock(&eventLoop.lock); return -1; } } watch = nextWatch++; eventLoop.handles[eventLoop.handlesCount].watch = watch; eventLoop.handles[eventLoop.handlesCount].fd = fd; eventLoop.handles[eventLoop.handlesCount].events = virEventPollToNativeEvents(events); eventLoop.handles[eventLoop.handlesCount].cb = cb; eventLoop.handles[eventLoop.handlesCount].ff = ff; eventLoop.handles[eventLoop.handlesCount].opaque = opaque; eventLoop.handles[eventLoop.handlesCount].deleted = 0;
qemuMonitorIO:
qemuMonitorIOWrite:
if (mon->msg->txFD == -1) done = write(mon->fd, mon->msg->txBuffer + mon->msg->txOffset, mon->msg->txLength - mon->msg->txOffset); else done = qemuMonitorIOWriteWithFD(mon, mon->msg->txBuffer + mon->msg->txOffset, mon->msg->txLength - mon->msg->txOffset, mon->msg->txFD); qemuMonitorIOWriteWithFD cmsg = CMSG_FIRSTHDR(&msg); /* Some static analyzers, like clang 2.6-0.6.pre2, fail to see that our use of CMSG_FIRSTHDR will not return NULL. */ sa_assert(cmsg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); do { ret =sendmsg(mon->fd, &msg, 0); } while (ret < 0 && errno == EINTR);
virEventPollRunOnce->virEventPollDispatchHandles
最后在这里virEventPollDispatchHandles调用了注册的callback: (cb)(watch, fds[n].fd, hEvents, opaque);
[qemu]
register_types
register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET, qemu_chr_parse_socket, qmp_chardev_open_socket); register_char_driver("file", CHARDEV_BACKEND_KIND_FILE, qemu_chr_parse_file_out, qmp_chardev_open_file); qmp_chardev_open_file->qemu_chr_open_fd chr->chr_update_read_handler = fd_chr_update_read_handler; remove_fd_in_watch(chr); if (s->ioc_in) { chr->fd_in_tag = io_add_watch_poll(s->ioc_in, fd_chr_read_poll, fd_chr_read, chr); }
fd_chr_read->qio_channel_read->qio_channel_readv_full->
return klass->io_readv(ioc, iov, niov, fds, nfds, errp);
ioc_klass->io_readv = qio_channel_socket_readv;
ret =recvmsg(sioc->fd, &msg, sflags);
qmp_getfd->qemu_chr_fe_get_msgfd->qemu_chr_fe_get_msgfds->
return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
参考文献: