本人目前就职于烽火集成,从事云计算产品架构设计相关工作,长期专注于内核、虚拟化、分布式、云计算等方向。
技术交流请联系:xiaoding@fiberhome.com
在虚拟化场景下,对于VM的访问可以使用VNC等可视化工具来操作。VNC的原理其实很简单,
qemu会对每一个虚拟机模拟一块网卡,而VM的显示信息都会留在这个网卡的显存中。qemu启动一个
VNC server,这个server其实就一个定时器,以一定的频率默认是(30ms)从显存中拿出显示的信息,
然后,当有VNC client连接上以后,定期的发送给VNC client就可以了。
这里描述的只是简单的流程。其实传输一个画面的像素还是需要非常大量的网络带宽的。所以为了显示效果
VNC仅仅传输屏幕上变化的像素,以及在传输中启用了视频压缩算法。
接下来就从代码角度来分析下这一流程。
首先是vnc入口
在vl.c中的main函数对vnc注册了初始化函数vnc_init_func,
ui/vnc.c中包含了vnc-server所需要的主要代码。
vnc_init_func直接调用了vnc真正的初始化函数,vnc_display_init和vnc_display_open,其实早期的qemu在vl.c中
是直接调用这2个函数的。
函数vnc_display_init在开始的时候主要是将启动线程,同时注册dcl_ops
vnc_display_open做了参数校验,然后根据指定的形式启动了vnc server监听端口。
由于函数太大,这里截取最关键的部分,同时代码可以看到,默认端口是5900
dcl_ops 定义了vnc的相关操作,
好了,那么当vnc连接时,就会触发vnc_connect函数,主要是初始化连接的client的socket,
以及注册回调等。最关键的会调用vnc_init_state。
vnc_init_state函数会和client进行vnc协议的版本协商等。
此时,会触发vnc_dpy_switch函数。早期版本是没有这个的,但是相同功能函数为vnc_dpy_resize
该函数主要初始化了一个空间server surface,这个就是用来缓存从显存读取出的屏幕像素变化。
guest surface则对应了每一个连接,自己需要得到刷新的像素。
vnc_desktop_resize 可以看到比较简单,就是调用vnc_flush来进行数据的传输
此后,定期刷新client端就会调用vnc_refresh,主要通过vnc_refresh_server_surface来让显存和
server surface对比,然后取出变化的像素,并通过vnc_update_client建立job,插入到发送队列中
graphic_hw_update函数式从硬件中来进行显存更新。这里不展开来讲。
vnc_refresh_server_surface是对像素进行对比。
vnc_update_client,开始建立一个job,并将需要更新的像素放在job中,然后放入队列中。
在前面vnc初始化时,初始化了一个线程,这个线程就会从队列中取出一个job,然后发送给client
该线程函数就是vnc_worker_thread_loop