QEMU IO事件处理框架
Qemu IO事件处理框架
qemu是基于事件驱动的,在基于KVM的qemu模型中,每一个VCPU对应一个qemu线程,且qemu主线程负责各种事件的监听,这里有一个小的IO监听框架,本节对此进行介绍。
1.1 涉及结构
struct GArray {
gchar *data;
guint len;
};
Data指向一个GpollFD数组,len表示数组的个数。
struct GPollFD
{
gint fd;
gushort events;
gushort revents;
};
Fd为监听的fd,event为请求监听的事件,是一组bit的组合。Revents为poll收到的事件,根据此判定当前什么事件可用。
typedef struct IOHandlerRecord {
IOCanReadHandler *fd_read_poll;
IOHandler *fd_read;
IOHandler *fd_write;
void *opaque;
QLIST_ENTRY(IOHandlerRecord) next;
int fd;
int pollfds_idx;
bool deleted;
} IOHandlerRecord;
该结构是qemu中IO框架的处理单位,fd_read和fd_write为注册的对应处理函数。Next表示该结构会连接在一个全局的链表上,fd是对应的fd,delete标志是否需要从链表中删除该结构。
1.2 代码分析
1.2.1 初始化阶段
Main-> qemu_init_main_loop
Main-> main_loop-> main_loop_wait
qemu_init_main_loop
int qemu_init_main_loop(void)
{
int ret;
GSource *src;
init_clocks();
ret = qemu_signal_init();
if (ret) {
return ret;
}
//malloc a globle fd array
gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
//create a aio context
qemu_aio_context = aio_context_new();
//get event source from aio context
src = aio_get_g_source(qemu_aio_context);
//add source to main loop
g_source_attach(src, NULL);
g_source_unref(src);
return ;
}
Qemu中的main loop主要采用 了glib中的事件循环,关于此详细内容,准备后面专门写一小节,本节主要看主体IO框架。
该函数主要就分配了一个Garray结构存储全局的GpollFD,在main_loop中的main_loop_wait阶段有两个比较重要的函数:qemu_iohandler_fill,os_host_main_loop_wait和qemu_iohandler_poll,前者把用户添加的fd信息注册到刚才分配的Garray结构中,os_host_main_loop_wait对事件进行监听,qemu_iohandler_poll对接收到的事件进行处理。
1.2.2 添加fd
用户添加fd的函数为qemu_set_fd_handler,参数中fd为本次添加的fd,后面分别是对该fd的处理函数(read or write),最后opaque为处理函数的参数。
int qemu_set_fd_handler(int fd,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
{
return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
}
可见该函数直接调用了qemu_set_fd_handler2:
int qemu_set_fd_handler2(int fd,
IOCanReadHandler *fd_read_poll,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
{
IOHandlerRecord *ioh;
assert(fd >= );
//if read and write are null,delete
if (!fd_read && !fd_write) {
QLIST_FOREACH(ioh, &io_handlers, next) {
if (ioh->fd == fd) {
ioh->deleted = ;
break;
}
}
} else {//find and goto find
QLIST_FOREACH(ioh, &io_handlers, next) {
if (ioh->fd == fd)
goto found;
}
ioh = g_malloc0(sizeof(IOHandlerRecord));
//insert ioh to io_handlers list
QLIST_INSERT_HEAD(&io_handlers, ioh, next);
found:
ioh->fd = fd;
ioh->fd_read_poll = fd_read_poll;
ioh->fd_read = fd_read;
ioh->fd_write = fd_write;
ioh->opaque = opaque;
ioh->pollfds_idx = -;
ioh->deleted = ;
qemu_notify_event();
}
return ;
}
这里判断如果read和write函数均为空的话就表示本次是要delete某个fd,就遍历所有的io_handlers,对指定的fd对应的IOHandlerRecord标志delete。
否则还有两种情况,添加或者更新。所以首先还是要从io_handlers找一下,如果找到直接更新,否则新创建一个IOHandlerRecord,然后再添加信息。具体信息内容就比较简单。
1.2.3 处理fd
在main_loop_wait函数中,通过os_host_main_loop_wait对fd进行监听,当然并不是它直接监听,而是通过glib的接口。
当os_host_main_loop_wait返回后,就表示当前有可用的事件,在main_loop_wait函数中,调用了qemu_iohandler_poll函数对fd进行处理。
void qemu_iohandler_poll(GArray *pollfds, int ret)
{
if (ret > ) {
IOHandlerRecord *pioh, *ioh;
QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
int revents = ;
if (!ioh->deleted && ioh->pollfds_idx != -) {
GPollFD *pfd = &g_array_index(pollfds, GPollFD,
ioh->pollfds_idx);
revents = pfd->revents;
}
if (!ioh->deleted && ioh->fd_read &&
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {
ioh->fd_read(ioh->opaque);
}
if (!ioh->deleted && ioh->fd_write &&
(revents & (G_IO_OUT | G_IO_ERR))) {
ioh->fd_write(ioh->opaque);
}
/* Do this last in case read/write handlers marked it for deletion */
if (ioh->deleted) {
QLIST_REMOVE(ioh, next);
g_free(ioh);
}
}
}
}
具体的处理倒也简单,逐个遍历io_handlers,对于每个GpollFD,取其revents,判断delete标志并校验状态,根据不同的状态,调用read或者write回调。最后如果是delete的GpollFD,就从链表中remove掉,释放GpollFD。
补充:针对qemu进程中线程数目的问题,从本节可以发现qemu主线程主要负责事件循环,针对每个虚拟机的VCPU,会有一个子线程为之服务,因此qemu线程数目至少要大于等于1+VCPU数目。
以马内利!
参考资料:
1、qemu 2.7源码
QEMU IO事件处理框架的更多相关文章
- ChannelPipeline----贯穿io事件处理的大动脉
ChannelPipeline贯穿io事件处理的大动脉 上一篇,我们分析了NioEventLoop及其相关类的主干逻辑代码,我们知道netty采用线程封闭的方式来避免多线程之间的资源竞争,最大限度地减 ...
- IO流框架
目录 IO流框架总结 字节流 字符流 IO流框架总结 普通IO / NIO 字节流 字节流是万能流,但是在处理字符方面有时候不太方便,一般用来处理二进制文件 字节输入流 InputStream int ...
- Java知识点总结——IO流框架
IO框架 一.流的概念 概念:内存与存储设备之间传输数据的通道. 二.流的分类 按方向分类: 输入流:将<存储设备>中的内容读入到<内存>中 输出流:将<内存>中的 ...
- 从读写角度,带你了解数仓的IO基本框架
摘要:本文从读取和写入的角度分别描述了行存和列存的IO模型,并对文件结构做了简单介绍. 本文分享自华为云社区<GaussDB(DWS)基本IO框架>,作者: Naibaoofficial. ...
- Hadoop Yarn事件处理框架源码分析
由于想在项目中使用类似yarn的事件处理机制,就看了实现.主要是由Dispatcher.java,EventHandler.java,Service.java这3个类撑起来的. 在事件处理之前,先注册 ...
- 【Spring Boot】集成Netty Socket.IO通讯框架
服务端 @Configuration public class NettySocketConfig { private static final Logger logger = LoggerFacto ...
- Spring Boot集成Reactor事件处理框架的简单示例
1. Reactor简介 Reactor 是 Spring 社区发布的基于事件驱动的异步框架,不仅解耦了程序之间的强调用关系,而且有效提升了系统的多线程并发处理能力. 2. Spring Boot集成 ...
- FD.io vpp 框架转发图
在ip4-icmp-input 与 ip4-udp-input后可以注册后续的处理函数,ip4-icmp-input根据 icmp的报文类型选择相应的处理函数,而ip4-udp-input根据端口选择 ...
- IO流框架关系总结(关系图)
字节流和字符流关系图 打印流和序列化流关系图
随机推荐
- Splash 对象方法
go() wait() jsfunc() evaljs() runjs() autoload() call_later() http_get() http_post() set_content() h ...
- Android涉及到的网址都记录在这把~~~~
http://source.android.com/source/initializing.html 开放源码 http://developer.android.com/about/versions ...
- ClamAV病毒软件的安装和使用
ClamAV 杀毒是Linux平台最受欢迎的杀毒软件,ClamAV属于免费开源产品,支持多种平台,如:Linux/Unix.MAC OS X.Windows.OpenVMS.ClamAV是基于病毒扫描 ...
- U3D教程宝典之两步实现超实用的XML存档
两步实现超实用的XML存档 本套存档的优点:易使用,跨平台,防作弊(内容加密 + 防拷贝) 脚本下载地址 使用方法非常简单:把GameDataManager和XmlSaver两个脚本添加至工程后(1) ...
- 音频——H5 audio
分享站又有新功能了:将文件站上的语音文件正确播放出来.效果图: 暂停: 播放: 实现的效果:类似于音乐播放器一般,但是较之更简单一些,可以正常播放语音,有拖动.快进后退效果便可. 思路: 首先想到的便 ...
- 在linux下如何判断是否已经安装某个软件?
如果你使用rpm -ivh matlab装的,用rpm -qa | grep matlab肯定是能够找到的. 如果你是用make && make install装的.那么最好直接去找执 ...
- Swift - 可选类型详解
可选类型详解 直接上代码解释 // 类中所有的属性在对象初始化时,必须有初始化值 class Person : NSObject { var name : String? var view : UIV ...
- 【开源整理】.Net开源项目资源大全
汇总了.NET平台开源的工具类库,新的内容在不断更新中.内容借鉴了博客园.伯乐在线.GitHub等平台. (注:下面用 [$] 标注的表示收费工具,但部分收费工具针对开源软件的开发/部署/托管是免费的 ...
- Makefile 中all 和.PHONY的作用
请编写一个makefile同时编译.链接下面两个程序: main1.c: #include<stdio.h> int main(void) { printf("main1\n&q ...
- 使用 github Pages 服务建立个人独立博客全过程
你是否有这样子的需求,只是想简单的写写文章,记录下自己的学习心得.成长经历等,都是些文字内容,不需要配置使用数据库.不想购买服务器自己搭建站点,只是想安安静静的用比较舒服的方式来写篇文章. 静态博客就 ...