KVM虚拟机IO处理过程(一) ----Guest VM I/O 处理过程
虚拟化技术主要包含三部分内容:CPU虚拟化,内存虚拟化,设备虚拟化.本系列文章主要描述磁盘设备的虚拟化过程,包含了一个读操作的I/O请求如何从Guest Vm到其最终被处理的整个过程.本系列文章中引用到的linux内核代码版本为3.7.10,使用的虚拟化平台是KVM,qemu的版本是1.6.1.
用户程序想要访问IO设备需要调用操作系统提供的接口,即系统调用.当在用户程序中调用一个read操作时,系统先保存好read操作的参数,然后调用int 80命令(也可能是sysenter)进入内核空间,在内核空间中,读操作的逻辑由sys_read函数实现.
在讲sys_read的实现过程之前,我们先来看看read操作在内核空间需要经历的层次结构.从图中可以看出,read操作首先经过虚拟文件系统曾(vfs), 接下来是具体的文件系统层,Page cache层,通用块层(generic block layer),I/O调度层(I/O scheduler layer),块设备驱动层(block device driver layer),最后是块物理设备层(block device layer).
- 虚拟文件系统层:该层屏蔽了下层的具体操作,为上层提供统一的接口,如vfs_read,vfs_write等.vfs_read,vfs_write通过调用下层具体文件系统的接口来实现相应的功能.
- 具体文件系统层:该层针对每一类文件系统都有相应的操作和实现了,包含了具体文件系统的处理逻辑.
- page cache层:该层缓存了从块设备中获取的数据.引入该层的目的是避免频繁的块设备访问,如果在page cache中已经缓存了I/O请求的数据,则可以将数据直接返回,无需访问块设备.
- 通过块层:接收上层的I/O请求,并最终发出I/O请求.该层向上层屏蔽了下层设备的特性.
- I/O调度层: 接收通用块层发出的 IO 请求,缓存请求并试图合并相邻的请求(如果这两个请求的数据在磁盘上是相邻的)。并根据设置好的调度算法,回调驱动层提供的请求处理函数,以处理具体的 IO 请求
- 块设备驱动层:从上层取出请求,并根据参数,操作具体的设备.
- 块设备层:真正的物理设备.
- asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count);
其函数实现在fs/read_write.c文件中:
- SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
- {
- struct fd f = fdget(fd);
- ssize_t ret = -EBADF;
- if (f.file) {
- loff_t pos = file_pos_read(f.file);
- ret = vfs_read(f.file, buf, count, &pos); //调用vfs layer中的read操作
- file_pos_write(f.file, pos);//设置当前文件的位置
- fdput(f);
- }
- return ret;
- }
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- ssize_t ret;
- if (!(file->f_mode & FMODE_READ))
- return -EBADF;
- if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
- return -EINVAL;
- if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
- return -EFAULT;
- ret = rw_verify_area(READ, file, pos, count);
- if (ret >= 0) {
- count = ret;
- if (file->f_op->read) {
- ret = file->f_op->read(file, buf, count, pos); //该函数由具体的文件系统指定
- } else
- ret = do_sync_read(file, buf, count, pos); //内核默认的读文件操作
- if (ret > 0) {
- fsnotify_access(file);
- add_rchar(current, ret);
- }
- inc_syscr(current);
- }
- return ret;
- }
- const struct file_operations ext4_file_operations = {
- .llseek = ext4_llseek,
- .read = do_sync_read,
- .write = do_sync_write,
- .aio_read = generic_file_aio_read,
- .aio_write = ext4_file_write,
- .unlocked_ioctl = ext4_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = ext4_compat_ioctl,
- #endif
- .mmap = ext4_file_mmap,
- .open = ext4_file_open,
- .release = ext4_release_file,
- .fsync = ext4_sync_file,
- .splice_read = generic_file_splice_read,
- .splice_write = generic_file_splice_write,
- .fallocate = ext4_fallocate,
- };
- ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
- {
- struct iovec iov = { .iov_base = buf, .iov_len = len };
- struct kiocb kiocb;
- ssize_t ret;
- init_sync_kiocb(&kiocb, filp);//初始化kiocp,描述符kiocb是用来记录I/O操作的完成状态
- kiocb.ki_pos = *ppos;
- kiocb.ki_left = len;
- kiocb.ki_nbytes = len;
- for (;;) {
- ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);//调用真正做读操作的函数,ext4文件系统在fs/ext4/file.c中配置
- if (ret != -EIOCBRETRY)
- break;
- wait_on_retry_sync_kiocb(&kiocb);
- }
- if (-EIOCBQUEUED == ret)
- ret = wait_on_sync_kiocb(&kiocb);
- *ppos = kiocb.ki_pos;
- return ret;
- }
- ssize_t
- generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
- {
- struct file *filp = iocb->ki_filp;
- ssize_t retval;
- unsigned long seg = 0;
- size_t count;
- loff_t *ppos = &iocb->ki_pos;
- count = 0;
- retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
- if (retval)
- return retval;
- /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
- if (filp->f_flags & O_DIRECT) {
- loff_t size;
- struct address_space *mapping;
- struct inode *inode;
- struct timex txc;
- do_gettimeofday(&(txc.time));
- mapping = filp->f_mapping;
- inode = mapping->host;
- if (!count)
- goto out; /* skip atime */
- size = i_size_read(inode);
- if (pos < size) {
- retval = filemap_write_and_wait_range(mapping, pos,
- pos + iov_length(iov, nr_segs) - 1);
- if (!retval) {
- retval = mapping->a_ops->direct_IO(READ, iocb,
- iov, pos, nr_segs);
- }
- if (retval > 0) {
- *ppos = pos + retval;
- count -= retval;
- }
- /*
- * Btrfs can have a short DIO read if we encounter
- * compressed extents, so if there was an error, or if
- * we've already read everything we wanted to, or if
- * there was a short read because we hit EOF, go ahead
- * and return. Otherwise fallthrough to buffered io for
- * the rest of the read.
- */
- if (retval < 0 || !count || *ppos >= size) {
- file_accessed(filp);
- goto out;
- }
- }
- }
- count = retval;
- for (seg = 0; seg < nr_segs; seg++) {
- read_descriptor_t desc;
- loff_t offset = 0;
- /*
- * If we did a short DIO read we need to skip the section of the
- * iov that we've already read data into.
- */
- if (count) {
- if (count > iov[seg].iov_len) {
- count -= iov[seg].iov_len;
- continue;
- }
- offset = count;
- count = 0;
- }
- desc.written = 0;
- desc.arg.buf = iov[seg].iov_base + offset;
- desc.count = iov[seg].iov_len - offset;
- if (desc.count == 0)
- continue;
- desc.error = 0;
- do_generic_file_read(filp, ppos, &desc, file_read_actor);
- retval += desc.written;
- if (desc.error) {
- retval = retval ?: desc.error;
- break;
- }
- if (desc.count > 0)
- break;
- }
- out:
- return retval;
- }
- static const struct address_space_operations ext4_ordered_aops = {
- .readpage = ext4_readpage,
- .readpages = ext4_readpages,
- .writepage = ext4_writepage,
- .write_begin = ext4_write_begin,
- .write_end = ext4_ordered_write_end,
- .bmap = ext4_bmap,
- .invalidatepage = ext4_invalidatepage,
- .releasepage = ext4_releasepage,
- .direct_IO = ext4_direct_IO,
- .migratepage = buffer_migrate_page,
- .is_partially_uptodate = block_is_partially_uptodate,
- .error_remove_page = generic_error_remove_page,
- };
- int mpage_readpage(struct page *page, get_block_t get_block)
- {
- struct bio *bio = NULL;
- sector_t last_block_in_bio = 0;
- struct buffer_head map_bh;
- unsigned long first_logical_block = 0;
- map_bh.b_state = 0;
- map_bh.b_size = 0;
- bio = do_mpage_readpage(bio, page, 1, &last_block_in_bio,
- &map_bh, &first_logical_block, get_block);
- if (bio)
- mpage_bio_submit(READ, bio);
- return 0;
- }
KVM虚拟机IO处理过程(一) ----Guest VM I/O 处理过程的更多相关文章
- KVM虚拟机IO处理过程(二) ----QEMU/KVM I/O 处理过程
接着KVM虚拟机IO处理过程中Guest Vm IO处理过程(http://blog.csdn.net/dashulu/article/details/16820281),本篇文章主要描述IO从gue ...
- ubuntu18.04上搭建KVM虚拟机环境超完整过程
看标题这是篇纯运维的文章.在中小型企业中,一般很少配置专业的运维人员,都是由开发人员兼着.同时,对有志于技术管理的开发人员来说,多了解一些运维及整个软件生命周期的知识,是很有帮助的,因为带团队不仅仅是 ...
- 一次 KVM 虚拟机磁盘占满的排查过程
一次 KVM 虚拟机磁盘占满的排查过程 KVM 虚拟机系统为 CentOS,文件系统为 XFS. 现象如下: 使用 df -h 命令发现磁盘剩余空间为30k(总大小为30G),使用 df -i 发现 ...
- 6、安装kvm虚拟机
6.1.虚拟机开启虚拟化: 6.2.检查linux虚拟机cpu是否开启了虚拟化: egrep -o 'vmx|svm' /proc/cpuinfo vmx 6.3.安装kvm管理和安装kvm虚拟机的软 ...
- [原] KVM虚拟机网络闪断分析
背景 公司云平台的机器时常会发生网络闪断,通常在10s-100s之间. 异常情况 VM出现问题时,表现出来的情况是外部监控系统无法访问,猜测可能是由于系统假死,OVS链路问题等等.但是在出现网络问题的 ...
- 关于Linux虚拟化技术KVM的科普 科普二(KVM虚拟机代码揭秘)
代码分析文章<KVM虚拟机代码揭秘--QEMU代码结构分析>.<KVM虚拟机代码揭秘--中断虚拟化>.<KVM虚拟机代码揭秘--设备IO虚拟化>.<KVM虚拟 ...
- KVM虚拟机网络闪断分析
https://www.cnblogs.com/Bozh/p/5484838.html 背景 公司云平台的机器时常会发生网络闪断,通常在10s-100s之间. 异常情况 VM出现问题时,表现出来的情况 ...
- Linux 中使用 virsh 管理 KVM 虚拟机 (转)
术语 虚拟化指的是:在相同的物理(硬件)系统上,同时运行多个操作系统,且这几个系统相互隔离的可能性,而那个硬件在虚拟化架构中被称作宿主机(host).虚拟机监视器(也被称为虚拟机管理程序(hyperv ...
- (转)CentOS7安装KVM虚拟机详解
原文:https://github.com/jaywcjlove/handbook/blob/master/CentOS/CentOS7%E5%AE%89%E8%A3%85KVM%E8%99%9A%E ...
随机推荐
- 慕课网 深入浅出javascript 笔记
javascript 数据类型 5种简单数据类型:Number.String.Boolean.Undefined.Null 1种复杂数据类型:Object = 表示赋值: == 表示比较,但是 ...
- leaflet 整合 esri
此 demo 通过 proj4js 将 leaflet 与 esri 整合起来,同时添加了 ClusteredFeatureLayer 的支持. 下载 <html> <head> ...
- 一步一步 Pwn RouterOS之调试环境搭建&&漏洞分析&&poc
前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 本文分析 Vault 7 中泄露的 RouterOs 漏洞.漏洞影 ...
- 润乾V4报表放入WEBINF保护目录下如何实现
润乾报表放入WEBINF保护目录下如何实现 WEB-INF下面的文件都是受保护的,客户为了保护项目的文件不受到非法的访问,jsp页面都放在WEB-INF下,那润乾报表放入WEB-INF保护目录下 ...
- 0java之泛型解说
1.集合中只能装入引用数据类型,不能装入基本数据类型.如,装入int类型的数值123会自动装箱. 2.开发人员装入集合的数据类型不确定,所以它被设计成可以装入所有的Object. 3.新的问题产生,装 ...
- SQL Server的优点与缺点
一般来说索引会加快查询速度,但会影响插入,修改,删除的数据,且占用物理空间;所以我们应该合理的创建索引,而且应该先创建聚合索引,再创建非聚合索引.要在经常进行查询的列上创建索引,而且如果表列较少的话要 ...
- windows 命令行报错:file(s) not in client view
今天在执行p4 sync命令时报错:File(s) not in client view,查找后发现其实是未连接上p4服务器.需要重新设置P4PORT=服务器地址 即可解决(参考链接:https:/ ...
- webpack HMR是如何工作的?
https://github.com/webpack/docs/wiki/hot-module-replacement-with-webpack https://www.jianshu.com/p/9 ...
- JAVA两种代理模式
简单设计动态代理,基本模拟spring的动态代理方式. before afterReturning around afterException after这些通知方法都可以这块模拟出来 spring的 ...
- Django学习---抽屉热搜榜分析【all】
Python实例---抽屉热搜榜前端代码分析 Python实例---抽屉后台框架分析 Python学习---抽屉框架分析[点赞功能分析] Python学习---抽屉框架分析[数据库设计分析]18031 ...