KVM源代码分析4:内存虚拟化

2023-11-02

终于把KVM源代码分析3:CPU虚拟化写完了,虽然还有run的部分另外在写,还是先看一下内存虚拟化部分。

代码版本:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git v4.5

在虚拟机的创建与运行中pc_init_pci负责在qemu中初始化虚拟机,内存初始化也是在这里完成的,还是一步步从qemu说起,在vl.c的main函数中有ram_size参数,由qemu入参标识QEMU_OPTION_m设定,顾名思义就是虚拟机内存的大小,通过machine->init一步步传递给pc_init1函数。在这里分出了above_4g_mem_size和below_4g_mem_size,即高低端内存(也不一定是32bit机器..),然后开始初始化内存,即pc_memory_init,内存通过memory_region_init_ram下面的qemu_ram_alloc分配,使用qemu_ram_alloc_from_ptr。


插播qemu对内存条的模拟管理,是通过RAMBlock和ram_list管理的,RAMBlock就是每次申请的内存池,ram_list则是RAMBlock的链表,他们结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct RAMBlock {
//对应宿主的内存地址
     uint8_t * host ;
//block在ramlist中的偏移
     ram_addr_t offset ;
//block长度
     ram_addr_t length ;
     uint32_t flags ;
//block名字
     char idstr [ 256 ] ;
     QLIST_ENTRY ( RAMBlock ) next ;
#if defined(__linux__) && !defined(TARGET_S390X)
     int fd ;
#endif
} RAMBlock ;
 
typedef struct RAMList {
//看代码理解就是list的head,但是不知道为啥叫dirty...
     uint8_t * phys_dirty ;
     QLIST_HEAD ( ram , RAMBlock ) blocks ;
} RAMList ;

下面再回到qemu_ram_alloc_from_ptr函数,使用find_ram_offset赋值给new block的offset,find_ram_offset具体工作模型已经在KVM源代码分析2:虚拟机的创建与运行中提到了,不赘述。然后是一串判断,在kvm_enabled的情况下使用new_block->host = kvm_vmalloc(size),最终内存是qemu_vmalloc分配的,使用qemu_memalign干活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void * qemu_memalign ( size_t alignment , size_t size )
{
     void * ptr ;
//使用posix进行内存针对页大小对齐
#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
     int ret ;
     ret = posix_memalign ( & ptr , alignment , size ) ;
     if ( ret != 0 ) {
         fprintf ( stderr , "Failed to allocate %zu B: %sn" ,
                 size , strerror ( ret ) ) ;
         abort ( ) ;
     }
#elif defined(CONFIG_BSD)
     ptr = qemu_oom_check ( valloc ( size ) ) ;
#else
//所谓检查oom就是看memalign对应malloc申请内存是否成功
     ptr = qemu_oom_check ( memalign ( alignment , size ) ) ;
#endif
     trace_qemu_memalign ( alignment , size , ptr ) ;
     return ptr ;
}

以上qemu_vmalloc进行内存申请就结束了。在qemu_ram_alloc_from_ptr函数末尾则是将block添加到链表,realloc整个ramlist,用memset初始化整个ramblock,madvise对内存使用限定。
然后一层层的退回到pc_memory_init函数。

此时pc.ram已经分配完成,ram_addr已经拿到了分配的内存地址,MemoryRegion ram初始化完成。下面则是对已有的ram进行分段,即ram-below-4g和ram-above-4g,也就是高端内存和低端内存。用memory_region_init_alias初始化子MemoryRegion,然后将memory_region_add_subregion添加关联起来,memory_region_add_subregion具体细节“KVM源码分析2”中已经说了,参考对照着看吧,中间很多映射代码过程也只是qemu遗留的软件实现,没看到具体存在的意义,直接看到kvm_set_user_memory_region函数,内核真正需要kvm_vm_ioctl传递过去的参数是什么, struct kvm_userspace_memory_region mem而已,也就是

1
2
3
4
5
6
7
struct kvm_userspace_memory_region {
__u32 slot ;
__u32 flags ;
__u64 guest_phys_addr ;
__u64 memory_size ; /* bytes */
__u64 userspace_addr ; /* start of the userspace allocated memory */
} ;

