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

news2024/12/25 12:19:37

最近在学习虚拟化相关的内容,想着使用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用户空间实例 – 龙进的博客

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

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maven依赖管理

文章目录 1 依赖传递与冲突问题2 可选依赖和排除依赖方案一:可选依赖方案二:排除依赖 Masked5 / heima_maven_codes GitCode 我们现在已经能把项目拆分成一个个独立的模块&#xff0c;当在其他项目中想要使用独立出来的这些模块&#xff0c;只需要在其pom.xml使用<depende…

看干货,10个网络安全小知识

如今&#xff0c;大家的生活与互联网已密不可分&#xff0c;每天享受着网络带给我们的服务和便利&#xff0c;工作、娱乐、购物、刷热点……&#xff0c;但网络也是一把双刃利器网络风险无孔不入&#xff0c;信息泄露、网络诈骗、虚假信息满天飞……所以&#xff0c;网络安全不…

JavaWeb-JQuery的学习

1、JQuery快速入门 1.1、JQuery介绍 jQuery 是一个 JavaScript 库。所谓的库&#xff0c;就是一个 JS 文件&#xff0c;里面封装了很多预定义的函数&#xff0c;比如获取元素&#xff0c;执行隐藏、移动等&#xff0c;目的就是在使用时直接调用&#xff0c;不需要再重复定义&…

图解LeetCode——142. 环形链表 II

一、题目 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统…

绚丽的流光心

快到520了&#xff0c;送大家一颗心吧。 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>body {background-color: #000;margin: 0;overflow…

国内免费版ChatGPT

目录 前言&#xff1a;网站大全 1. ChatGPT是什么 2. ChatGPT的发展历程 3. ChatGPT对程序员的影响 4. ChatGPT对普通人的影响 5. ChatGPT的不足之处 前言&#xff1a;网站大全 AI文本工具站 (laicj.cn) ——gpt-3.5 功能强大(推荐&#xff09; Chatgpt在线网页版-…

2024王道数据结构考研丨第四章:串

2024王道数据结构考研笔记专栏将持续更新&#xff0c;欢迎 点此 收藏&#xff0c;共同交流学习… 文章目录 第四章&#xff1a;串4.1串的定义和实现4.1.1串的定义4.1.2串的基本操作4.1.3串的存储结构 4.2串的模式匹配4.2.1朴素模式匹配算法4.2.2改进的模式匹配算法——KMP算法 …

【SQLServer】sqlserver数据库导入oracle

将sqlserver数据库导入到oracle 实用工具&#xff1a; SQL Server Management Studio 15.0.18424.0 SQL Server 管理对象 (SMO) 16.100.47021.07eef34a564af48c5b0cf0d617a65fd77f06c3eb1 Microsoft Analysis Services 客户端工具 15.0.19750.0 Microsoft 数据访问组件 (MDAC) …

漏洞扫描的原理

漏洞扫描是指通过自动或者手动的方式&#xff0c;对系统进行全面扫描&#xff0c;发现系统中存在的漏洞。随着互联网的发展&#xff0c;漏洞扫描的重要性越来越凸显&#xff0c;因为漏洞一旦被黑客利用&#xff0c;就可能会导致系统被攻击、数据被窃取等问题。那么什么是漏洞扫…

【实践篇】教你玩转JWT认证---从一个优惠券聊起 | 京东云技术团队

引言 最近面试过程中&#xff0c;无意中跟候选人聊到了JWT相关的东西&#xff0c;也就联想到我自己关于JWT落地过的那些项目。 关于JWT&#xff0c;可以说是分布式系统下的一个利器&#xff0c;我在我的很多项目实践中&#xff0c;认证系统的第一选择都是JWT。它的优势会让你…

shallowRef和shallowReactive的使用?

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、 shallowRef&#xff1f;二、 shallowReactive&#xff1f;在什么时候使用&#xff1f; 三、案例1、shallowRef2、shallowReactive 提示&#xff1a;以下是本篇…

为世界第一大癌症高效研发首创新药,AI大模型助力药物研发叩开未来之门

近日&#xff0c;三位高中生引爆了医药圈&#xff0c;他们使用人工智能&#xff08;AI&#xff09;引擎进行靶点发现&#xff0c;确定了多形性胶质母细胞瘤&#xff08;GBM&#xff09;的新治疗靶点&#xff0c;多形性胶质母细胞瘤&#xff08;GBM&#xff09;是最具侵袭性和最…

在外出差,如何远程登录公司内网金蝶云ERP管理系统【cpolar内网穿透】

文章目录 前言1.金蝶安装简介2. 安装cpolar内网穿透3. 创建安全隧道映射4. 在外远程访问金蝶云星空管理中心5. 固定访问地址6. 配置固定公网访问地址7.创建数据中心简介8.远程访问数据中心9. 固定远程访问数据中心地址10. 配置固定公网访问地址 转发自CSDN风浪越大%鱼越贵的文章…

微信小程序最新获取头像昵称方式

前言 版本历史变迁 一、获取头像的正确姿势 二、获取昵称的正确姿势 总结 前言 产品需要获取微信用户的昵称和头像。 这这还不简单&#xff0c;so easy&#xff01; 通过wx.getUserProfile或者 wx.getUserInfo 就可以获取到。 但是获取的昵称是”微信用户“获取的头像是…

LeetCode 515. 在每个树行中找最大值

515. 在每个树行中找最大值 描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09; 示例 示例1 输入&#xff1a;root [1,3,2,5,3,null,9] 输出&#xff1a;[1,3,9] 示例2 输入&#…

OpenHarmony dump渲染和合成图层SurfaceBuffer指南

OpenHarmony dump渲染和合成图层SurfaceBuffer指南 引言 博客停更很久了&#xff0c;提起笔来渐感生疏啊&#xff01;看来&#xff0c;还是得抽出时间来更新更新啊&#xff01;好了&#xff0c;感慨也发完了&#xff0c;是时候切入正题了。本篇博客主要以本人在实际项目的开发中…

Excel技巧之 【提取文件夹内的全部文件名】

在工作中&#xff0c;有时候需要将所有文档的名字提取出来做成表格。 这种情况&#xff0c;你是一个一个复制粘贴么&#xff1f;nonono&#xff01; 如果你是复制粘贴的&#xff0c;一定要试试下面的方法&#xff0c;可以快速提取文件名。 具体步骤&#xff1a; 1. 将所有的…

chatgpt赋能Python-python3_5怎么算

Python3|5是如何计算的&#xff1f; 介绍 Python是一种高级编程语言&#xff0c;许多开发人员喜欢使用它来构建各种应用程序&#xff0c;从网站到机器学习应用程序。然而&#xff0c;在使用Python编写代码时&#xff0c;很多人都会遇到一个问题&#xff1a;Python3|5计算是如…

粪菌移植——一种治疗人体疾病的新型疗法

谷禾健康 粪菌移植是一项近年来备受关注的医疗技术&#xff0c;它涉及将健康捐赠者的粪便物质转移至患有疾病或障碍患者的胃肠道。 简单来说就是选择健康合适的人粪便&#xff0c;通过科学方法提取出有用的微生物&#xff0c;去除有害与无用的部分&#xff0c;然后制成制剂&…

Redis缓存实战

一 Redis缓存简介 二 Redis缓存入门 在我们查询数据一般都是直接查询数据库&#xff0c;返回给前端。为了提高效率实际项目中会将基础数据等热点数据放入redis缓存中。 发起请求&#xff0c;先访问redis缓存&#xff0c;缓存中有直接将数据返回&#xff1b;缓存没有命中&…