libvirt 向qemu传文件描述符

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;

 参考文献:

Leave a Reply

Your email address will not be published. Required fields are marked *