kvm_vm_ioctl进入到内核是在KVM_SET_USER_MEMORY_REGION参数中,即执行kvm_vm_ioctl_set_memory_region,然后一直向下,到__kvm_set_memory_region函数,check_memory_region_flags检查mem->flags是否合法,而当前flag也就使用了两位,KVM_MEM_LOG_DIRTY_PAGES和KVM_MEM_READONLY,从qemu传递过来只能是KVM_MEM_LOG_DIRTY_PAGES,下面是对mem中各参数的合规检查,(mem->memory_size & (PAGE_SIZE – 1))要求以页为单位,(mem->guest_phys_addr & (PAGE_SIZE – 1))要求guest_phys_addr页对齐,而((mem->userspace_addr & (PAGE_SIZE – 1)) || !access_ok(VERIFY_WRITE,(void __user *)(unsigned long)mem->userspace_addr,mem->memory_size))则保证host的线性地址页对齐而且该地址域有写权限。
id_to_memslot则是根据qemu的内存槽号得到kvm结构下的内存槽号,转换关系来自id_to_index数组,那映射关系怎么来的,映射关系是一一对应的,在kvm_create_vm虚拟机创建过程中,kvm_init_memslots_id初始化对应关系,即slots->id_to_index[i] = slots->memslots[i].id = i,当前映射是没有意义的,估计是为了后续扩展而存在的。
扩充了new的kvm_memory_slot,下面直接在代码中注释更方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//映射内存有大小,不是删除内存条
if ( npages ) {
//内存槽号没有虚拟内存条,意味内存新创建
     if ( ! old . npages )
         change = KVM_MR_CREATE ;
     else { /* Modify an existing slot. */
//修改已存在的内存修改标志或者平移映射地址
//下面是不能处理的状态(内存条大小不能变,物理地址不能变,不能修改只读)
         if ( ( mem -> userspace_addr != old . userspace_addr ) ||
             ( npages != old . npages ) ||
             ( ( new . flags ^ old . flags ) & KVM_MEM_READONLY ) )
             goto out ;
//guest地址不同,内存条平移
         if ( base_gfn != old . base_gfn )
             change = KVM_MR_MOVE ;
         else if ( new . flags != old . flags )
//修改属性
             change = KVM_MR_FLAGS_ONLY ;
         else { /* Nothing to change. */
             r = 0 ;
             goto out ;
         }
     }
} else if ( old . npages ) {
//申请插入的内存为0,而内存槽上有内存,意味删除
     change = KVM_MR_DELETE ;
} else /* Modify a non-existent slot: disallowed. */
     goto out ;

另外看kvm_mr_change就知道memslot的变动值了:

1
2
3
4
5
6
enum kvm_mr_change {
     KVM_MR_CREATE ,
     KVM_MR_DELETE ,
     KVM_MR_MOVE ,
     KVM_MR_FLAGS_ONLY ,
} ;

在往下是一段检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ( ( change == KVM_MR_CREATE ) || ( change == KVM_MR_MOVE ) ) {
     /* Check for overlaps */
     r = - EEXIST ;
     kvm_for_each_memslot ( slot , kvm -> memslots ) {
         if ( ( slot -> id >= KVM_USER_MEM_SLOTS ) ||
//下面排除掉准备操作的内存条,在KVM_MR_MOVE中是有交集的
             ( slot -> id == mem -> slot ) )
             continue ;
//下面就是当前已有的slot与new在guest线性区间上有交集
         if ( ! ( ( base_gfn + npages <= slot -> base_gfn ) ||
               ( base_gfn >= slot -> base_gfn + slot -> npages ) ) )
             goto out ;
//out错误码就是EEXIST
     }
}

如果是新插入内存条,代码则走入kvm_arch_create_memslot函数,里面主要是一个循环,KVM_NR_PAGE_SIZES是分页的级数,此处是3,第一次循环,lpages = gfn_to_index(slot->base_gfn + npages – 1,slot->base_gfn, level) + 1,lpages就是一级页表所需要的page数,大致是npages>>0*9,然后为slot->arch.rmap[i]申请了内存空间,此处可以猜想,rmap就是一级页表了,继续看,lpages约为npages>>1*9,此处又多为lpage_info申请了同等空间,然后对lpage_info初始化赋值,现在看不到lpage_info的具体作用,看到后再补上。整体上看kvm_arch_create_memslot做了一个3级的软件页表。
如果有脏页,并且脏页位图为空,则分配脏页位图, kvm_create_dirty_bitmap实际就是”页数/8″.

