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;
参考文献: