当前位置: 首页 > 工具软件 > monitor_agent > 使用案例 >

qemu monitor和qemu agent的实现

太叔何平
2023-12-01

虚拟机配置中:

    <channel type='unix'>

     <target type='virtio' name='org.qemu.guest_agent.0'/>

     <address type='virtio-serial' controller='0' bus='0' port='1'/>

   </channel>

With qemu, if name is "org.qemu.guest_agent.0", then libvirt can interact with a guest agent installed in the guest, for actions such as guest shutdown or file system quiescing.

虚拟机启动命令行:

/usr/libexec/qemu-kvm

-chardev socket,id=charmonitor,fd=35,server,nowait

-mon chardev=charmonitor,id=monitor,mode=control

-chardev socket,id=charchannel0,fd=42,server,nowait

-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=org.qemu.guest_agent.0

启动虚拟机时libvirt会创建一个qemu monitor的unix socket和一个qemu agent的unix socket:

srwxrwxr-x 1 root root 0 Jan 11 16:13 /var/lib/libvirt/qemu/domain-28-Centos7/monitor.sock

srwxrwxr-x 1 qemu qemu 0 Jan 11 16:13 /var/lib/libvirt/qemu/channel/target/domain-28-Centos7/org.qemu.guest_agent.0

qemu monitor和qemu agent通道创建过程

Libvirt-6.9

libvirt启动虚拟机时会打开两个unix socket,作为server,一个用作qemu monitor,一个用作qemu agent,然后启动qemu子进程。

qemuProcessStart

    ->qemuProcessPrepareDomain

    ->for循环执行qemuDomainPrepareChannel # 设置channel->source->data.nix.path,例如:/var/lib/libvirt/qemu/channel/target/domain-28-Centos7/org.qemu.guest_agent.0

qemuProcessLaunch

    ->qemuBuildCommandLine

        ->qemuBuildMonitorCommandLine

            ->qemuBuildChrChardevStr

                ->fd = qemuOpenChrChardevUNIXSocket # dev->type == VIR_DOMAIN_CHR_TYPE_UNIX

                ->virCommandPassFD(cmd, fd, VIR_COMMAND_PASS_FD_CLOSE_PARENT); # 将要传递的fd放在cmd->passfd中

        ->qemuBuildChannelsCommandLine

                ->qemuBuildChrChardevStr # VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO

                    ->fd = qemuOpenChrChardevUNIXSocket(dev); # VIR_DOMAIN_CHR_TYPE_UNIX,打开unix socker listen

                    ->virCommandPassFD(cmd, fd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);

                ->qemuBuildChrDeviceCommandLine # 生成-device参数

    ->virCommandRun # 启动qemu子进程,之后libvirt进程关闭cmd->passfd中的fd

    ->qemuProcessWaitForMonitor

        ->qemuConnectMonitor

            ->qemuMonitorOpen # 连接unix socket

            ->qemuProcessInitMonitor # 发送qmp_capabilities

            ->qemuMigrationCapsCheck # 发送query-migrate-capabilities

        ->qemuMonitorGetChardevInfo # 发送query-chardev

        ->qemuProcessRefreshChannelVirtioState # 增加domain agent生命周期事件

    ->qemuConnectAgent

        ->agent = qemuAgentOpen(...)

            ->agent->fd = qemuAgentOpenUnix(config->data.nix.path); # 通过unix socket连接到qemu agent,config->data.nix.path在之前的qemuDomainPrepareChannel 函数中已经设置好

            ->qemuAgentRegister(agent); # 使用glib事件循环监测qemu agent事件,处理函数为qemuAgentIO

qemu-5.0

qemu_init

    ->for循环执行chardev_init_func

        ->qemu_chr_new_from_opts

            ->backend = qemu_chr_parse_opts(opts, errp);

                ->qemu_chr_parse_socket # SOCKET_ADDRESS_LEGACY_KIND_FD

            ->chr = qemu_chardev_new(...)

                ->chardev_new

                    ->qemu_char_open

                        ->qmp_chardev_open_socket

                            ->qmp_chardev_open_socket_server

qemu-5.0对qmp命令和qga命令的处理

全局变量qmp_dispatcher_bh用于进行qmp命令

/* Bottom half to dispatch the requests received from I/O thread */
QEMUBH *qmp_dispatcher_bh;

函数monitor_init_globals_core初始化qmp_dispatcher_bh并将其处理函数设置为monitor_qmp_bh_dispatcher

void monitor_init_globals_core(void)
{
    monitor_qapi_event_init();
    qemu_mutex_init(&monitor_lock);
    /*
     * The dispatcher BH must run in the main loop thread, since we
     * have commands assuming that context.  It would be nice to get
     * rid of those assumptions.
     */
    qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(),
                                   monitor_qmp_bh_dispatcher,
                                   NULL);
}

qmp命令的入口函数是qmp_dispatch:

virsh qemu-monitor-command --hmp info block调用堆栈如下:

Breakpoint 1, 0x000055fdab6f88a0 in hmp_info_block ()

(gdb) bt

#0  0x000055fdab6f88a0 in hmp_info_block ()

#1  0x000055fdab7e3dd6 in handle_hmp_command ()

#2  0x000055fdab674d12 in qmp_human_monitor_command ()

#3  0x000055fdab807019 in qmp_marshal_human_monitor_command ()

#4  0x000055fdab8c14b0 in qmp_dispatch ()

#5  0x000055fdab7e1111 in monitor_qmp_dispatch ()

#6  0x000055fdab7e17d0 in monitor_qmp_bh_dispatcher ()

#7  0x000055fdab9081e3 in aio_bh_poll ()

#8  0x000055fdab90b59e in aio_dispatch ()

#9  0x000055fdab90807e in aio_ctx_dispatch ()

#10 0x00007f25d5f15099 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0

#11 0x000055fdab90a8e3 in main_loop_wait ()

#12 0x000055fdab67b269 in qemu_main_loop ()

#13 0x000055fdab59c08e in main ()

virsh qemu-monitor-command '{"execute": "query-block"}'调用堆栈如下:

#0  0x000055fdab87b5a0 in qmp_query_block ()

#1  0x000055fdab7fc797 in qmp_marshal_query_block ()

#2  0x000055fdab8c1536 in qmp_dispatch ()

#3  0x000055fdab7e1111 in monitor_qmp_dispatch ()

#4  0x000055fdab7e17d0 in monitor_qmp_bh_dispatcher ()

#5  0x000055fdab9081e3 in aio_bh_poll ()

#6  0x000055fdab90b59e in aio_dispatch ()

#7  0x000055fdab90807e in aio_ctx_dispatch ()

#8  0x00007f25d5f15099 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0

#9  0x000055fdab90a8e3 in main_loop_wait ()

#10 0x000055fdab67b269 in qemu_main_loop ()

#11 0x000055fdab59c08e in main ()

qga命令的入口函数是tcp_chr_read:

tcp_chr_read

    ->qemu_chr_be_write

        ->chr_read # virtconsole_realize中调用qemu_chr_fe_set_handlers将backend的读函数设置为chr_read(virtio-console.c)

            ->virtio_serial_write

                ->virtqueue_push

                ->virtio_notify

之后就是通过virtio机制通知到虚拟机内部,虚拟机内部的qemu guest agent打开的virtio串口收到消息进行处理。

 类似资料: