使用Rust构建一个kvm用户空间实例

最近在学习虚拟化相关的内容,想着使用Rust构建一个最小的kvm用户空间实例。也就是直接调用kvm的api,然后创建虚拟机。网络上关于kvm的内容大部分是使用libvirt的,然后kvm用户空间实例也是使用C编写的。因此想着使用Rust写一个简单的。

思路

话不多说,直接讲思路:

  • 创建kvm实例
  • 初始化内存
  • 初始化virtual cpu
  • 加载镜像文件到客户机内存
  • 运行vcpu

查了一下crates.io,发现有2个库,分别是

  • kvm_bindings
  • kvm_ioctls

利用kvm_bindings和kvm_ioctls这两个库对kvm api的封装,能够简化我们的代码编写。

代码讲解

代码讲解将分为2个部分,分别是用户空间实例以及客户操作系统的代码。主要是讲解kvm用户空间实例。

完整的代码在这里:https://github.com/fslongjin/kvm_userspace

用户空间实例

在这里,将结合main.rs的代码,对创建并运行虚拟机的全过程进行讲解。

main.rs的代码放在这里:https://github.com/fslongjin/kvm_userspace/blob/main/kvm_userspace/src/main.rs

这个代码是一个使用Rust编写的kvm用户空间实例,用于创建一个虚拟机并运行一个内核。

下面是创建虚拟机的过程:

1.创建Kvm实例

let kvm = Kvm::new().unwrap();

这个语句创建了一个Kvm实例。Kvm是一个结构体,代表了/dev/kvm。

2.创建VmFd实例

let vm = kvm.create_vm().unwrap();

这个语句创建了一个VmFd实例。VmFd是一个结构体,代表了一个虚拟机实例。

3.设置虚拟机内存

fn setup_memory(&mut self, ram_size: usize) {
    // ...
    let ptr = unsafe {
        mmap(
            0 as *mut c_void,
            ram_size,
            PROT_READ | PROT_WRITE,
            MAP_SHARED | MAP_ANONYMOUS,
            -1,
            0,
        )
    };
    // ...
}

这个函数使用mmap分配一块内存用于虚拟机,并设置虚拟机的内存区域。首先,把内存大小按照4096对齐,然后使用mmap函数分配一块内存。mmap函数的参数依次是:

  • 0 as *mut c_void:分配的内存地址,这里使用0表示由系统自动分配。
  • ram_size:分配的内存大小,按照4096对齐。
  • PROT_READ | PROT_WRITE:内存的读写权限。
  • MAP_SHARED | MAP_ANONYMOUS:分配匿名内存,多个进程可以共享这块内存。
  • -1:文件描述符,这里使用-1表示不使用文件。
  • 0:文件偏移量,这里使用0表示从文件开头开始分配内存。

然后,将分配的内存地址存储在Vm结构体的hva_ram_start字段中。接着,创建一个kvm_userspace_memory_region结构体,设置虚拟机的内存区域,然后使用VmFd的set_user_memory_region函数设置虚拟机的内存区域。

4.创建虚拟CPU

fn setup_cpu(&mut self) {
    // ...
    let vcpu = self.vm.create_vcpu(0).unwrap();
    // ...
}

这个函数创建一个虚拟CPU,使用VmFd的create_vcpu函数创建。参数0表示创建一个编号为0的虚拟CPU。

5.设置虚拟CPU的寄存器

let mut vcpu_sregs: kvm_sregs = self
    .vcpu
    .as_ref()
    .unwrap()
    .get_sregs()
    .expect("get sregs failed");
vcpu_sregs.cs.selector = 0;
vcpu_sregs.cs.base = 0;
self.vcpu
    .as_ref()
    .unwrap()
    .set_sregs(&vcpu_sregs)
    .expect("set sregs failed");

let mut vcpu_regs: kvm_regs = self
    .vcpu
    .as_ref()
    .unwrap()
    .get_regs()
    .expect("get regs failed");
vcpu_regs.rax = 0;
vcpu_regs.rbx = 0;
vcpu_regs.rip = 0;
self.vcpu.as_ref().unwrap().set_regs(&vcpu_regs).unwrap();

这个代码块设置虚拟CPU的寄存器。首先,使用VcpuFd的get_sregs函数获取虚拟CPU的状态寄存器,然后设置代码段寄存器(cs)的选择符(selector)和基地址(base)。接着,使用VcpuFd的set_sregs函数设置虚拟CPU的状态寄存器。然后,使用VcpuFd的get_regs函数获取虚拟CPU的一般寄存器,然后将rax、rbx和rip寄存器设置为0。最后,使用VcpuFd的set_regs函数设置虚拟CPU的一般寄存器。

6. 加载内核镜像

fn load_image(&mut self, image: PathBuf) {
    // ...
    let kernel = std::fs::read(image).unwrap();
    // ...
}

这个函数加载内核镜像。使用std::fs的read函数读取内核镜像文件,然后把内核镜像写入虚拟机的内存中。使用VmFd的set_user_memory_region函数设置内存区域。

7.运行虚拟机

fn run(&mut self) {
    // ...
    let vcpu = self.vcpu.as_mut().unwrap();
    loop {
        match vcpu.run().expect("run failed") {
            kvm_ioctls::VcpuExit::Hlt => {
                println!("KVM_EXIT_HLT");
                // sleep 1s using rust std
                std::thread::sleep(std::time::Duration::from_secs(1));
            }
            kvm_ioctls::VcpuExit::IoOut(port, data) => {
                let data_str = String::from_utf8_lossy(data);
                print!("{}", data_str);
            }
            kvm_ioctls::VcpuExit::FailEntry(reason, vcpu) => {
                println!("KVM_EXIT_FAIL_ENTRY");
                break;
            }
            _ => {
                println!("Other exit reason");
                break;
            }
        }
    }
}

这个函数运行虚拟机。使用VcpuFd的run函数运行虚拟CPU,如果虚拟CPU执行HLT指令,则休眠1秒钟,然后继续执行。如果虚拟CPU执行IOOUT指令,则将输出字符串打印到标准输出。如果虚拟CPU执行失败,则退出循环。

客户操作系统代码

这个客户机操作系统,其实也不算是操作系统了,就是一段汇编代码而已,循环往IO端口输出HELLO,然后hlt。

完整的代码文件:https://github.com/fslongjin/kvm_userspace/blob/main/guest_os/kernel.S

详解:

这段汇编代码是一个简单的内核程序,它向0xf1端口输出一些字符(”HELLO\n”),然后进入hlt指令,等待中断或重置。

下面是对代码的逐行解释

.code16gcc

这个指令告诉编译器使用16位代码,以便与实模式兼容。

.text

这个指令告诉编译器下面的代码是代码段。

.global _start

这个指令告诉编译器,_start标签是一个全局符号,可以在其他文件中使用。

.type _start, @function

这个指令告诉编译器,_start标签是一个函数。

_start:

这个标签是程序的入口点。

1:

这个标签定义了一个循环的起点。

mov $0x48,%al
outb %al,$0xf1
mov $0x65,%al
outb %al,$0xf1
mov $0x6c,%al
outb %al,$0xf1
mov $0x6c,%al
outb %al,$0xf1
mov $0x6f,%al
outb %al,$0xf1
mov $0x0a,%al
outb %al,$0xf1

这段代码使用outb指令将字符”H”, “E”, “L”, “L”, “O”, “\n”写入0xf1端口。outb指令的第一个操作数是要写入的数据,第二个操作数是要写入的端口地址。

hlt

这个指令让处理器进入hlt状态,等待中断或重置。hlt指令会使处理器停止执行指令,但不会禁用中断。当有中断发生时,处理器会退出hlt状态。

jmp 1b

这个指令跳转到标签1,实现了一个简单的循环,使程序不停地向端口输出字符。

运行结果

接着,首先在guest_os文件夹下执行make命令编译guest os,接着在外层目录执行cargo run,就能运行起这个kvm用户空间实例了。

执行现象就是,会不断输出HELLO,然后hlt。

如图所示:

 

转载请注明来源:使用Rust构建一个kvm用户空间实例 – 龙进的博客

欢迎关注我的公众号“灯珑”,让我们一起了解更多的事物~

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/20927.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android PickerView简单应用

1. Android-PickerView Android-PickerView这是一款仿iOS的PickerView控件,有时间选择器和选项选择器。 添加依赖项 implementation com.contrarywind:Android-PickerView:4.1.92. 时间选择器 Android-PickerView时间选择器使用Build模式来创建 var timePicker…

鲲鹏昇腾开发者峰会开幕 星辰天合发布新一代天合翔宇一体机

近日,主题为“创未来 享非凡”的鲲鹏昇腾开发者峰会 2023 在东莞松山湖开幕,此次大会旨在帮助开发者深入了解鲲鹏、昇腾全栈技术,加速行业数智化的技术、产品和解决方案创新。 作为鲲鹏生态重要合作伙伴,XSKY星辰天合获邀参加此次…

1053 Path of Equal Weight(超级无敌详细注释+45行代码)

分数 30 全屏浏览题目 切换布局 作者 CHEN, Yue 单位 浙江大学 Given a non-empty tree with root R, and with weight Wi​ assigned to each tree node Ti​. The weight of a path from R to L is defined to be the sum of the weights of all the nodes along the pa…

亲水性Sulfo-Cyanine3 NHS ester水溶性CY3标记活性脂

Sulfo-Cy3是一种荧光染料,可用于生物成像和细胞标记等应用。Sulfo-Cy3是一种含有硫酸基的Cy3染料,具有高度的水溶性和稳定性。Sulfo-Cy3可以与NHS(N-羟基琥珀酰亚胺)结合,形成Sulfo-Cy3 NHS,这种结合物可以…

前端开发之函数式编程实践 | 京东云技术团队

作者:京东科技 牛志伟 函数式编程简介 常见应用场景 1、ES6中的map、filter、reduce等函数 [1,2,3,4,5].map(x > x * 2).filter(x > x > 5).reduce((p,n) > p n);2、React类组件 -> 函数式组件hooks、Vue3中的组合式API 3、RxJS、Lodash和Ramd…

华为基于dhcp snooping表的各种攻击防御

