virsh是libvirt的一个命令行工具。相当于libvirt的一个客户端(libvirtd是服务器)。每次执行virsh命令,程序是从virsh.c中的main函数开始执行。

在virsh中有几个比较重要的结构体,其一如下:

struct _vshControl {const char *name;           /* hardcoded name of the binary that cannot* be changed without recompilation compared* to program name */char *connname;             /* connection name */char *progname;             /* program name */vshCmd *cmd;                /* the current command */char *cmdstr;               /* string with command */bool imode;                 /* interactive mode? */bool quiet;                 /* quiet mode */bool timing;                /* print timing info? */int debug;                  /* print debug messages? */char *logfile;              /* log file name */int log_fd;                 /* log file descriptor */char *historydir;           /* readline history directory name */char *historyfile;          /* readline history file name */virThread eventLoop;virMutex lock;bool eventLoopStarted;bool quit;int eventPipe[2];           /* Write-to-self pipe to end waiting for an* event to occur */int eventTimerId;           /* id of event loop timeout registration */int keepalive_interval;     /* Client keepalive interval */int keepalive_count;        /* Client keepalive count */# ifndef WIN32struct termios termattr;    /* settings of the tty terminal */
# endifbool istty;                 /* is the terminal a tty */const vshClientHooks *hooks;/* mandatory client specific hooks */void *privData;             /* client specific data */
};

从virsh.c中main函数开始分析

    if (!(progname = strrchr(argv[0], '/')))progname = argv[0];elseprogname++;ctl->progname = progname;

该部分是获取命令的名字,命令行启动virsh有两种方法,一是直接virsh xxx 二是利用绝对路径启动。对于virsh命令,progname就是“virsh”

    if (isatty(STDIN_FILENO)) {ctl->istty = true;if (tcgetattr(STDIN_FILENO, &ctl->termattr) < 0)ctl->istty = false;}

该部分就是获取标准输入设备的一些信息。

    if (virMutexInit(&ctl->lock) < 0) {vshError(ctl, "%s", _("Failed to initialize mutex"));return EXIT_FAILURE;}

初始化锁。

    if (virInitialize() < 0) {vshError(ctl, "%s", _("Failed to initialize libvirt"));  return EXIT_FAILURE;}

virInitialize函数的实现如下

int
virInitialize(void)
{if (virOnce(&virGlobalOnce, virGlobalInit) < 0)return -1;if (virGlobalError)return -1;return 0;
}

在多线程环境里面,virGlobalInit函数只执行一次,该函数对于virsh端主要初始化如下

virErrorInitialize();
virLogSetFromEnv();
remoteRegister();

virErrorInitialize函数用于初始化错误信息变量,该变量是线程特定数据。具体调用方式virThreadLocalInit(&virLastErr, virLastErrFreeData);virLastErr就是一个线程特定数据。
virLogSetFromEnv函数的实现如下

void
virLogSetFromEnv(void)
{const char *debugEnv;if (virLogInitialize() < 0)return;debugEnv = virGetEnvAllowSUID("LIBVIRT_DEBUG");if (debugEnv && *debugEnv)virLogParseDefaultPriority(debugEnv); /*virLogDefaultPriority*/debugEnv = virGetEnvAllowSUID("LIBVIRT_LOG_FILTERS");if (debugEnv && *debugEnv)virLogParseFilters(debugEnv);  /*LOGµÈ¼¶*/debugEnv = virGetEnvAllowSUID("LIBVIRT_LOG_OUTPUTS");if (debugEnv && *debugEnv)virLogParseOutputs(debugEnv);  /*Êä³öµ½ÏµÍ³ÈÕÖ¾»¹ÊÇ×Ô¼ºµÄ?*/
}

可以看见virLogSetFromEnv函数是用来从系统获取LIBVIRT_DEBUG,LIBVIRT_LOG_FILTERS,LIBVIRT_LOG_OUTPUTS三个配置信息,如果获取到则将配置信息赋值给对应的全局变量。最终LIBVIRT_DEBUG的值会赋值给virLogDefaultPriority。LIBVIRT_LOG_FILTERS的值赋值给virLogFilters。LIBVIRT_LOG_OUTPUTS的值赋值给virLogOutputs。

