1. 系统启动流程
- 启动服务(obmc-ikvm.service):ExecStart=/usr/bin/obmc-ikvm -v /dev/video0 -k /dev/hidg0 -p /dev/hidg1
- ikvm_server.cpp/mian()函数创建Args和Manager对象,解析输入参数和创建管理服务ikvm::Manager manager(args)
- ikvm_manager.cpp 创建 Manager 对象的时候,初始化 input/video/server 对象,三个对象在 ikvm_manager.hpp 头文件中创建
Manager::Manager(const Args& args) :
continueExecuting(true), serverDone(false), videoDone(true),
input(args.getKeyboardPath(), args.getPointerPath(), args.getUdcName()),
video(args.getVideoPath(), input, args.getFrameRate(),
args.getSubsampling()),
server(args, input, video) //创建Server对象
{}
void Manager::run()
{
std::thread run(serverThread, this);
..........
}
void Manager::serverThread(Manager* manager)
{
while (manager->continueExecuting)
{
manager->server.run(); //执行Server.run()函数
manager->setServerDone();
manager->waitVideo();
}
}
- 创建 Server 对象的时候会 通过rfbGetScreen()创建 vncserver 的屏幕缓冲结构体 rfbScreenInfoPtr rfbScree, Server::run()函数调用 rfbProcessEvents()
rfbScreenInfoPtr server;
Server::Server(const Args& args, Input& i, Video& v) :
pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
{
std::string ip("localhost");
const Args::CommandLine& commandLine = args.getCommandLine();
int argc = commandLine.argc;
// ikvm_server.hpp: rfbScreenInfoPtr server
server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(), //在libvncserver中calloc内存,并对结构体初始化赋值
video.getHeight(), Video::bitsPerSample, //代码位置:src/libvncserver/main.c
Video::samplesPerPixel, Video::bytesPerPixel);
..............
}
void Server::run()
{ // libvncserver/main.c/L1261/rfbBool rfbProcessEvents(rfbScreenInfoPtr screen,long usec)
rfbProcessEvents(server, processTime); // 与libvncserver相关的函数、处理事件从这里开始
// 代码位置:src/libvncserver/main.c
if (server->clientHead)
{
frameCounter++;
if (pendingResize && frameCounter > video.getFrameRate())
{
doResize();
pendingResize = false;
}
}
}
- libvncserver/mian.c/rfbProcessEvents()是整个 libvncserver 的入口,套接字监听及消息处理关键在**rfbCheckFds(screen,usec)**函数
//obmc-ikvm函数入口:Server::run()
rfbBool rfbProcessEvents(rfbScreenInfoPtr screen,long usec)
{
rfbClientIteratorPtr i;
rfbClientPtr cl,clPrev;
rfbBool result=FALSE;
extern rfbClientIteratorPtr
rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen);
/* 如果未指定超时时间,使用屏幕的延迟更新时间 */
if(usec<0)
usec=screen->deferUpdateTime*1000;
/* 检查文件描述符集合中的活动,处理网络 I/O 事件 */
rfbCheckFds(screen,usec);
/* 检查 HTTP 文件描述符集合中的活动,处理 HTTP 请求 */
rfbHttpCheckFds(screen);
/* 获取包含已关闭连接的客户端迭代器 */
i = rfbGetClientIteratorWithClosed(screen);
/* 遍历所有客户端连接 */
cl=rfbClientIteratorHead(i);
while(cl) {
/* 更新客户端屏幕 */
result = rfbUpdateClient(cl);
/* 保存当前客户端指针,以便在迭代器前进后检查连接状态 */
clPrev=cl;
cl=rfbClientIteratorNext(i);
/* 如果客户端套接字无效,表示连接已关闭 */
if(clPrev->sock==RFB_INVALID_SOCKET) {
/* 处理客户端连接断开事件 */
rfbClientConnectionGone(clPrev);
result=TRUE;
}
}
/* 释放客户端迭代器 */
rfbReleaseClientIterator(i);
return result;
}
- rfbCheckFds 主要干两件事:监听新的 socket 连接和转发正常消息(rfbHttpCheckFds 监听 Http 连接,功能类似)
- **rfbNewTCPOrUDPClient()**函数是初始化rfbClientPtr cl 和迭代器rfbClientIteratorPtr 的地方,同时也是调用newClientHook()钩子函数的地方(回调在 ikvm_server.cpp 中定义enum rfbNewClientAction Server::newClient(rfbClientPtr cl))
2. 客户端迭代器
2.1 动态链表
//libvncserver/src/libvncserver/rfbserver.c
struct rfbClientIterator
{
rfbClientPtr next;
rfbScreenInfoPtr screen;
rfbBool closedToo;
}
struct rfbClientIterator;
//libvncserver/include/rfb/rfb.h
//屏幕帧缓冲区结构体
typedef struct _rfbScreenInfo
{
void* screenData; //ikvm_server.cpp中定义:Server对象
char* frameBuffer; //ikvm_server.cpp中定义:宽*高*bytesPerPixel
struct _rfbClientRec* clientHead; //存储最后一个建立会话的cl信息
struct _rfbClientRec* pointerClient; /**< "Mutex" for pointer events */
.................
} rfbScreenInfo, *rfbScreenInfoPtr;
//libvncserver/include/rfb/rfb.h
//连接到VNC服务器的客户端信息
typedef struct _rfbClientRec {
rfbScreenInfoPtr screen;
void* clientData;
struct _rfbClientRec *prev; //创建的最后cl为NULL,其他的见下图
struct _rfbClientRec *next; //创建的首个cl为NULL,其他的见下图
.................
} rfbClientRec, *rfbClientPtr;
假如定义:rfbClientIterator i = rfbGetClientIterator(screen),启动了 3 个 KVM 会话:rfbClientPtr cl0 cl1 cl2
- rfbScreen 是在创建 obmc-ikvm :Server 对象时创建的,是唯一的
- Server.run() -> fbProcessEvents(server, processTime) -> i->screen = rfbScreen, i->next = screen->clientHead [libvncserver/main.c/fbProcessEvents(L1278)]
- CL 是双向动态链表,其赋值逻辑为:libvncserver/src/libvncserver/rfbserver.c/rfbNewTCPOrUDPClient(L470)
//libvncserver/src/libvncserver/rfbserver.c/rfbNewTCPOrUDPClient(L470)
static rfbClientPtr
rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
rfbSocket sock,
rfbBool isUDP)
{
.................
cl->next = rfbScreen->clientHead; //首个会话为clientHead = NULL,指向前一个cl
cl->prev = NULL;
if (rfbScreen->clientHead)
rfbScreen->clientHead->prev = cl; //修改前一个cl的prev值
rfbScreen->clientHead = cl; //永远存储最后一个创建的会话cl
.................
}
2.2 初始化
首次初始化,在 libvncserver/main.c/rfbProcessEvents()函数中完成,具体场景为:
i = rfbGetClientIteratorWithClosed(screen); //malloc迭代器结构体,初始化赋值i->screen = rfbScreen
cl=rfbClientIteratorHead(i); //获取最后建立的客户端,cl = i->next = i->screen->clientHead
while(cl) {
/* 更新客户端屏幕 */
result = rfbUpdateClient(cl);
/* 保存当前客户端指针,以便在迭代器前进后检查连接状态 */
clPrev=cl;
cl=rfbClientIteratorNext(i); //动态链表查询,直到查询到第一个建立的客户端,其cl0->next = NULL
。。。。。
}
2.3 应用场景
- 创建并获取迭代器:it = rfbGetClientIterator(server)
- 动态链表查询客户:cl = rfbClientIteratorNext(it),查询到首个客户端时,cl0->next = NULL,从而退出