1
2
3
4
if ( ( new . flags & KVM_MEM_LOG_DIRTY_PAGES ) && ! new . dirty_bitmap ) {
         if ( kvm_create_dirty_bitmap ( & new ) < 0 )
             goto out_free ;
     }

当内存条的改变是KVM_MR_DELETE或者KVM_MR_MOVE,先申请一个slots,把kvm->memslots暂存到这里,首先通过id_to_memslot获取准备插入的内存条对应到kvm的插槽是slot,无论删除还是移动,将其先标记为KVM_MEMSLOT_INVALID,然后是install_new_memslots,其实就是更新了一下slots->generation的值,
——–待编辑———–

—结束—

 

KVM源代码分析4:内存虚拟化OenHan

http://www.oenhan.com/kvm-src-4-mem

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

KVM源代码分析4:内存虚拟化 的相关文章

  • linux-删除KVM虚拟机

    1 查看主机 virsh list 2 关闭主机 virsh shutdown 虚拟机名称 3 删除主机定义 virsh undefine 虚拟机名称 4 删除KVM虚拟机文件 find name 虚拟机名称 rm rf 虚拟机文件
  • 虚拟机隔离和容器隔离机制及区别

    1 虚拟机隔离机制 通过虚拟化技术虚拟出资源完全独立的的主机 支持虚拟化的层是hypervisor hypervisor是一种虚拟化服务器的软件 2 Docker容器的隔离机制 Docker利用Namespace实现系统环境隔离 采用Cgr
  • KVM的HVM虚拟机使用非串口方式建立virsh console 连接

    在去年写的文章中 http blog csdn net dobell article details 14442457 写到了怎么利用serial 设备进行console连接 不过比较麻烦 因为1 需要修改虚拟机内部的grub启动选项 2
  • qemu-system-x86_64方式创建KVM虚拟机

    一 QEMU介绍 QEMU是一款高效而实用的模拟器及虚拟机监管器 Virtual Machine Monitor VMM 主要提供两种功能给用户使用 一是作为用户态模拟器 利用动态代码翻译机制来执行不同于主机架构的代码 二是作为虚拟机监管器
  • CentOS 7 virt-install 命令行方式(非图形界面)安装KVM虚拟机

    环境及网卡配置请参考 https blog csdn net mshxuyi article details 98305715 创建镜像目录 mkdir p home vms virt install 配置文件 virt install n
  • kvm qemu内幕介绍

    kvm qemu内幕介绍 标签 虚拟化io数据结构linux内核优化磁盘 2012 09 14 11 10 6923人阅读 评论 4 收藏 举报 分类 linux 56 版权声明 本文为博主原创文章 未经博主允许不得转载 目录 1 硬件虚拟
  • Linux上启用kvm嵌套虚拟化功能

    kvm支持嵌套虚拟化 即可以在虚拟机中创建虚拟机 本文主要介绍如何在使用Intel处理器的CentOS7中开启KVM的嵌套虚拟化功能 kvm主要是通过内核模块来实现的 因此我们查看系统是否开启了kvm嵌套虚拟化 只需要 cat sys mo
  • 10 KVM虚拟机配置-虚拟CPU和虚拟内存

    文章目录 10 KVM虚拟机配置 虚拟CPU和虚拟内存 10 1 概述 10 2 元素介绍 10 3 配置示例 10 KVM虚拟机配置 虚拟CPU和虚拟内存 10 1 概述 本节介绍虚拟CPU和虚拟内存的常用配置 10 2 元素介绍 vcp
  • Proxmox VE(PVE) 进行网卡直通

    文章目录 我的设备 介绍 添加CPU支持 开启iommu 查询网卡信息 Intel CPU AMD CPU 新增所需模块 添加PCI设备 命令模式添加 web页面模式添加 验证IOMMU有效 IOMMU中断重映射 查看中断重映射 启用中断重
  • KVM-6、virsh 命令及功能详解

    1 虚拟机管理操作 attach device 从XML文件附加设备 attach disk 附加磁盘设备 attach interface 连接网络接口 autostart 自动启动一个域 blkdeviotune 设置或查询块设备I O
  • 使用VMware完成KVM虚拟化实验并运行Centos

    本次实验在VMware中的Ubuntu18内安装KVM并运行centos 首先 在VMware下开启虚拟化 更新软件索引 apt get update 安装依赖 apt get install qemu kvm qemu virt mana
  • KVM和QEMU

    原文地址 KVM和QEMU 作者 embeddedlwp 目录 1 硬件虚拟化技术背景 2 KVM的内部实现概述 2 1 KVM的抽象对象 2 2 KVM的vcpu 2 3 KVM的IO虚拟化 2 3 1 IO的虚拟化 2 3 2 Virt
  • 详解KVM虚拟化原理

    详解KVM虚拟化原理 KVM架构 KVM Kernel based Virtual Machine 包含一个为处理器提供底层虚拟化 可加载的核心 模块kvm ko kvm intel ko或kvm amd ko 使用QEMU QEMU KV
  • KVM无法进入virt-manager,提示Unable to init server: Could not connect: Connection refused

    1 KVM virt manager不能以root用户进入 需切换成普通用户或者sudo用户 2 需要配置ssh 密钥 3 需要安装Xming或者Xmanager等KVM可用等图形界面软件 无法连接kvm 设置用户到组 一定要当前用户不要r
  • kvm-ovirt-vdsm安装记录

    小技巧 1 fedaro 19自动查找最快yum源 yum install yum fastestmirror 2 libvirt启动sasl添加用户 saslpasswd2 c a libvirt admin 3 查看kvm模块 lsmo
  • KVM MMU EPT内存管理

    转载请注明 转载自博客xelatex KVM 并附本文链接 谢谢 注 文章中采用的版本 Linux 3 11 https www kernel org pub linux kernel v3 x linux 3 11 tar gz qemu
  • kvm磁盘管理

    kvm磁盘管理 KVM虚拟磁盘类型 raw qcow2 qemu img常用参数 主要参数 安装qemu img 查看磁盘信息 info 创建磁盘文件 create 磁盘容量调整 resize 热扩容 磁盘增加容量 磁盘缩小容量 生产环境下
  • 2020最新版KVM虚拟机安装详解

    VMware Workstation Pro15 5下 1 操作环境 CentOS Linux release 7 7 1908 Core 2 需要用到的工具 XSHELL Centos任意版本镜像 3 必须安装的软件 Xmanager p
  • Android 模拟器在 Ubuntu 18.04 上因 SIGSEGV 崩溃

    Android Studio版本 Android Studio 3 1 2 内部版本 AI 173 4720617 建于 2018 年 4 月 14 日 它是从 Ubuntu 的 snap 商店安装的 我使用安装了kvm1 在 Ubuntu
  • Android模拟器和virtualbox不能同时运行

    每当我运行 Virtualbox 时 我都无法启动 Android 模拟器映像 反之亦然 AVD管理器中的错误消息是 ioctl KVM CREATE VM failed Device or resource busy ko failed