remoteRegister函数是初始化远程模块。在virsh端没有注册真正的虚拟化模块,知识注册了一个远程模块。

int
remoteRegister(void)
{if (virRegisterConnectDriver(&connect_driver,false) < 0)return -1;if (virRegisterStateDriver(&state_driver) < 0)return -1;return 0;
}

virRegisterConnectDriver函数是将connect_driver变量赋值给virConnectDriverTab数组,该数组存储已经注册的驱动。connect_driver结构中包含了针对remote模块各种操作的接口。connect_driver的初始化如下

static virConnectDriver connect_driver = {.hypervisorDriver = &hypervisor_driver,.interfaceDriver = &interface_driver,.networkDriver = &network_driver,.nodeDeviceDriver = &node_device_driver,.nwfilterDriver = &nwfilter_driver,.secretDriver = &secret_driver,.storageDriver = &storage_driver,
};

重新回到main函数中,接下来是

    if ((defaultConn = virGetEnvBlockSUID("VIRSH_DEFAULT_CONNECT_URI")))ctl->connname = vshStrdup(ctl, defaultConn);

该部分从系统环境里面获取默认连接的名字。比如qemu://system

    if (!vshInit(ctl, cmdGroups, NULL))exit(EXIT_FAILURE);

该部是初始化操作函数数组。cmdGroups是一个全局的变量,该变量存储了和通过命令行敲入操作匹配的操作函数。该函数里面还调用了vshInitDebug(ctl); vshInitDebug函数是获取日志等级和日志文件。

接下来是virshParseArgv函数,该函数会解析命令行传进来的参数。先解析有没有传入配置项,最后再调用vshCommandStringParse函数或者vshCommandArgvParse函数去解析真正的操作参数。

    if (argc == optind) {  /*no option , command is onli "virsh"*/ctl->imode = true;} else {/* parse command */ctl->imode = false;if (argc - optind == 1) {vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);return vshCommandStringParse(ctl, argv[optind]);} else {return vshCommandArgvParse(ctl, argc - optind, argv + optind);}}

经过解析过会,程序会调用vshCmddefSearch函数从cmdGroups数组中寻找对应的操作函数。然后再获取传进来的操作的参数。并将参数存储在opts链表里,获取到的操作函数则存储在def里。opts和def是在_vshCmd结构体中定义的,如下:

struct _vshCmd {const vshCmdDef *def;       /* command definition */vshCmdOpt *opts;            /* list of command arguments */vshCmd *next;      /* next command */
};

最后将该vshCmd结构体变量存储在ctl->cmd中。

参数解析完毕后回到main中开始执行virshInit函数。
该函数中会执行virEventRegisterDefaultImpl函数,该函数中先执行virEventPollInit来创建一个管道,并将管道的读端加入到eventLoop数组中。读端的事件服务函数仅仅是从管道中读取一字节,然后丢弃。
然后再调用virEventRegisterImpl函数去初始化全局变量addHandleImpl、updateHandleImpl、removeHandleImpl、addTimeoutImpl、updateTimeoutImpl、removeTimeoutImpl。

virEventRegisterDefaultImpl函数执行完后,开始调用如下函数

    if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0)return false;

该部分先创造一个线程,该线程会监听eventLoop中的所有描述符。如果有期待时间发生,则调用事件处理函数。
最后调用virshReconnect(ctl)函数去连接到对应的模块。

最后去调用vshCommandRun(ctl, ctl->cmd);
该函数里会调用cmd->def->handler(ctl, cmd),最总就会调用的寻找到的remote端的操作函数。然后解析返回值来判断操作是否正确。

