SimBricks 作为仿真框架,运行入口大概率是可执行的 Python 脚本 / Shell 脚本,优先搜索这类文件
- 替换模拟器映射 :将
sim.QemuSim替换为 Gem5 对应的模拟器类sim.Gem5Sim; - 适配 Gem5 主机类型 :Gem5 有专属的主机类(
Gem5Host),需替换原有的I40ELinuxHost; - 补充 Gem5 必要配置:设置 Gem5 的 CPU 类型、内存大小等必填参数(QEMU 可默认,Gem5 必须显式指定)。
System-Host
这个文件(推测是 simbricks/orchestration/system/host/base.py 或类似路径)定义了仿真中「主机」的抽象模型:
- 描述主机的硬件属性(内存、核心数、磁盘);
- 管理主机上运行的应用(如 Ping、Sleep);
- 处理主机与外设(如 NIC 网卡)的 PCIe 连接;
- 生成主机的配置命令(如网卡驱动加载、IP 配置);
- 支持 JSON 序列化 / 反序列化(用于仿真配置的持久化)
类的继承关系(核心脉络)

-
底层基类 :
base.Component是所有仿真组件(主机、NIC、交换机)的根类,提供基础的 ID、系统关联、接口管理能力; -
逐层扩展:每个子类在父类基础上增加专属功能(如 Linux 系统特性、特定网卡驱动)。
-
add_app():向主机添加仿真应用(如你之前代码中的PingClient); -
connect_pcie_dev():建立主机与 PCIe 设备(如 NIC 网卡)的连接,是主机与外设交互的核心入口; -
toJSON()/fromJSON():实现主机配置的序列化 / 反序列化(用于仿真配置的保存和加载)。
全系统主机类 FullSystemHost
- 硬件参数:内存、核心数、CPU 频率(对应真实主机的硬件配置);
- 磁盘管理:
add_disk()方法添加磁盘镜像(如你代码中的DistroDiskImage); - 异步准备:
prepare()方法异步准备所有磁盘镜像(仿真启动前的前置操作)。
Linux 主机基础类 BaseLinuxHost
- 应用限定:仅允许添加
BaseLinuxApplication类型的应用(如 Ping、Sleep); - 命令生成:
run_cmds():生成主机上运行应用的命令;cleanup_cmds():生成仿真结束后的清理命令;config_str():拼接所有配置命令(启动、应用、清理),最终传递给仿真器;
- 配置文件:
config_files()支持向仿真主机内挂载额外文件(如驱动文件)。
通用 Linux 主机类 LinuxHost
-
清理逻辑:
cleanup_cmds()增加poweroff -f(仿真结束后强制关机); -
初始化配置:
prepare_pre_cp():设置环境变量、主机名、/etc/hosts 等基础配置;prepare_post_cp():加载驱动、配置网卡(IP、MAC、启动网卡);
-
网卡配置:自动遍历主机的 PCIe 接口,为连接的 NIC 配置 IP 地址、启动网卡(核心逻辑)。
遍历主机的PCIe接口,找到连接的NIC网卡
for host_inf in base.Interface.filter_by_type(self.interfaces(), pcie.PCIeHostInterface):
inf = host_inf.get_opposing_interface()
# 配置网卡IP:eth0/eth1...,添加IP地址(如10.0.0.1/24)
cmds.append(f"ip addr add {com._ip}/24 dev {ifn}")
这部分代码对应你之前脚本中 nic0.add_ipv4("10.0.0.1") 的实际生效逻辑 ------LinuxHost 会自动读取 NIC 的 IP 配置,生成对应的 ip addr add 命令。
针对 Intel I40E 网卡的专用主机类,仅在通用 LinuxHost 基础上增加了 i40e 驱动的自动加载(无需手动配置)。
你之前代码中使用的 host0 = system.I40ELinuxHost(sys) 就是实例化此类,因此主机启动时会自动加载 i40e 驱动,适配 Intel I40eNIC 网卡。
所有仿真组件(主机、NIC、交换机)都通过「接口(Interface)」实现交互 ,add_if() 就是把「接口」绑定到「组件」上的核心方法,是组件能和外部设备通信的前提。
add_if = add interface(添加接口),作用是:
将一个「通信接口对象」(比如这里的
PCIeHostInterface)注册到当前组件(比如主机)中,让组件拥有这个接口,从而具备和外部设备通信的能力。
序列化 / 反序列化的意义
toJSON()/fromJSON() 方法用于将主机配置转换为 JSON 格式,核心用途:
- 仿真配置的保存:将复杂的主机配置(硬件、应用、驱动)保存为文件;
- 跨进程 / 节点传输:仿真调度器(Orchestrator)通过 JSON 传递主机配置给仿真器(如 QEMU/Gem5);
- 仿真回放:加载历史配置,复现相同的仿真场景。
现实中,一台主机可以插多个 PCIe 设备(比如 1 块网卡 + 1 块显卡 + 1 块固态硬盘),对应到仿真中:
- 每个 PCIe 设备都需要一个独立的「插槽(接口)」;
- 主机通过
add_if()管理所有插槽,框架能清晰知道「主机有哪些 PCIe 接口,分别连了什么设备」
Sim

