#虚拟机 #框架 #虚拟 #机器 #模拟

bin+lib modVM

一个用于轻松创建模块化虚拟机的框架

3 个不稳定版本

0.3.0 2019 年 3 月 19 日
0.2.1 2019 年 3 月 18 日
0.2.0 2019 年 2 月 28 日

模拟器 中排名 #115

每月下载量 48

MIT 许可证

27KB
586

ModVM-Rust

ModVM 为在 Rust 中构建虚拟机提供了一个简单的框架。ModVM 中的虚拟机是模块化的,并且为此目的由两种不同类型的组件组成:处理器和外围设备。

执行模型

虚拟机初始化时带有处理器向量和外设向量,并自动构建每个处理器和外设之间的通信通道。处理器在其 exe_ins() 函数中向下发送请求,该函数在循环中被调用。当任何外围设备通过通道接收数据时,它的 handle(request) 函数被调用,并返回供处理器使用的数据。

初始化后,虚拟机返回一个 MachineHandle 结构体,它可以用来加入虚拟机的线程。

用法

双向通道结构体

modVM 包实现了 TwoWayChannel<I, O> 结构体,以便在虚拟机组件之间进行轻松通信。

TwoWayChannel 总是成对实例化,并且每一对 ('end') 包含两个通道,用于通过通道进行双向通信。它接受以下方法:

  • send - 通过通道发送数据。
  • recv - 等待下一个项目,然后返回它。
  • try_recv - 测试在通道队列中找到项目;如果找到它,则返回它。
  • iter - recv 的循环版本。
  • try_iter - try_recv 的循环版本。
  • query - 通过通道发送数据,然后等待直到收到回复,然后返回它。

在内部,处理器使用类型 modVM::Query 发出请求,而外设则使用 modVM::Response 进行响应。

处理器特质

Processor<BASE> 特质要求在结构体上实现 exe_ins() 函数。此函数必须接受一个 FrontEnd<BASE>TwoWayChannel<Query<BASE>> 的包装器)的向量以及 &mut self,并返回 Result<(), BASE>。如果处理器中的任何函数返回 Err,则处理器会终止。

一个简单的实现可能是

impl Processor<u8> for ProcessorEightBit {
    fn exe_ins(&mut self, channels: &Vec<FrontEnd<u16>>) -> Result<(), u8> {
        // get instruction:
        let ins = channels[0].query(LoadRequest(self.counter)).unwrap();

        match ins {
            
            /* snip */

            // instruction didn't match:
            _ => Err(0),
        }
    }
}

外设特质

Peripheral<BASE> 特质与网络服务器的操作类似:其主要目的是发送和接收数据请求。这是通过 handle() 函数实现的,该函数接受一个 Query 枚举请求并输出一个 Result<Response<BASE>, BASE>,如果为 Ok,则解包并沿通道发送回。这种灵活性为 ModVM 快速创建不同的虚拟机组件提供了广阔的天地。每次外设收到请求时都会调用 handle() 函数。

一个简单的实现可能是

impl Peripheral<u8> for MemEightBit {
    fn handle(&mut self, incoming: Query<u16>) -> Result<Response<u16>, u16> {
        Ok(match incoming {
            LoadRequest(x) => {
                match self.mem.get(x as usize) {
                    Some(y) => {
                        Data(*y)
                    },
                    None => Fail(0),
                }
            },
            SaveRequest(x, y) => {
                match self.mem.get_mut(x as usize) {
                    Some(z) => {
                        *z = y;
                        Good
                    },
                    None => Fail(0),
                }
            },
        })
    }
}

然而,Peripheral 不仅限于内存。例如,您可以配置一个作为 GPU,或处理 I/O。为此,可以使用可选的 cycle 函数来运行每个周期都需要活跃的过程。但是,此函数只能修改组件的状态。

共享函数

上述所有特质都共享某些函数:metadataboothalt。元数据 metadata 指定了虚拟机的数据,并返回一个 Metadata 结构体。这用于线程的标识。 boothalt 比较直观;它们在启动和终止后运行,在 Processor 中,函数可以访问通道,因此可以发出数据请求。然而,在这些函数中,只有 metadata 函数是编译所必需的。

以下是在 Processor 上实现所有这些函数的示例

impl Processor<u8> for ProcessorEightBit {
    fn boot(&mut self, channels: &Vec<FrontEnd<u8>>) -> Result<(), BASE> {
        channels[0].query(SaveRequest(PRINT, self.bootcode)).unwrap(); // print the bootcode
        Ok(())
    }

    fn halt(&mut self, channels: &Vec<FrontEnd<u8>>) -> Result<(), BASE> {
        channels[0].query(SaveRequest(PRINT, self.error)).unwrap(); // print the error message

        for i in channels {
            i.send(LoadRequest(HALT)); // halt all peripherals
        }
    }
}

以及在外设上的实现

impl Peripheral<u8> for MemEightBit {
    fn boot(&mut self) -> Result<(), BASE> {
        /* initialise things */
        Ok(())
    }

    fn halt(&mut self) -> Result<(), BASE> {
        /* halt processes */
        Ok(())
    }
}

无运行时依赖