libvirt-virsh代码解读相关推荐

  1. 200行代码解读TDEngine背后的定时器

    作者 | beyondma来源 | CSDN博客 导读:最近几周,本文作者几篇有关陶建辉老师最新的创业项目-TdEngine代码解读文章出人意料地引起了巨大的反响,原以为C语言已经是昨日黄花,不过从读 ...

  2. 装逼一步到位!GauGAN代码解读来了

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:游璐颖,福州大学,Datawhale成员 AI神笔马良 如何装逼一 ...

  3. Unet论文解读代码解读

    论文地址:http://www.arxiv.org/pdf/1505.04597.pdf 论文解读 网络 架构: a.U-net建立在FCN的网络架构上,作者修改并扩大了这个网络框架,使其能够使用很少 ...

  4. Lossless Codec---APE代码解读系列(二)

    APE file 一些概念 APE代码解读系列(一) APE代码解读系列(三) 1. 先要了解APE compression level APE主要有5level, 分别是: CompressionL ...

  5. RT-Thread 学习笔记(五)—— RTGUI代码解读

    ---恢复内容开始--- RT-Thread 版本:2.1.0 RTGUI相关代码解读,仅为自己学习记录,若有错误之处,请告知maoxudong0813@163.com,不胜感激! GUI流程: ma ...

  6. vins 解读_代码解读 | VINS 视觉前端

    AI 人工智能 代码解读 | VINS 视觉前端 本文作者是计算机视觉life公众号成员蔡量力,由于格式问题部分内容显示可能有问题,更好的阅读体验,请查看原文链接:代码解读 | VINS 视觉前端 v ...

  7. BERT:代码解读、实体关系抽取实战

    目录 前言 一.BERT的主要亮点 1. 双向Transformers 2.句子级别的应用 3.能够解决的任务 二.BERT代码解读 1. 数据预处理 1.1 InputExample类 1.2 In ...

  8. shfflenetv2代码解读

    shufflenetv2代码解读 目录 shufflenetv2代码解读 概述 shufflenetv2网络结构图 shufflenetv2架构参数 shufflenetv2代码细节分析 概述 shu ...

  9. GoogLeNet代码解读

    GoogLeNet代码解读 目录 GoogLeNet代码解读 概述 GooLeNet网络结构图 1)从输入到第一层inception 2)从第2层inception到第4层inception 3)从第 ...

  10. Inception代码解读

    Inception代码解读 目录 Inception代码解读 概述 Inception网络结构图 inception网络结构框架 inception代码细节分析 概述 inception相比起最开始兴 ...

最新文章

  1. 第30本:《怎样解题》
  2. matlab ga rbf,GA PSO优化的RBF神经网络
  3. OAF_开发系列11_实现OAF通过DataBoundValues动态显示表列的左右对齐
  4. 【存储知识学习】第三章磁盘原理与技术3.10 固态存储介质和固态硬盘-《大话存储》阅读笔记
  5. 支付宝前端开源框架Alice(解决各个浏览器的样式不一致的问题)
  6. Sql plus命令报command not found的解决笔记
  7. primefaces_PrimeFaces扩展中的全新JSF组件
  8. 事务的四大特性和隔离级别
  9. python web开发项目 源码_Python + Flask 项目开发实践系列七
  10. C/C++ volatile
  11. 大型网站的特点及优化方向是什么
  12. 计算机网络典型的通信协议有,常用的通信网络协议有哪几种
  13. 微信+WeTest:小程序云端测试系统上线
  14. 黑马程序员——OC语言------类的声明实现、面向对象
  15. ROM,PROM,EPROM,EEPROM及FLASH存储器的区别
  16. 澳洲值得代购物品汇总
  17. ncnn param文件及bin模型可视化解析
  18. 冯山C语言第六章作业答案,C语言四川师范大学信息与计算科学冯山实验九课案.docx...
  19. tif格式怎么转jpg,tif转jpg步骤
  20. base64 转各种类型的图片

热门文章

  1. 设计模式(二)--UML类图和设计模式概述
  2. 学习建议(to小班)
  3. WPF 组态软件实现思路(WPF控件可视化布局)
  4. 网站WEB前端开发需要掌握什么技术
  5. 【Linux】Linux入门必会基础
  6. RGB888与RGB565颜色
  7. 蜂鸣器+按键+led灯组合使用
  8. [乐意黎转载]依依服装店某一天将两件不同的衣服均以每件120元出售,结果一件赚20%,另一件赔20%,那么商店老板是赚了,还是亏了?赚(亏)了多少元?
  9. 眼睛不舒服就滴眼药水,但眼药水不是你想用就能用的!
  10. 移动端App与后台服务的对接方案