-
sim_base.Simulator:所有仿真器的根类,封装了仿真器的基础属性(可执行文件路径、名称、组件列表); -
HostSim:主机仿真器的通用抽象,定义了主机仿真器的共性接口(如supported_image_formats、full_name); -
Gem5Sim/QemuSim:具体的仿真器实现,适配各自的命令行参数、磁盘格式、同步逻辑。 -
继承
sim_base.Simulator,获取仿真器的基础能力(组件管理、ID 生成、JSON 序列化); -
定义主机仿真器的通用接口(未实现的抽象方法,由子类实现):
supported_image_formats():声明支持的磁盘镜像格式(Gem5 支持 raw,QEMU 支持 raw/qcow2);add():将「主机组件」(如I40ELinuxHost)绑定到仿真器;full_name():生成仿真器的唯一名称(如host.Gem5Sim-1);supported_socket_types():声明支持的 socket 类型(默认CONNECT,用于组件间通信)。
核心方法:run_cmd()(生成 Gem5 运行命令)
这是整个类的核心,负责将「主机配置」转换为 Gem5 的命令行参数,拆解关键逻辑:
① 基础命令拼接
python
运行
cmd = f"{inst.env.repo_base(f'{self._executable}.{self._variant}')} --outdir={输出目录} "
cmd += "额外主参数 "
cmd += f" gem5配置脚本路径 --caches --l2cache 缓存参数 --cpu-clock={主机CPU频率} "
gem5.opt执行文件:拼接变体(如gem5.fast),对应不同编译版本;- 配置脚本:
sims/external/gem5/configs/simbricks/simbricks.py(SimBricks 定制的 Gem5 配置脚本); - 缓存参数:指定 L1/L2 缓存大小、关联度(32kB L1、32MB L2)。
镜像
DistroDiskImage 是 SimBricks 框架中对预打包发行版磁盘镜像 的封装类,其核心逻辑不是 "创建 / 构建镜像",而是定位和验证已存在的预打包镜像文件,为仿真主机提供可挂载的磁盘路径。
初始化(__init__)
- 接收两个关键参数:
h(关联的主机实例)、name(镜像名称,如"base"); - 定义该镜像支持的格式:
raw(原始镜像)、qcow2(QEMU 写时复制镜像); - 不实际创建镜像,仅记录镜像名称和支持的格式。
**路径生成(path() 方法)**这是核心方法,作用是根据指定格式,返回镜像文件的实际路径:
# 步骤1:通过仿真环境获取基础路径
path = inst.env.hd_path(self.name)
# 步骤2:根据格式拼接后缀(仅 raw 加后缀,qcow2 不加)
if format == "raw": path += ".raw"
# 步骤3:验证文件存在,不存在则抛异常
DiskImage.assert_is_file(path)
磁盘镜像格式
磁盘镜像格式,本质是把物理硬盘 / 分区的全部数据 + 结构,按特定规则打包成的单个文件格式 ------ 就像你把一个文件夹打包成 zip/rar 格式,不同的是,磁盘镜像包是给模拟器(Gem5/QEMU)"识别和使用" 的,模拟器能直接把这个文件当成真实硬盘来读写
- 真实电脑:硬盘 → 分区 → 系统 / 数据;
- 模拟器(Gem5/QEMU):磁盘镜像文件(如
disk.raw/disk.qcow2)→ 模拟器解析格式 → 模拟出 "硬盘" 供虚拟机 / 仿真程序使用
不同模拟器只能识别 / 使用特定格式的磁盘镜像文件,超出支持范围的格式会无法加载

