XenStore: 使用,结构和原理
摘要:XenStore是Xen提供的一个域间共享的存储系统,它以字符串形式存放了管理程序和前、后端驱动程序的配置信息。Dom0管理所有的数据,而DomU通过共享内存,向Dom0请求与自己相关的键值,以此实现域间通信。Xen提供了多种接口用来操作XenStore:命令行的xenstore-*命令、用户空间的xs_系列函数、内核的XenBus接口,都可以用来方便地操作XenStore的数据。
0. 动机
在Xen的半虚拟化(PV)系统中,各个虚拟机(Domain)之间可以利用Xen核心提供的授权表(Grant Table)和事件通道(Event Channel)机制进行域间通信。但是在实际使用中有这样的问题:
- Alice使用授权表共享了一个页面,但是Sanae想要拿来用,必须事先知道它的授权引用(gref);
- Alice打开一个事件通道(unbound evtchn),但是Sanae想要绑定这个通道,必须事先知道Alice使用的端口号(remote port)。
显然,想要解决这两个问题,需要Alice和Sanae之间,在建立自定义的授权表、事件通道之前就能够互相通信。
使用XenStore可以很方便地实现这种通信。
1. 快速入门
XenStore是Xen提供的一个域间共享的存储系统,它以字符串形式存放了管理程序和前、后端驱动程序的配置信息。Dom0管理所有的数据,而DomU通过共享内存,向Dom0请求与自己相关的键值,以此实现域间通信。
XenStore的存储结构类似于Dom树:每一个节点(Node)有一个字符串值(Value),并且可以有多个子节点。在Dom0启动后,XenStore的结构如下:
/
vm
{uuid}
…
local
domain
0
vm
device
control
memory
console
limit
type
name
......
1
…
tool
xenstored
在根(/)下有三个子目录,vm,local(实际上是/local/domain)和tool。vm存放虚拟机管理信息。tool暂时没有数据。而/local/domain存放了活动虚拟机配置和驱动信息。/local/domain中每一个目录项代表一个活动的虚拟机,例如/local/domain/0代表dom0。Dom0可以读写XenStore的全部数据,而Dom-x只能访问/local/domain/x的内容。需要在dom0和domx之间共享的内容,一般都写在这个目录下,所以/local/domain是最常用的目录。
Xen提供了多种接口用来操作XenStore:命令行的xenstore-*命令、用户空间的xs_系列函数、内核的XenBus接口,都可以用来方便地操作XenStore的数据。
操作 |
命令行 |
用户空间
[cpp] view plaincopyprint?
|
内核空间
[cpp] view plaincopyprint?
|
列目录 |
xenstore-ls(递归) xenstore-list(不递归) |
xs_directory |
xenbus_directory |
读写 |
xenstore-read xenstore-write |
xs_read xs_write |
xenbus_read xenbus_write xenbus_scanf xenbus_printf |
建立/删除目录 |
xenstore-mkdir xenstore-rm |
xs_mkdir xs_rm |
xenbus_mkdir xenbus_rm |
监视 |
xenstore-watch |
xs_watch xs_unwatch xs_read_watch |
xenbus_watch xenbus_unwatch |
例子
命令行
- xenstore-write example/Alice hello!
用户空间
- struct xs_handle *xh = xs_open(0);
- int xt=xs_transaction_begin(xh);
- xs_write(xh,xt,"/local/domain/0/example/Alice", "hello!", sizeof("hello!"));
- xs_transaction_end(xh, xt, 0);
- xs_close(xh);
内核空间
- struct xenbus_transaction trans;
- xenbus_transaction_start(&trans);
- xenbus_printf(trans, "example", "Alice", "Hello! trans_id = %d", trans.id);
- xenbus_transaction_end(trans, 0);
Note: example/Alice相当于/local/domain/{当前domain}/example/Alice
具体的使用方法,可以查看相应的头文件
2. xenstore结构和通信数据流程
XenStore结构
XenStore包含两个模块:Dom0用户空间的xenstored服务主模块,和Dom*里的XenBus内核模块;此外,还有各种通信接口。各个部分的功能如下:
Xenstored:Dom0用户空间的一个服务,负责管理xenstore的数据(tdb),并且处理所有的请求。
XenBus:内核模块,提供Dom*内核访问xenstore的API,提供用户空间的xenfs接口(/proc/xen/xenbus)。Dom0的Xenbus还要初始化xenstored的运行环境。
共享页面:每一个Domain(包括Dom0自身)都有一个与Dom0通信的共享页面(共享环),这个页面同时被映射到了xenstored的内存空间。也就是说这个页面可以同时被xenstored、dom0内核、和对应的DomU内核读写。Xenstored会随时监控页面,只要环被任意一方更新,xenstored就会直接处理消息,不需要内核模块干预。
Xenstore特殊文件:
文件 |
路径 |
功能 |
xenbus |
/proc/xen |
XenBus用户接口。用户把请求写入该文件,经由内核发送到共享环 |
以下只在dom0中 |
||
tdb |
/var/lib/xenstored |
xenstore的数据库,存放在dom0的/var/lib/xenstored/tdb,可以用tdbtool打开 |
socket, socket_ro |
/var/run/xenstored |
Dom0直接操纵xenstored的接口 |
xsd_port, xsd_kva |
/proc/xen |
用于Dom0初始化 |
XenStore的通信协议:
Xenstore各个模块间的通信内容——请求和回复——都封装成为一种数据包,格式定义在文件xs_wire.h中。
- struct xsd_sockmsg
- {
- uint32_t type; /* XS_??? */
- uint32_treq_id;/* Request identifier, echoed in daemon's response. */
- uint32_ttx_id; /* Transaction id (0 if not related to a transaction). */
- uint32_t len; /* Length of data following this. */
- /* Generally followed bynul-terminated string(s). */
- };
而共享页面上定义了一个环结构xenstore_domain_interface:
- #define XENSTORE_RING_SIZE 1024
- typedef uint32_t XENSTORE_RING_IDX;
- #define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE-1))
- struct xenstore_domain_interface {
- char req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */
- char rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */
- XENSTORE_RING_IDX req_cons, req_prod;
- XENSTORE_RING_IDX rsp_cons, rsp_prod;
- };
DomU通信流程 DomU的内核将请求放入共享环,这时Dom0的xenstored会直接检测到这个改变并执行操作,最后把结果返回共享页面。DomU的用户空间需要通过/proc/xen/xenbus来操作xenstore。DomU向XenStore写入数据的xs_write函数调用流程: DomU的内核调用流程: Dom0 User、Kernel 通信流程 Dom0的流程和DomU类似。但是在客户空间,可以通过socket方式直接连接xenstored进行操作,不需要通过xenbus绕圈子。这也就是老版本Xen里面,xs_domain_open和xs_daemon_open的区别。 摘要:XenStore的初始化完全在用户空间的libxl中实现。Dom0包办了所有的操作,使DomU一启动,就可以与Dom0进行xenstore通信,但是程序非常的复杂和难以维护。Xen核心仅仅为xenstore提供了两个辅助数据项,其余的完全在xen已有的架构下实现。 XenStore所进行的域间通信,在底层仍然是通过共享内存和事件通道机制进行的。但是前面已经提到,想要共享内存或者建立事件通道,必须事先进行通信。那么XenStore自身是怎样建立起这些通道的呢?有必要研究一下Xenstore的启动过程。我们一般使用命令行新建domu。例如:sudo xl create –c ubuntu.cfg 所以我们从xl所在的程序库libxl开始,仔细分析虚拟机启动过程中xenstore的初始化过程。 其中核心的步骤就是:Dom0的libxl调用libxc,替新创建的domu分配了属于DomU的共享页面和事件通道,并且通知Xenstored建立domu的管理数据结构;在分配好以后再由XenStore映射回来。这样DomU一启动,就可以从start_info结构体中获取共享内存的地址,与Dom0进行通信了。然而,这样做就意味着Dom0包办了所有的操作,并且要考虑架构相关的因素,精确地控制DomU的内存分布,同时还要避免C语言的内存泄漏;所以程序变得非常的复杂和难以维护。这种做法的另一个特点是:实现代码完全在用户空间的libxl和libxc中,完全在xen已有的架构下实现。Xen核心唯一需要做的,就是在start_info结构体中增加两个数据项store_mfn和store_port。这也说明xen与kvm相比,核心提供更多的功能。 Xenstore的监视(watch)功能很实用,在xenstore监视目标文件夹里发生的任何修改,都会通知watch的注册者。xen虚拟机的后端驱动程序,就是通过watch来检测前端设备的改变。需要注意的:(1)注册watch不需要开始一个transaction,只要用xs_open打开连接就行了。内核可以直接调用register_xenbus_watch(2)watch在注册时,xenstored会马上产生一个事件。它就是这么设计的。如果不想处理这个时间,需要设法把它忽略掉。 watch的用法:内核空间:(转自http://wiki.xen.org/xenwiki/XenBus)
- static struct xenbus_watch xb_watch = {
- .node = "memory",
- .callback = watch_target;
- };
- ret = register_xenbus_watch(&xb_watch);
- if(IS_ERR(ret)) {
- IPRINTK("Failed to initialize balloon watcher\n");
- } else {
- IPRINTK("Balloon xenbus watcher initialized\n");
- }
用户空间:用户空间和内核稍有不同。用户空间可以用xs_watch函数定义一个监视,但是不能指定回调函数,而是需要调用xs_read_watch进行阻塞式的读取,像这样:
- xs_handle *xh=xs_open(0);
- assert(xs_watch(xh, path, token));
- char **result = 0;
- result = xs_read_watch(xh, &num); //会阻塞
- free(result);
- xs_unwatch(xh, path, token);
如果不想阻塞,就得自己开一个线程用来监听。但是这里有一个bug(4.1.3版本xen):xs_read_watch不是线程安全的。如果在xs_read_watch阻塞的时候,使用pthread_cancel来终止线程,xs_read_watch不会释放所有的资源(锁)。而主线程如果调用xs_close关闭与xenstore的连接,则会因为不能得到这些资源而死锁。具体分析如下:tools/xenstore/xs.c
- char **xs_read_watch(struct xs_handle *h, unsigned int *num)
- {
- struct xs_stored_msg *msg;
- char **ret, *strings, c = 0;
- unsigned int num_strings, i;
- mutex_lock(&h->watch_mutex); //------------------>加锁
- //此处应该加一个pthread_cleanup_push(pthread_mutex_unlock, &h->watch_mutex)
- #ifdef USE_PTHREAD
- /* Wait on the condition variable for a watch to fire.
- * If the reader thread doesn't exist yet, then that's because
- * we haven't called xs_watch. Presumably the application
- * will do so later; in the meantime we just block.
- */
- while (list_empty(&h->watch_list) && h->fd != -1)
- condvar_wait(&h->watch_condvar, &h->watch_mutex);
- #else /* !defined(USE_PTHREAD) */
- /* Read from comms channel ourselves if there are no threads
- * and therefore no reader thread. */
- assert(!read_thread_exists(h)); /* not threadsafe but worth a check */
- if ((read_message(h) == -1)) //----------------------->没有消息时阻塞在这里
- return NULL;
xs_read_watch一开始加锁,紧接着调用read_message。read_message会阻塞调用read函数。如果这时主线程调用pthread_cancel来取消运行xs_read_watch的线程,h->watch_mutex就不会被释放,会造成后面的死锁。 很挫的解决办法:在调用xs_unwatch和xs_close之前,检查xs_handle内部锁的情况,强行解锁资源。xs_handle这个结构体在头文件里只有类型声明,具体的定义在xs.c文件里,仅供xenstore内部使用。要调用xs_handle内部的对象,首先得把xs_handle的详细声明提取出来:
- //used to extract (>_<) xs_handle internal members.
- typedef struct list_head {
- struct list_head *next, *prev;
- }list_head_struct;
- typedef struct
- {
- int fd;
- pthread_t read_thr;
- int read_thr_exists;
- struct list_head watch_list;
- pthread_mutex_t watch_mutex;//监视信号量,可能死锁
- pthread_cond_t watch_condvar;
- int watch_pipe[2];
- struct list_head reply_list;
- pthread_mutex_t reply_mutex;//回复信号量
- pthread_cond_t reply_condvar;
- pthread_mutex_t request_mutex;//请求信号量
- }my_xs_handle;
- #if __GNUC__ > 3
- #define offsetof(a,b) __builtin_offsetof(a,b)//提取偏移量的宏。
- #else
- #define offsetof(a,b) ((unsigned long)&(((a *)0)->b))
- #endif
然后像这样提取xh内部成员: pthread_mutex_t *pm_watch = (pthread_mutex_t *)(((void *)xh) + offsetof(my_xs_handle, watch_mutex)); 解锁的宏。其实写成个函数也行
- #define check_xh_lock(x) do {\
- pthread_mutex_t *pm = (pthread_mutex_t *)(((void *)pthis->xh) + offsetof(my_xs_handle, x)); \
- if (pthread_mutex_trylock(pm) == EBUSY){ \ //pthread_mutex_trylock 如果没上锁,则加锁;否则返回EBUSY
- cout << "thread_cleanup -> " #x " is already locked!" << endl; \
- if (0 != pthread_mutex_unlock(pm)) \
- cout << "thread_cleanup -> error unlocking!" << endl; \
- else cout << "thread_cleanup -> unlocking " #x << endl; \
- } else assert(pthread_mutex_unlock(pm)==0); \
- } while (0)
- check_xh_lock(watch_mutex);
- check_xh_lock(request_mutex);
- check_xh_lock(reply_mutex);
- cout << "----- unwatch -----" << endl;
- xs_unwatch(pthis->xh, pthis->path.c_str(), map_path("watch", pthis->path).c_str());
- check_xh_lock(watch_mutex);
- check_xh_lock(request_mutex);
- check_xh_lock(reply_mutex);
- cout << "----- close -----" << endl;
- xs_close(pthis->xh);
- pthis->xh=0;
测试如下:
- viktor@buxiang-OptiPlex-330:~/proj/xc_map$ sudo ./domu domu.cpp
- watch -> add watch on path mmap/domu-cpp callback func 0x8049aad //主线程打开监视线程
- thread_func -> begin watch thread path= mmap/domu-cpp //监视线程注册watch
- thread_func -> watch event path= mmap/domu-cpp token= watch/mmap/domu-cpp //注册时会发生一次事件,忽略之
- watch_callback -> entering
- //(在这里等待,此时按下Ctrl+C)
- ^Cmain received signal Interrupt
- unwatch -> **stopping work thread. waiting here...work thread already stopped. //主线程用pthread_cancel关闭监视线程
- thread_cleanup -> path= mmap/domu-cpp thread=b6d6eb70 //监视线程退出,主线程开始清理
- ----- unwatch -----
- map_path -> watch/mmap/domu-cpp
- thread_cleanup -> watch_mutex is already locked! //此时watch_mutex上锁了,说明xs_read_watch没有释放资源
- thread_cleanup -> unlocking watch_mutex //强行解锁
- ----- close ----- //调用xs_unwatch和xs_close关闭连接。
XenStore: 使用,结构和原理相关推荐
- 3D-camera结构光原理
3D-camera结构光原理 目前主流的深度探测技术是结构光,TOF,和双目.具体的百度就有很详细的信息. 而结构光也有双目结构光和散斑结构光等,没错,Iphone X 的3D深度相机就用 散斑结构光 ...
- camera(9)--手机摄像头技术结构与原理
科普:手机摄像头技术结构与原理 21世纪初夏普与当时的日本通信运营商J-PHONE发明了夏普 J-SH04,夏普 J-SH04具有拍照功能,2003年4月24日夏普发售了全球首款百万像素手机J-SH5 ...
- 临界区设计太大或太小有何缺点_空压机100课之016:离心式压缩机的结构、原理及优缺点...
上一课:空压机100课之015:容积式-滑片机.涡旋机的结构.原理及优缺点 在大流量的用气场合,没有例外几乎都是离心机或轴流压缩机的天下.特别是石油化工行业中,离心机是关键设备之一. •离心式压缩机普 ...
- 四旋翼飞行器基本知识(四旋翼飞行器结构和原理+四轴飞行diy全套入门教程)
转载两篇日志: 第一篇<四旋翼飞行器结构和原理> 第二篇<四旋翼飞行diy全套入门教程> =========================================== ...
- 图解RAM结构与原理,系统内存的Channel、Chip与Bank
文章目录 转载正文 标题挥发性内存分2种,SRAM和DRAM 主内存子系统 channel 和 DIMM rank 和 chip bank.row.column 内存的读写方式 越多越好,加速读写能力 ...
- 步进电机基础(2.1)- 定子相数的分类、结构、原理
步进电机基础(2.1)- 定子相数的分类.结构.原理 前言 基本信息 前言说明 定子相数的分类.结构.原理 1. 决定步距角的因素 2. 单相步进电机 3. 两相步进电机 4.三相步进电机 5. 四相 ...
- Camera基本结构及原理
** Camera基本结构及原理 ** ** 备忘 **: 文末支持一波,感谢鞠躬 一.学习目的 本模块主要是了解一个摄像头模组的基本组成,每个组成部分的主要作用是什么,同时掌握一些基本术语. 二.必 ...
- 计算机基本原理——CPU的结构与原理
一. CPU的逻辑结构 (一)冯·诺依曼计算机的结构与原理 (0)冯·诺依曼:"存储程序控制"式原理.----现代计算机的工作原理. (1) 计算机的工作由程序控制,程序是一个指令 ...
- Axure-中继器结构与原理详解
本文转自:Axure原创教程网>>中继器结构与原理详解 中继器这个元件,对很多人来说是个难点.但实际上,只要理解它的结构和工作原理,使用起来非常简单.首先,我们将中继器元件拖入画布,是 ...
最新文章
- 多视图几何总结——单应矩阵和基础矩阵的兼容关系
- 俄罗斯机器人雄鹿_在雄鹿无球可打,在火箭重获新生!哈登,你又让一人打出身价...
- Elasticsearch和Hive整合,将hive数据同步到ES中
- 小米线刷包需要解压么_【连载】刷机教程之小米手机通用线刷教程
- arma3自定义服务器,Arma3 生存服架设教程,武装突袭3游戏服务器架设
- JSON Web Token (JWT)生成Token及解密实战
- 自行车房车,还是第一次看到,惊倒一片!
- 软件测试——0319作业
- Oracle Hint(提示)与常用方法
- 【计算机组成与设计】3.4计算机CPU除法运算
- 最后的作业——NP完全问题证明
- 数字经济的网络黑手,中科信安:勒索软件攻击比去年同期增加7倍
- 7.0 站在生物学的角度看TypeScript类的继承
- (附源码)SSM学科竞赛管理JAVA计算机毕业设计项目
- 订餐系统c语言代码大全,C语言订餐系统
- C语言(经典编程题:报数游戏)
- 高仿淘宝商品详情标题栏渐变
- 购买服务器 操作系统选什么区别吗,购买服务器选择什么操作系统
- android 多个style,Android style详解
- 微课登陆显示服务器繁忙,老师为什么教别人孩子容易,教自家孩子却这么难?| 公益微课...
热门文章
- LDA基本介绍以及LDA源码分析(BLEI)
- jquery选择器玩得不6啊,只能慢慢写判断了,唉..........................
- nginx连接php-fpm sock文件失败502
- 无法解决 equal to 操作中 Chinese_PRC_CI_AS_WS 和 Chinese_PRC_CI_AS 之间的排序规则冲突...
- shell高级视频答学生while循环问题
- 长城电脑或收购夏新电子笔记本业务
- Go 语言——Tensorflow
- iis8.5部署net项目
- J-Link驱动下载和JLINK下载Hex程序
- UbuntuでPostgreSQLをインストールからリモートアクセスまでの手順