libvirt-virsh代码解读
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代码解读相关推荐
- 200行代码解读TDEngine背后的定时器
作者 | beyondma来源 | CSDN博客 导读:最近几周,本文作者几篇有关陶建辉老师最新的创业项目-TdEngine代码解读文章出人意料地引起了巨大的反响,原以为C语言已经是昨日黄花,不过从读 ...
- 装逼一步到位!GauGAN代码解读来了
↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:游璐颖,福州大学,Datawhale成员 AI神笔马良 如何装逼一 ...
- Unet论文解读代码解读
论文地址:http://www.arxiv.org/pdf/1505.04597.pdf 论文解读 网络 架构: a.U-net建立在FCN的网络架构上,作者修改并扩大了这个网络框架,使其能够使用很少 ...
- Lossless Codec---APE代码解读系列(二)
APE file 一些概念 APE代码解读系列(一) APE代码解读系列(三) 1. 先要了解APE compression level APE主要有5level, 分别是: CompressionL ...
- RT-Thread 学习笔记(五)—— RTGUI代码解读
---恢复内容开始--- RT-Thread 版本:2.1.0 RTGUI相关代码解读,仅为自己学习记录,若有错误之处,请告知maoxudong0813@163.com,不胜感激! GUI流程: ma ...
- vins 解读_代码解读 | VINS 视觉前端
AI 人工智能 代码解读 | VINS 视觉前端 本文作者是计算机视觉life公众号成员蔡量力,由于格式问题部分内容显示可能有问题,更好的阅读体验,请查看原文链接:代码解读 | VINS 视觉前端 v ...
- BERT:代码解读、实体关系抽取实战
目录 前言 一.BERT的主要亮点 1. 双向Transformers 2.句子级别的应用 3.能够解决的任务 二.BERT代码解读 1. 数据预处理 1.1 InputExample类 1.2 In ...
- shfflenetv2代码解读
shufflenetv2代码解读 目录 shufflenetv2代码解读 概述 shufflenetv2网络结构图 shufflenetv2架构参数 shufflenetv2代码细节分析 概述 shu ...
- GoogLeNet代码解读
GoogLeNet代码解读 目录 GoogLeNet代码解读 概述 GooLeNet网络结构图 1)从输入到第一层inception 2)从第2层inception到第4层inception 3)从第 ...
- Inception代码解读
Inception代码解读 目录 Inception代码解读 概述 Inception网络结构图 inception网络结构框架 inception代码细节分析 概述 inception相比起最开始兴 ...
最新文章
- 第30本:《怎样解题》
- matlab ga rbf,GA PSO优化的RBF神经网络
- OAF_开发系列11_实现OAF通过DataBoundValues动态显示表列的左右对齐
- 【存储知识学习】第三章磁盘原理与技术3.10 固态存储介质和固态硬盘-《大话存储》阅读笔记
- 支付宝前端开源框架Alice(解决各个浏览器的样式不一致的问题)
- Sql plus命令报command not found的解决笔记
- primefaces_PrimeFaces扩展中的全新JSF组件
- 事务的四大特性和隔离级别
- python web开发项目 源码_Python + Flask 项目开发实践系列七
- C/C++ volatile
- 大型网站的特点及优化方向是什么
- 计算机网络典型的通信协议有,常用的通信网络协议有哪几种
- 微信+WeTest:小程序云端测试系统上线
- 黑马程序员——OC语言------类的声明实现、面向对象
- ROM,PROM,EPROM,EEPROM及FLASH存储器的区别
- 澳洲值得代购物品汇总
- ncnn param文件及bin模型可视化解析
- 冯山C语言第六章作业答案,C语言四川师范大学信息与计算科学冯山实验九课案.docx...
- tif格式怎么转jpg,tif转jpg步骤
- base64 转各种类型的图片