Gem5 的核心定位是「微架构仿真」(关注 CPU / 内存的执行细节),而非 "完整虚拟化"------raw 格式最简单,无需复杂的解析逻辑,能让 Gem5 把资源集中在核心仿真功能上;而 qcow2 的快照、压缩等功能对 Gem5 来说是 "冗余" 的,所以不做支持。
QEMU 是「全功能虚拟化工具」(模拟完整的计算机系统),需要兼顾 "兼容性" 和 "实用性":
- 支持 raw:兼容其他工具(如 Gem5)生成的镜像,方便跨工具复用;
- 支持 qcow2:满足实际使用需求(比如节省磁盘空间、做快照回滚),是 QEMU 日常使用的主流格式。
假设你要创建一个 "10GB 容量、实际存 2GB 数据" 的镜像:
- raw 格式:文件大小 = 10GB(不管实际存多少,占满标称空间);
- qcow2 格式:文件大小 ≈ 2GB(只存实际数据,节省空间);
- 如果你想在 Gem5 中使用这个镜像:必须转成 raw 格式(否则 Gem5 识别不了);
- 如果你想在 QEMU 中使用:直接用 qcow2 即可(更省空间),也可以用 raw(兼容但占空间)
模拟器(QEMU/Gem5)给操作系统 / 程序 "造了个假"------ 让它们以为自己在操作物理硬盘,但实际上所有读写指令都被模拟器拦截,转而操作一个普通的镜像文件。
模拟器能做到这一点,本质是靠「硬件抽象 + 地址映射 + 指令拦截」
真实硬盘的核心结构是「扇区(Sector)」------ 硬盘被分成一个个固定大小的块(通常 512 字节 / 4KB),所有数据按扇区地址读写;镜像文件(比如 raw 格式)会完全复刻这个结构:
- raw 格式文件的第 0-511 字节 = 硬盘第 0 扇区(MBR / 分区表);
- 第 512-1023 字节 = 硬盘第 1 扇区;
- 以此类推......相当于把硬盘的 "扇区地址" 直接映射成文件的 "字节偏移地址",一一对应。
步骤 2:模拟器拦截所有 "硬盘读写指令"
操作系统 / 程序要操作硬盘时,会通过「CPU 指令 + 硬件寄存器」向硬盘控制器发命令(比如 "读第 100 扇区");模拟器(QEMU/Gem5)作为 "虚拟硬件层",会拦截这些指令:
- 不把指令发给真实的物理硬盘控制器;
- 而是解析指令中的 "扇区地址",转换成镜像文件的 "字节偏移"(比如第 100 扇区 = 100×512 = 51200 字节偏移);
- 然后对镜像文件执行普通的文件读写(读 / 写 51200 字节位置开始的 512 字节)。
步骤 3:返回 "伪装的结果" 给操作系统
模拟器完成文件读写后,会把结果打包成 "硬盘控制器的响应格式",返回给操作系统 / 程序;操作系统完全察觉不到差异 ------ 它以为自己收到的是物理硬盘的响应,实际上是模拟器从镜像文件里读出来的数据。
不同格式的核心区别,只在 "扇区→文件偏移" 的映射逻辑,拦截 / 返回的流程完全一样:
比如同样是 "读第 100 扇区":
- raw:直接读文件的 100×512=51200 字节位置;
- qcow2:先读 qcow2 文件里的 "扇区映射表",看第 100 扇区实际存在文件的哪个位置,再读对应位置(如果该扇区没数据,直接返回空,不用占用文件空间)。
脚本
给 host1(Gem5/QEMU 仿真的 Linux 主机)添加一个后台运行的 Bash 脚本应用 ,作用是在仿真过程中自动收集 host1 的系统 / 网络信息,并把输出保存到主机内的 /tmp/host1_net_check.log 文件中,用于后续验证和调试。
基础结构:system.ScriptApp(host1, script="...", wait=False)
-
system.ScriptApp:SimBricks 提供的 "脚本应用类",用于在仿真主机中执行自定义 Shell 脚本; -
host1:指定脚本要在哪个主机上运行(这里是 Ping 服务端host1); -
script="""...""":要执行的 Bash 脚本内容(多行字符串); -
wait=False:脚本后台运行 ,不阻塞仿真流程(即使脚本没执行完,仿真也会继续);如果设为True,仿真会等待脚本执行完毕后再继续。#!/bin/bash # 声明脚本用 Bash 解释器执行
1. 输出分隔符,标记日志开始
echo "=== Host1 系统信息 ===" >> /tmp/host1_net_check.log
2. 输出内核版本、CPU 架构等系统信息,追加到日志文件
uname -a >> /tmp/host1_net_check.log
3. 输出所有网卡的 IP/MAC 地址、状态等信息,追加到日志文件
ip addr show >> /tmp/host1_net_check.log
4. 测试到 host0(10.0.0.1)的连通性(Ping 3 次),追加到日志文件
2>&1:把错误输出(如 Ping 超时)也重定向到日志文件,避免遗漏报错
ping -c 3 10.0.0.1 >> /tmp/host1_net_check.log 2>&1