随机推荐

  • Redis+Mysql模式和内存+硬盘模式的异同

    学习任何新知识 都是一个循序渐进的过程 从刚开始的懵懂无知 到简单熟悉 然后突然的彻悟 成果让人欣喜若狂 心情也会快乐很久 redis mysql和内存 硬盘类似的地方 首先看图 首先 我们知道 mysql是持久化存储 存放在磁盘里面 检索
  • Latex:图片、表格占据双栏排版的两栏时 的位置控制

    目录 1 问题 怎么在双栏排版中 让占据两栏的表格出现在页面顶端 2 解决 在latex中加入 usepackage stfloats 即可 1 图片 占据两栏显示在页面顶端 2 表格 占据两栏显示在页面顶端 1 问题 怎么在双栏排版中 让
  • CMake buildsystem

    官方文档 https cmake org cmake help latest manual cmake buildsystem 7 html 介绍 基于CMake的构建系统 buildsystem 其组织形式是一组高级逻辑目标 high l
  • LR1语法分析C++实现:一、项目集簇的生成

    转载请注明出处 https blog csdn net hhhhhhhhhhkkkkkkkkkk 嗯 先上代码 后面慢慢写注释 我好像太鸡智 贼 了 哈哈 生成项目集簇 基本符号的定义与相关操作 using t sym int 符号 usi
  • 第十一届蓝桥杯(国赛)——质数行者

    问题描述 小蓝在玩一个叫质数行者的游戏 游戏在一个 n m w n m w n m w 的立体方格图上进行 从北到南依次标号为第 1
  • C++编程用梯形法求积分

    这是我们学校oj的作业可以看看 include
  • 一图抵千言:带你了解最直观的神经网络架构可视化

    一张好的图抵得上一千个等式 神经网络是复杂 多维 非线性的数组运算 如何在避免过于复杂或重复的情况下呈现深度学习模型架构的重要特征呢 又该以何种方式清晰直观 启发性地呈现它们呢 好看也是加分项 无论研究还是教学项目对此都没有固定标准 本文我
  • Redis3.0集群完全版(数据迁移问题)

    Redis3 0集群安装手册 一 概述 要让集群正常工作至少需要3个主节点 在这里我们要创建6个Redis节点 其中三个为主节点 三个为从节点 对应的redis节点的ip和端口对应关系如下 127 0 0 1 7000127 0 0 1 7
  • 图说三极管,太容易懂了!(史上最详细版本)

    晶体三极管 是半导体基本元器件之一 具有电流放大作用 是电子电路的核心元件 在电子元件家族中 三极管属于半导体主动元件中的分立元件 广义上 三极管有多种 常见如下图所示 狭义上 三极管指双极型三极管 是最基础最通用的三极管 本文所述的是狭义
  • 阻止Mac版 Adobe Acrobat Pro DC的顽固的自动更新

    阻止Mac版 Adobe Acrobat Pro DC的顽固的自动更新 方案一 无效 方案二 无效 方案三 无效 方案四 验证了一年 应该有效 在mac上安装强大的Adobe Acrobat Pro DC之后 你会发现使用AZii破解之后过
  • c++命令行解析

    发现一个项目中可以用的c 的命令行解析器封装 cmdline 下载地址 GitHub tanakh cmdline A Command Line Parser c 的命令行解析 只有一个 h文件 可直接加入项目 当然Qt有自己的命令行解析类
  • Java FTP按关键字批量下载文件

    一 所需jar
  • 离职方知人心凉!

    2018年初夏 还是大三的我 在班级群里看到老师发的一条招聘信息后 写了我人生中第一封简历 殊不知我鼓了多大勇气才点击 发送邮件 或许是缘分又或许是缺人 就这样我通过了一面二面 正式入职 由此开启了我的职业生涯 实习生的工资很低 但工作强度
  • 软件测试网上订餐系统,星月外卖网上订餐系统软件测试报告(正式).doc

    文档介绍 计算机科学与技术 1 班 网上订餐系统软件测试报告 小组名称 第五组 小组成员 魏川浩 黄星月 瞿坤杨 李多福 王伟 项目组成员 组长 魏川浩 班级学号 20140181 姓名 魏川浩 负责工作 手工输入测试用例并记录测试结果 评
  • 哈希表 java

    给定一个字符串 找到它的第一个不重复的字符 并返回它的索引 如果不存在 则返回 1 案例 s leetcode 返回 0 s loveleetcode 返回 2 public class solu public int ff String
  • TensorFlow 高性能数据输入管道设计指南

    TensorFlow版本 1 12 0 本篇主要介绍怎么使用 tf data API 来构建高性能的输入 pipeline tf data官方教程详见前面的博客 lt lt lt lt lt lt lt lt lt
  • Spring Boot集成MyBatis Plus中的QueryWrapper的eq方法详解及示例代码

    1 简介 MyBatis Plus是一个强大的MyBatis增强工具包 它为我们在进行数据库操作时提供了很多便利的方法 其中 QueryWrapper是MyBatis Plus中的一个重要类 它可以用于构建复杂的查询条件 在QueryWra
  • while语句

    while循环语句 while语句 格式 初始化语句 while 条 件判断语句 循环体语句 条件控制语句 执行流程 1 执行初始化语句 2 执行条件判断语句 看其结果是true还是false 如果是false 循环结束 如果是true 继
  • 宝塔部署sll证书报错CertificateFile: file ‘/etc/letsencrypt/live/域名/fullchain.pem‘ does not exist or is empty

    报错 CertificateFile file etc letsencrypt live 域名 fullchain pem does not exist or is empty 根据上图提示报错找到对应的站点并关闭ssl证书 关闭后重新提交
  • KVM源代码分析4:内存虚拟化

    终于把KVM源代码分析3 CPU虚拟化写完了 虽然还有run的部分另外在写 还是先看一下内存虚拟化部分 代码版本 https git kernel org pub scm linux kernel git stable linux stab