OpenBMC开发之obmc-ikvm与libvncserver的连理关系
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 serverserver = rfbGetScreen(&argc, commandLine.argv, video.getWidth(), //在libvncserver中calloc内存,并对结构体初始化赋值video.getHeight(), Video::bitsPerSample, //代码位置:src/libvncserver/main.cVideo::samplesPerPixel, Video::bytesPerPixel);..............
}void Server::run()
{ // libvncserver/main.c/L1261/rfbBool rfbProcessEvents(rfbScreenInfoPtr screen,long usec)rfbProcessEvents(server, processTime); // 与libvncserver相关的函数、处理事件从这里开始// 代码位置:src/libvncserver/main.cif (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 rfbClientIteratorPtrrfbGetClientIteratorWithClosed(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中定义:宽*高*bytesPerPixelstruct _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,指向前一个clcl->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 = rfbScreencl=rfbClientIteratorHead(i); //获取最后建立的客户端,cl = i->next = i->screen->clientHeadwhile(cl) {/* 更新客户端屏幕 */result = rfbUpdateClient(cl);/* 保存当前客户端指针,以便在迭代器前进后检查连接状态 */clPrev=cl;cl=rfbClientIteratorNext(i); //动态链表查询,直到查询到第一个建立的客户端,其cl0->next = NULL。。。。。}
2.3 应用场景
- 创建并获取迭代器:it = rfbGetClientIterator(server)
- 动态链表查询客户:cl = rfbClientIteratorNext(it),查询到首个客户端时,cl0->next = NULL,从而退出