Simple_ping_ns3
DistroDiskImage:基础操作系统镜像(如 Ubuntu),提供运行环境;LinuxConfigDiskImage:临时配置盘,用于注入 IP 配置、驱动加载脚本等(无需修改基础镜像)。

- 场景举例 :你要跑 10 个不同的仿真案例(不同 IP、不同驱动、不同启动脚本),如果只用一个
DistroDiskImage:- 每次仿真都要修改基础镜像(比如改
/etc/hosts、装驱动、改 IP); - 改完后基础镜像被污染,下次跑其他案例时,必须重新制作镜像,否则会出现 "上一次的配置影响本次仿真" 的问题;
- 每次仿真都要修改基础镜像(比如改
- 双磁盘方案 :
DistroDiskImage始终保持 "干净的基础系统",10 个案例都复用这一个镜像;LinuxConfigDiskImage为每个案例单独生成临时配置,仿真结束后自动删除,完全不影响基础镜像
Project
from simbricks.orchestration import system # 系统组件(主机、NIC、交换机)
from simbricks.orchestration import simulation as sim # 仿真器(QEMU/NS3/NIC)
from simbricks.orchestration.simulation.net import ns3_components # ns-3 组件(日志级别等)
from simbricks.orchestration import instantiation as inst # 仿真实例化(控制执行)
from simbricks.orchestration.helpers import simulation as sim_helpers # 仿真辅助函数(同步控制)
App.py
utils_base.IdObj(基础ID类)
↓
Application(所有应用的基类)
↓
BaseLinuxApplication(Linux主机应用抽象基类)
↓
├─ PingClient(Ping客户端)
├─ Sleep(休眠应用)
├─ NetperfServer/NetperfClient(网络性能测试)
└─ IperfTCPServer/IperfUDPServer/IperfTCPClient/IperfUDPClient(带宽测试)
Netperf/Iperf 系列(网络性能测试)
Netperf:测试网络吞吐量、延迟(TCP_RR 模式可测往返延迟);
Iperf:测试 TCP/UDP 带宽(支持多进程、大缓冲区);
核心逻辑:run_cmds() 返回 netserver/netperf/iperf 命令,用于网络性能基准测试
- 接口标准化 :所有 Linux 应用都通过
run_cmds()输出要执行的命令,SimBricks 框架会自动将这些命令注入到仿真主机中执行; - 可扩展 :新增自定义应用只需继承
BaseLinuxApplication并实现run_cmds(); - 序列化支持 :所有类都实现
toJSON/fromJSON,便于仿真配置的保存和分布式执行
run_cmds 方法不是打印命令 ,而是定义要在模拟节点上真实执行的命令列表
这些命令最终会被 SimBricks 框架注入到模拟的 Linux 主机(Guest OS)中实际运行 ,而非仅打印日志 ------ 你可以把 run_cmds 理解为 "给模拟主机下达的'操作清单'",框架会负责把这些命令送到模拟环境里执行

Instantiation
instantiation" 的意思是 实例化。
在编程和计算机科学中,它指的是根据一个类(class)或模板(template)来创建一个具体的、可用的对象(object)或实例(instance)的过程
Orchestration (编排)
在技术领域,"编排"指的是自动化配置、协调和管理计算机系统、中间件及服务的过程。
- 简单来说,就是指挥和协调多个独立的组件,让它们协同工作来完成一个复杂的任务。
- 常见的例子有 Kubernetes(用于容器编排)、Terraform(用于基础设施编排)。
2. Instantiation (实例化)
如上所述,这里指的是创建具体实例的动作。在编排的语境下,这通常意味着"启动"或"创建"一个服务、一个虚拟机、一个容器或一个应用程序。
综合理解
因此,orchestration/instantiation/ 这个目录很可能存放的是负责启动和创建服务实例的代码、脚本或配置文件
假设你正在开发一个电商平台,你需要部署一个数据库、一个后端API和一个前端网页。
- Orchestration (编排):就是整个部署流程,它定义了先部署数据库,等数据库好了再部署后端API,最后部署前端。
- Instantiation (实例化):就是编排流程中的具体一步,比如"启动一个MySQL数据库容器"或"创建一个API服务器的虚拟机"。
所以,orchestration/instantiation/ 目录下可能就包含了:
- 用于启动数据库的脚本。
- 用于创建API服务实例的配置文件。
- 定义如何从零开始构建一个应用实例的代码。
总而言之,这个目录里的内容是整个自动化流程中,专门负责"从无到有"创建出具体服务实例的那一部分。
InstantiationEnvironment:管理仿真运行所需的所有路径(工作目录、临时文件、镜像、日志等),是整个仿真的 "文件系统环境";Instantiation:管理单个仿真任务的完整生命周期(环境初始化、碎片分配、套接字 / 代理配置、检查点管理、依赖解析等),是仿真从 "配置" 到 "运行" 的核心桥梁。
hd_path()解析磁盘镜像路径:如果是绝对路径直接用,否则从项目 images/ 目录读取统一管理 "自定义镜像" 和 "内置镜像" 的路径
Instantiation:仿真实例化的 "总控制器"
核心作用
把「仿真配置(Simulation)」转换成「可执行的仿真任务」,核心职责包括:
- 管理仿真碎片(Fragment):将大型仿真拆分为多个可分布式运行的碎片;
- 配置套接字 / 代理:连接不同仿真组件(如主机 <-> 网卡、主机 <-> 内存);
- 检查点管理:支持仿真状态的保存 / 恢复(加速启动);
- 路径与环境绑定:关联
InstantiationEnvironment; - 依赖解析:构建仿真组件的启动依赖关系(如先启动网卡,再启动主机)




「命令注入」在 base.py 中的底层支撑:
env提供了脚本挂载的基础路径(如tmp_dir→/tmp/sim-exp-1/tmp);fragments保证包含主机的碎片被正确分配,命令能被解析;prepare()清理并创建挂载目录,保证脚本能被正确挂载;- 套接字管理保证容器内的命令能和其他仿真组件通信(如 ping 命令能发送到目标主机)。
简单说:base.py 不直接处理「命令注入」,但它提供了命令执行的底层环境和核心规则 ------ 没有 base.py 管理的路径、套接字、碎片,后续的命令封装和执行都无法实现。
add_app() 只是把应用 "注册" 到主机的列表中,而真正解析、转换、执行这些应用 的逻辑,分散在 Instantiation(实例化) 和 仿真器(如 Gem5Sim/QemuSim) 的代码中,而非 System 类本身
System 类(system/base.py)只是存储所有组件(Host/NIC/Switch)的容器 ,本身不处理应用逻辑;Host 的 applications 列表,是在仿真实例化阶段 被框架解析,最终由对应仿真器(如 Gem5Sim) 转换为可执行的命令 / 脚本,注入到仿真主机中运行。
instantiation = inst_helpers.simple_instantiation(simulation) 是关键入口,该方法会创建 Instantiation 实例(instantiation/base.py),核心作用是:
- 遍历
simulation中的所有组件(Host/NIC/Switch); - 为每个 Host 生成执行上下文(如挂载目录、环境变量、启动脚本);
- 调用仿真器(如 Gem5Sim)的
prepare()方法,处理 Host 的 applications。
Fragment 是 "一组要在同一进程 / 机器上运行的模拟器",fragments 属性通过 setter 严格校验分片的合法性:
组件间通信(如 Host-NIC、Host - 内存)依赖套接字(Socket),Instantiation 负责为每个接口分配套接字路径和类型(LISTEN/CONNECT):

inst = inst_helpers.simple_instantiation(simulation) 为例,完整链路:
- 创建 Instantiation :关联你的
simulation(包含 Gem5Sim/Host1); - 设置 Fragment :
fragment.add_simulators(*simulation.all_simulators())→inst.fragments = [fragment],校验分片合法性; - 绑定环境 :
inst.env = InstantiationEnvironment(workdir, simbricksdir),初始化所有路径
Simulator
Simulator:所有模拟器(如 Gem5Sim/I40eNicSim)的抽象基类,定义了模拟器的通用行为(组件管理、命令生成、套接字处理、生命周期等);Simulation:仿真实验的总容器,管理所有模拟器实例、组件 - 模拟器映射、通道(Channel)、资源需求等,是连接 "静态配置" 和 "动态运行" 的核心。
简单说:Simulator 是 "单个仿真器" 的模板,Simulation 是 "整个仿真实验" 的管家。
所有具体模拟器都继承此类,封装了模拟器的通用逻辑:
- 组件管理(添加 / 过滤组件);
- 命令生成(
run_cmd抽象方法,子类必须实现); - 套接字 / 通道处理(通信相关);
- 生命周期(prepare/cleanup);
- 资源需求(核心 / 内存);
- 序列化 / 反序列化(JSON 转换)。
simple_simulation中创建的模拟器,通过simulation.add_sim(self)加入_sim_list;- 通过
simulator.add(comp)→simulation.add_spec_sim_map(comp, sim)写入_sys_sim_map; - 最终
Simulation的_sim_list和_sys_sim_map都包含了所有模拟器 / 组件的关联关系

SimBricks 框架中主机模拟器的具体实现层 ,定义了两种主流的主机仿真器(Gem5Sim/QemuSim),均继承自通用的 HostSim 基类,核心职责是:
- 实现
Simulator抽象基类的所有抽象方法(如run_cmd/supported_socket_types); - 封装 Gem5/QEMU 模拟器的启动参数、资源需求、生命周期逻辑;
- 对接 Host 组件的配置(如 CPU 核心数、内存大小、磁盘镜像),生成可直接执行的模拟器启动命令;
- 处理 Host 与外设(PCIe/NIC/ 内存)的通信参数(套接字、延迟、同步)。
简单说:这个文件是「通用模拟器基类」到「具体主机模拟器」的落地实现,是指令从 Instantiation 层传递到 "真实模拟器进程" 的最后一环。
Gem5的run_cmd
def run_cmd(self, inst: inst_base.Instantiation) -> str:
# 1. 选择CPU类型(检查点/正常运行)
cpu_type = self.cpu_type if not inst.create_checkpoint else self.cpu_type_cp
# 2. 验证:Gem5Sim仅支持1个FullSystemHost
full_sys_hosts = self.filter_components_by_type(ty=sys_host.BaseLinuxHost)
if len(full_sys_hosts) != 1:
raise Exception("Gem5Sim only supports simulating 1 FullSystemHost")
host_spec = full_sys_hosts[0] # 获取绑定的Host配置
# 3. 拼接Gem5启动命令(核心!)
cmd = f"{inst.env.repo_base(f'{self._executable}.{self._variant}')} --outdir={inst.env.get_simulator_output_dir(sim=self)} "
cmd += " ".join(self.extra_main_args)
# 3.1 基础配置:Gem5脚本路径、缓存配置、CPU/系统时钟
cmd += (
f" {inst.env.repo_base('sims/external/gem5/configs/simbricks/simbricks.py')} --caches --l2cache "
"--l1d_size=32kB --l1i_size=32kB --l2_size=32MB "
"--l1d_assoc=8 --l1i_assoc=8 --l2_assoc=16 "
f"--cacheline_size=64 --cpu-clock={host_spec.cpu_freq}"
f" --sys-clock={self._sys_clock} "
f"--checkpoint-dir={inst.env.cpdir_sim(sim=self)} "
f"--kernel={inst.env.repo_base('images/vmlinux')} "
)
# 3.2 磁盘镜像:拼接Host配置的所有磁盘
for disk in host_spec.disks:
cmd += f"--disk-image={disk.path(inst=inst, format='raw')} "
# 3.3 CPU/内存配置:对接Host的cores/memory属性
cmd += (
f"--cpu-type={cpu_type} --mem-size={host_spec.memory}MB "
f"--num-cpus={host_spec.cores} "
"--mem-type=DDR4_2400_16x4 "
)
# 3.4 内核命令行:传递Host的kcmd_append(如启动脚本路径)
if host_spec.kcmd_append is not None:
cmd += f'--command-line-append="{host_spec.kcmd_append}" '
# 3.5 检查点配置:创建/恢复检查点
if inst.create_checkpoint:
cmd += "--max-checkpoints=1 "
if inst.restore_checkpoint:
cmd += "-r 1 "
# 3.6 通信参数:PCIe/内存接口的套接字、延迟、同步
latency, sync_period, run_sync = sim_base.Simulator.get_unique_latency_period_sync(channels=self.get_channels())
# PCIe接口(如NIC):拼接CONNECT类型套接字参数
pci_interfaces = system.Interface.filter_by_type(interfaces=host_spec.interfaces(), ty=sys_pcie.PCIeHostInterface)
for inf in pci_interfaces:
socket = inst.get_socket(interface=inf)
if socket:
cmd += (
f"--simbricks-pci=connect:{socket._path}"
f":latency={latency}ns"
f":sync_interval={sync_period}ns"
+ (":sync" if run_sync and not inst.create_checkpoint else "")
+ " "
)
# 内存接口:同理拼接参数
# ... 省略内存接口逻辑 ...
# 3.7 额外配置参数
cmd += " ".join(self.extra_config_args)
return cmd
Dummy
Dummy 英文直译是「假的、虚拟的、占位的」,在编程中,DummyXXX 这类命名的类 / 对象,核心作用是:作为 "占位符" 使用,填补某个需要特定类型对象但暂时无法提供真实实现的场景,避免程序因 "缺少对象" 而崩溃。
简单说:DummySimulator 就是一个「假的模拟器」------ 它继承了 Simulator 抽象基类的结构,但没有实现任何实际的模拟逻辑,仅用于兼容 / 容错场景
DummySimulator 是 Simulator 基类的 "空实现"(也叫「哑实现」):
- 它满足
Simulator的类型要求(能被加入Simulation的_sim_list); - 但所有关键方法(
run_cmd/supported_socket_types)都只抛出异常,不做任何实际操作; - 唯一的额外属性
_is_dummy = True用于标记 "这是一个假模拟器"。
app处理流程
Simulation 是连接 System(静态配置)和 Instantiation(动态执行)的桥梁,它会遍历 System 中的所有 Host,处理其 applications。
- 把你定义的
Simulation(包含 Host/NIC/Switch/Application 等所有配置)绑定到一个可执行的Instantiation实例; - 给
Instantiation设置两个通用默认参数(临时文件自动删除、启用快照),仅此而已。
这个方法仅做「实例化 + 默认参数配置」 ,完全不处理 Application、不执行任何仿真逻辑、不解析任何组件 ------ 它只是创建了一个 "空的执行上下文容器",真正的组件 / 应用处理逻辑在调用 Instantiation 实例的后续方法 (如 prepare()/run())时才触发。
simple_instantiation 返回 Instantiation 实例后,框架对 Application 的操作不会自动触发,而是需要显式调用仿真器的 prepare() 方法 (或框架顶层的 run_simulation() 方法),才会走到 Application 解析逻辑。
这行代码是创建具体的模拟器实例,拆解如下:
-
st:是compmap映射中的「模拟器类型」(如Gem5Sim/I40eNicSim/SwitchNet),本质是一个类(而非实例); -
simulation:是当前正在构建的Simulation核心对象(包含仿真的全局配置); -
st(simulation):调用模拟器类的构造函数,传入simulation作为参数,创建该模拟器的实例对象; -
simulator = ...:将创建好的模拟器实例赋值给变量,后续用于绑定组件。 -
Host 配置层 :
Sleep被加入host1.applications,其run_cmds返回["sleep infinity"]; -
Instantiation 层 :
inst.prepare()调用host1.gen_cmds(),生成包含sleep infinity的脚本,路径写入host1.kcmd_append; -
Gem5Sim 层 :
run_cmd读取host1.kcmd_append,将脚本路径嵌入 Gem5 启动命令的--command-line-append参数; -
执行层 :
Instantiation.execute()调用subprocess执行 Gem5 启动命令; -
Gem5 内部 :加载内核,执行
kcmd_append中的脚本,最终运行sleep infinity。
其他
-> 是 Python 3.5+ 引入的函数返回值类型注解(Type Annotation) 语法,它的作用是:明确告诉开发者 / 编辑器,这个函数预期返回什么类型的值(注意:Python 解释器不会强制校验类型,仅做提示和静态检查用)。
简单说:-> 就是给函数的返回值 "贴标签",让代码更易读、易维护,也能让 IDE(如 VS Code/PyCharm)做类型提示和错误检查。
-> None:函数没有返回值(执行完只做操作,不 return 任何东西);-> list[str]:返回一个由字符串组成的列表;-> inst_socket.SockType:返回一个自定义的枚举类型(SockType)
在 Docker 容器中执行 pip install matplotlib,库会被安装到容器内部的 Python 环境目录(而非宿主机)
# 查看Python库安装路径(执行容器内的python)
python -c "import site; print(site.getsitepackages())"

配置层(simulation.py)→ 实例化层(instantiation.py)→ 执行层(sim_processes.py)
