qga工作原理

QGA工作原理

qga简介

qemu通过串口设备(串口设备的速率较低,适合小数据量的交互)的模拟,为宿主机和虚拟机提供了一个数据通道(channel),这个通道的两端分别为 VM中的串口和 HOST中的unix socket文件。

-chardev socket,id=charchannel0,fd=22,server=on,wait=off -device {"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"org.qemu.guest_agent.0"}

    <channel type='unix'>
      <source mode='bind' path='/var/lib/libvirt/qemu/org.qemu.test_agent'/>
      <target type='virtio' name='org.qemu.guest_agent.0' state='connected'/>
      <alias name='channel0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>

  • -chardev socket 参数指定了一个chardev设备,其后端设备为socket,fd为对应的unix socket(path为/var/lib/libvirt/qemu/org.qemu.test_agent)的文件描述符
  • -device {…} 创建一个virtio-serial-port设备,其对应的chardev为charchannel0,其对应的名字为org.qemu.guest_agent.0,该设别会被挂载在virtio-serial-bus(virtio-serial0.0)上。在虚拟机启动时则会创建一个/dev/vport1p1设备,并为其创建一个符号链接/dev/virtio-ports/org.qemu.guest_agent.0
host:
# file /var/lib/libvirt/qemu/org.qemu.test_agent
/var/lib/libvirt/qemu/org.qemu.test_agent: socket

guest:
# systemctl status qemu-guest-agent.service 
● qemu-guest-agent.service - QEMU Guest Agent
    Loaded: loaded (/usr/lib/systemd/system/qemu-guest-agent.service; enabled; preset: enabled)
    ...
    CGroup: /system.slice/qemu-guest-agent.service
             └─802 /usr/bin/qemu-ga --method=virtio-serial --path=/dev/virtio-ports/org.qemu.guest_agent.0 --allow-rpcs=guest-sync-delimited,guest-sync,guest

# readlink /dev/virtio-ports/org.qemu.guest_agent.0
../vport2p1
# file /dev/vport2p1 
/dev/vport2p1: character special (241/1)

因此宿主机端可以和/var/lib/libvirt/qemu/org.qemu.test_agent这个unix socket建立连接,从而与虚拟机内部的qga进行通信。

qga与qemu之间的关系如下所示
在这里插入图片描述

qemu创建一个virtserialport virtio串口设备,该设备有一个chardev设备,该设备的后端为unix socket,对应的文件为/var/lib/libvirt/qemu/org.qemu.test_agent,qemu会将该unix socket对应的fd加入到事件监听的主循环中。chardev设备主要用于提供虚拟机与外部的连接,chardev设备的后端用于表示数据传输的方式(有的chardev基于文件,有的网络,有的基于管道)。在内核驱动和该virtio串口设备匹配时则会在sysfs下创建一个/dev/vport1p1设备提供给用户态,并将设备状态设置为已连接。

当虚拟机外部需要向qga发出请求,使其设置或者获取虚拟机内部的一些信息时,就要向/var/lib/libvirt/qemu/org.qemu.test_agent这个Unix socket文件写入请求数据,请求数据的格式与qmp一样,也是Json格式。当该unix socket接收到数据时,就会唤醒主循环,串口设备读取数据,然后填写virtio中的vring结构,向设备注入一个中断。

设备接收到这个中断后会读取数据,并唤醒用户态的qga进程。qga本身是一个事件循环监听串口/dev/vport2p1的数据,当它由于串口数据被唤醒时就会按照请求进行处理。请求应答的数据格式与qmp一样也是json格式,当应答数据生成好以后就通过virtio串口设备向qemu发送数据。QEMU则通过chardev设备向连接/var/lib/libvirt/qemu/org.qemu的对端发送其对应的请求应答数据。

qga源码解析

GAState和GAConfig分别用于表示qga的整个状态和配置文件,

qemu/qga/main.c

int main(int argc, char **argv)
{
    int ret = EXIT_SUCESS;
    GAState *s;
    GAConfig *config = g_new0(GAConfig, 1);
    int socket_activation;

    ...
    qga_qmp_init_marshal(&ga_commands); //将qga支持的命令注册到系统中
    ...
    config_parse(config, argc, argv);
    ...
    config_dump(config);
    ...
    // 该函数中会调用json_message_parser_init(&s->parser, process_event, s, NULL)
    // 将s->parser的emit函数注册为process_event
    s = initialize_agent(config, socket_activation); 
    ...
    ret = run_agent(s);
    ...
    cleanup_agent(s);
}

struct GAState {
    JSONMessageParser parser;   // 解析json对象,并调用设置的回调函数
    GMainLoop *main_loop;   // 表示qga的主循环
    GAChannel *channel;     // 表示virtio serial端口设备的通道
    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
    GACommandState *command_state;
    GLogLevelFlags log_level;
    FILE *log_file;
    bool logging_enabled;
#ifdef _WIN32
    GAService service;
    HANDLE wakeup_event;
    HANDLE event_log;
#endif
    bool delimit_response;
    bool frozen;
    GList *blockedrpcs;
    GList *allowedrpcs;
    char *state_filepath_isfrozen;
    struct {
        const char *log_filepath;
        const char *pid_filepath;
    } deferred_options;
#ifdef CONFIG_FSFREEZE
    const char *fsfreeze_hook;
#endif
    gchar *pstate_filepath;
    GAPersistentState pstate;
    GAConfig *config;
    int socket_activation;
    bool force_exit;
};

struct GAConfig {
    char *channel_path; //串口端口的路径
    char *method;   // 串口端口的类型,可以是virtio-serial或isa
    char *log_filepath;
    char *pid_filepath;
#ifdef CONFIG_FSFREEZE
    char *fsfreeze_hook;
#endif
    char *state_dir;
#ifdef _WIN32
    const char *service;
#endif
    gchar *bliststr; /* blockedrpcs may point to this string */
    gchar *aliststr; /* allowedrpcs may point to this string */
    GList *blockedrpcs;
    GList *allowedrpcs;
    int daemonize;  // 是否守护进程运行
    GLogLevelFlags log_level;
    int dumpconf;
    bool retry_path;
};

在qmp_command的构造完成以及程序的初始化完成后,开始调用run_agent函数,

static int run_agent(GAState *s)
{
    int ret = EXIT_SUCCESS;

    s->force_exit = false;

    do {
        ret = run_agent_once
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值