QEMU的入口main函数所在的源文件vl.c中针对QEMU_OPTION_vnc对vnc参数进行解析,vnc选项支持的具体参数在ui/vnc.c中进行定义,如下所示:
static QemuOptsList qemu_vnc_opts = {
.name = "vnc",
.head = QTAILQ_HEAD_INITIALIZER(qemu_vnc_opts.head),
.implied_opt_name = "vnc",
.desc = {
{
.name = "vnc",
.type = QEMU_OPT_STRING,
},{
.name = "websocket",
.type = QEMU_OPT_STRING,
},{
.name = "tls-creds",
.type = QEMU_OPT_STRING,
},{
/* Deprecated in favour of tls-creds */
.name = "x509",
.type = QEMU_OPT_STRING,
},{
.name = "share",
.type = QEMU_OPT_STRING,
},{
.name = "display",
.type = QEMU_OPT_STRING,
},{
.name = "head",
.type = QEMU_OPT_NUMBER,
},{
.name = "connections",
.type = QEMU_OPT_NUMBER,
},{
.name = "to",
.type = QEMU_OPT_NUMBER,
},{
.name = "ipv4",
.type = QEMU_OPT_BOOL,
},{
.name = "ipv6",
.type = QEMU_OPT_BOOL,
},{
.name = "password",
.type = QEMU_OPT_BOOL,
},{
.name = "reverse",
.type = QEMU_OPT_BOOL,
},{
.name = "lock-key-sync",
.type = QEMU_OPT_BOOL,
},{
.name = "key-delay-ms",
.type = QEMU_OPT_NUMBER,
},{
.name = "sasl",
.type = QEMU_OPT_BOOL,
},{
/* Deprecated in favour of tls-creds */
.name = "tls",
.type = QEMU_OPT_BOOL,
},{
/* Deprecated in favour of tls-creds */
.name = "x509verify",
.type = QEMU_OPT_STRING,
},{
.name = "acl",
.type = QEMU_OPT_BOOL,
},{
.name = "lossy",
.type = QEMU_OPT_BOOL,
},{
.name = "non-adaptive",
.type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
};
QEMU的VNC Server架构中主要包含以下几个概念:
1. 显卡,即VNC Server提供的图像所对应的显卡,可以是qxl显卡,也可以是cirrus等,这是VNC Server图像的来源。
2. VncDisplay,即QEMU中定义的代表一个VNC Server的结构体,即一个VncDisplay代表一个VNC Server
3. VncState,即VNC Server中针对每个VNC Client保存的一个状态信息
4. VncClient,即对应到VNC客户端
5. VNCJobQueue,即VNC Server中用于存放更新VNC客户端的工作队列
其具体关系如下图所示:
在QEMU的源码中入口函数vl.c:main()中,先会调用module_call_init(MODULE_INIT_OPTS),该函数会调用到ui/vnc.c:vnc_register_config()添加QEMU对VNC参数的支持。然后调用qemu_opts_foreach(qemu_find_opts("vnc"), vnc_init_func, NULL, NULL);对QEMU命令行中的VNC参数进行解析,在解析的过程中会调用vnc_init_func()建立VNC Server,并对VNC Server进行相应的初始化。
vnc_init_func()的具体源码如下所示:
vnc_init_func()函数的具体操作如下所示:
vnc_init_func()
总的来说,vnc_init_func()函数会在QEMU的VNC Server上建立如下结构的系统结构:
首先是在显卡和VncDisplay之间建立起两个定时器,用于更新VNC Server画面的鼠标和图像的显示,将需要更新的图像拆分成一个个的VncJob,放到VncJobQueue中。VNC Server中会对客户端连接的IO端口进行监听,如果有新的连接请求,则会创建新的VNC State,用于代表一个VNC客户端的连接。另外VNC Server中会起一个VNC worker thread的线程,该worker thread会一直检测VncJobQueue中是否有需要处理的VncJob,如果有,则会将VncJob进行处理和转换,将转换后的数据放到代表VNC客户端连接的VncState结构体中,等待进一步处理。最后VNC worker thread会对VNC的bh进行调度,该bh的处理函数会将VncState output buffer中的数据写到具体的io socket中。