所有的前提是必须开启了dhcp snooping功能 一、dhcp 饿死攻击: 接口下或vlan下开启 dhcp snooping check dhcp-chaddr enable 开启二层源mac和chaddr一致性检测 dhcp snooping max-user-number 1 接口上手动配置的绑定成员数量(可选择项) …

亚马逊云科技作为中国出海力量之一,为中国企业提供技术桥梁

这是一个真实的故事:一家出海企业的项目交付需要在非洲吉布提部署上云,企业负责人在地图上找了半天才找到吉布提,而亚马逊云科技仅用了3天的时间就为企业在当地的业务开展,交付了IT基础设施。对于出海企业来说,这种效率…

文本三剑客awk

awk 工作原理: 逐行读取文本,默认以空格或tab键为分隔符进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。 sed命令常用于一整行的处理,而awk比较倾向于将一行分成多个“字段”然后再进…

如何高效搭建影视及游戏工业化管线?

影视和游戏工业化是指制作流程上呈现出标准化、自动化、平台化、数智化的特征。工业化趋势会让制作影视和游戏门槛变高,让其进入精品对决时代。 不进行迭代,就面临被淘汰的危险。 随着受众对于影视和游戏质量的要求越发“苛刻”,精品化是整…

python:随机森林分类器的性能评估(决策树数量的影响)

作者:CSDN @ _养乐多_ 随机森林(Random Forest)是一种强大的机器学习算法,常用于分类和回归任务。它由多个决策树构成,通过集成学习的方式进行预测。在本篇博客中,我们将探讨随机森林分类器在不同决策树数量下的性能,并绘制相应的图表进行可视化分析。OOB误差,0被误判为…

Kubernetes 二进制部署高可用集群 失败 看报错

概述 openssl证书有问题导致失败,未能解决openssl如何创建私钥,可参考ansible 在私有局域网内完成Kubernetes二进制高可用集群的部署 ETCD Openssl > ca 证书 Haproxy Keepalived Kubernetes 主机规划 序号名字功能VMNET 1备注 1备注 2备注 3 备注…

【C++】-static在类和对象中的作用和细节(下)

💖作者:小树苗渴望变成参天大树 ❤️‍🩹作者宣言:认真写好每一篇博客 💨作者gitee:gitee 💞作者专栏:C语言,数据结构初阶,Linux,C 文章目录 前言 前言 今天我们来讲一个static对类的对象的作用…

C++模板template

我们现在有几个变量,我们向要实现他们的交换,所以我们现在写了一个swap函数 我们现在可以实现对这两个变量之间的交换, 那么我们有有两个变量需要交换呢?? 我们刚才的Swap函数的参数是int类型的,我们现在的…

SOME/IP 草稿

SOME/IP 名词解释 SOME/IP 全称是 Scalable service-Oriented MiddlewarE over IP。也就是基于 IP 协议的面向服务的可扩展性通信中间件协议。 面向服务 SOA基于 IP 协议之上的通信协议中间件 SOME/IP 功能 服务发现 (Service Discovery)远程服务调用 (RPC,rem…

ConvTranspose2d 的简单例子理解

文章目录 参考基础概念output_padding 简单例子: stride2step1step2step3 参考 逆卷积的详细解释ConvTranspose2d(fractionally-strided convolutions)nn.ConvTranspose2d的参数output_padding的作用torch.nn.ConvTranspose2d Explained 基础概念 逆卷…

VMware、CentOS、XShell、Xftp的安装

第 1 章 VMware 1.1 VMware 安装 一台电脑本身是可以装多个操作系统的,但是做不到多个操作系统切换自如,所以我们 需要一款软件帮助我们达到这个目的,不然数仓项目搭建不起来。 推荐的软件为 VMware,VMware 可以使用户在一台计…

一篇文章搞定《Android中的ANR》

------《ANR》 什么是ANR举个例子帮你认识ANRANR的产生原因ANR的监控手段方法一: 监控trace文件夹方法二:利用我们主线程的Looper方法三:监控SIGQUIT信号 ANR日志Traces.txtTraces文件分析几个分析案例:一、好定位的问题(简单案例…

【C++】设计模式

目录 设计模式概述 单例模式 饿汉模式 懒汉模式 工厂模式 简单工厂模式 工厂方法模式 抽象工厂模式 观察者模式 设计模式概述 设计模式:一套反复被人使用、多数人知晓的、经过分类编目的代码设计经验的总结。一种固定的写代码的思维逻辑方式,一…

小学妹刚毕业没地方住想来借宿,于是我连夜用Python给她找了个好房子,我真是太机智了

事情是这样的,小学妹刚毕业参加工作,人生地不熟的,因为就在我附近上班,所以想找我借宿。。。 想什么呢,都不给住宿费,想免费住?于是我用Python连夜给她找了个单间,自己去住吧&#…

ChatGPT api 接口调用测试

参考文档: https://platform.openai.com/docs/quickstart/build-your-application示例说明: 本示例会生成一个简单的ChatGPT api接口调用server程序,该程序可以给用户输入的宠物类别为宠物取三个名字。打开网页后,会看到用户输入…