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 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 连接,功能类似)

OpenBMC开发之obmc-ikvm与libvncserver的连理关系_第1张图片

  • **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
    .................
}

OpenBMC开发之obmc-ikvm与libvncserver的连理关系_第2张图片

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,从而退出

你可能感兴趣的:(OpenBMC,数据库)