main.rs 入口与 CLI 解析
概述
main.rs 是 Cube Hypervisor 的入口文件,负责命令行参数解析、日志初始化、VMM 实例创建和信号处理。本文深入分析其设计和实现。
文件结构
rust
// src/main.rs (1832 行)
mod common; // 通用工具
// 依赖导入
use clap::{Arg, ArgAction, ArgGroup, Command};
use vmm::config::{VmParams, RestoreConfig};
use vmm::vm_config::{VmConfig, ...};
// 错误类型
enum Error {
CreateApiEventFd,
CreateHypervisor,
StartVmmThread,
ParsingConfig,
// ...
}
// 主函数
fn main() { ... }
// CLI 定义
fn create_app() -> Command { ... }
// 配置解析
fn parse_config(matches: &ArgMatches) -> VmmConfig { ... }
// VMM 启动
fn start_vmm(config: VmmConfig) -> Result<()> { ... }
// 信号处理
fn handle_signals() { ... }CLI 参数定义
命令结构
rust
// src/main.rs
fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command {
Command::new("cube-hypervisor")
.version(env!("BUILT_VERSION"))
.about("Launch a cloud-hypervisor VMM.")
// VM 配置组
.group(ArgGroup::new("vm-config").multiple(true))
// VMM 配置组
.group(ArgGroup::new("vmm-config").multiple(true))
// 日志配置组
.group(ArgGroup::new("logging").multiple(true))
// CPU 配置
.arg(Arg::new("cpus")
.long("cpus")
.help("boot=<boot_vcpus>,max=<max_vcpus>,topology=...")
.default_value(default_vcpus)
.group("vm-config"))
// 内存配置
.arg(Arg::new("memory")
.long("memory")
.help("size=<guest_memory_size>,mergeable=on|off,shared=on|off,...")
.default_value(default_memory)
.group("vm-config"))
// 内核路径
.arg(Arg::new("kernel")
.long("kernel")
.help("Path to kernel to load")
.num_args(1)
.group("vm-config"))
// 磁盘配置
.arg(Arg::new("disk")
.long("disk")
.help(DiskConfig::SYNTAX)
.num_args(1..)
.group("vm-config"))
// 网络配置
.arg(Arg::new("net")
.long("net")
.help(NetConfig::SYNTAX)
.num_args(1..)
.group("vm-config"))
// ... 更多参数
}参数分组
参数解析示例
bash
# 基本启动
cube-hypervisor \
--kernel /path/to/vmlinux \
--disk path=/path/to/rootfs.ext4 \
--cpus boot=2 \
--memory size=512M \
--net tap=tap0,mac=00:11:22:33:44:55
# 高级配置
cube-hypervisor \
--kernel /path/to/vmlinux \
--disk path=/path/to/rootfs.ext4,io_uring=true \
--cpus boot=4,max=8,topology=2:2:1:1 \
--memory size=1G,hugepages=on,shared=on \
--net tap=tap0,mac=00:11:22:33:44:55,num_queues=4 \
--fs tag=shared,socket=/tmp/virtiofs.sock \
--api-socket /tmp/ch-api.sock \
--seccomp true配置解析
VmParams 解析
rust
// vmm/src/config.rs
pub struct VmParams<'a> {
pub cpus: &'a str,
pub memory: &'a str,
pub memory_zones: Option<Vec<&'a str>>,
pub firmware: Option<&'a str>,
pub kernel: Option<&'a str>,
pub initramfs: Option<&'a str>,
pub cmdline: Option<&'a str>,
pub disks: Option<Vec<&'a str>>,
pub net: Option<Vec<&'a str>>,
pub rng: &'a str,
pub balloon: Option<&'a str>,
pub fs: Option<Vec<&'a str>>,
pub pmem: Option<Vec<&'a str>>,
pub serial: &'a str,
pub console: &'a str,
pub devices: Option<Vec<&'a str>>,
pub user_devices: Option<Vec<&'a str>>,
pub vdpa: Option<Vec<&'a str>>,
pub vsock: Option<&'a str>,
pub numa: Option<Vec<&'a str>>,
// ...
}
impl<'a> VmParams<'a> {
pub fn parse(matches: &'a ArgMatches) -> Self {
Self {
cpus: matches.get_one::<String>("cpus").unwrap(),
memory: matches.get_one::<String>("memory").unwrap(),
kernel: matches.get_one::<String>("kernel").map(|s| s.as_str()),
// ...
}
}
}VmConfig 构建
rust
// vmm/src/config.rs
impl VmConfig {
pub fn parse(vm_params: &VmParams) -> Result<Self> {
let cpus = CpusConfig::parse(vm_params.cpus)?;
let memory = MemoryConfig::parse(vm_params.memory)?;
let kernel = vm_params.kernel.map(|k| PayloadConfig {
kernel: Some(PathBuf::from(k)),
// ...
});
let disks = vm_params.disks.as_ref()
.map(|disks| {
disks.iter()
.map(|d| DiskConfig::parse(d))
.collect::<Result<Vec<_>>>()
})
.transpose()?;
// ... 解析其他配置
Ok(Self {
cpus,
memory,
kernel,
disks,
net,
// ...
})
}
}日志初始化
日志配置
rust
// src/main.rs
fn setup_logger(matches: &ArgMatches) {
let log_file = matches.get_one::<String>("log-file")
.cloned()
.unwrap_or_else(|| DEFAULT_LOG_FILE.to_string());
let log_level = matches.get_one::<String>("log-level")
.map(|s| s.parse::<LevelFilter>().unwrap())
.unwrap_or(LevelFilter::Info);
let log_stderr = matches.get_flag("log-stderr");
// 创建 logger
let logger = Logger::new(
Mutex::new(Some(Box::new(File::create(&log_file)?))),
Instant::now(),
sandbox_id,
true, // log_async
false, // defer_logger_thread
// ...
);
// 设置全局 logger
slog_scope::set_global_logger(logger);
}异步日志
rust
// src/common.rs
impl Logger {
fn log_async_flush(&self, buf: &[u8]) {
let mut output = self.output.lock().unwrap();
if output.is_none() {
*output = match std::fs::File::options()
.create(true)
.append(true)
.open(std::path::Path::new(&self.log_file_name))
{
Ok(file) => Some(Box::new(file)),
Err(_) => Some(Box::new(std::io::stderr())),
};
}
if output.is_some() {
(*(output.as_mut().unwrap())).write(buf).ok();
}
}
fn build_logger_thread(&self) {
// 启动异步日志线程
thread::spawn(move || {
loop {
// 从缓冲区读取日志
// 写入文件
}
});
}
}VMM 启动
VmmInstance 创建
rust
// src/lib.rs
pub struct VmmInstance {
vmm_thread: Option<JoinHandle<Result<(), VmmError>>>,
}
impl VmmInstance {
pub fn new(vmm_config: VmmConfig) -> Self {
Self {
vmm_thread: None,
}
}
pub fn start(&mut self) -> Result<()> {
// 1. 创建 API event fd
let api_evt = EventFd::new(EFD_NONBLOCK)?;
// 2. 创建 API 通道
let (api_sender, api_receiver) = channel();
// 3. 创建 hypervisor
let hypervisor = hypervisor::new()?;
// 4. 启动 VMM 线程
let vmm_thread = thread::spawn(move || {
let mut vmm = Vmm::new(
hypervisor,
api_evt,
api_receiver,
// ...
);
// 控制循环
vmm.control_loop()
});
self.vmm_thread = Some(vmm_thread);
Ok(())
}
}VMM 控制循环
信号处理
信号注册
rust
// src/main.rs
fn handle_signals() {
// 注册信号处理器
let signals = Signals::new(&[SIGINT, SIGTERM])?;
// 处理信号
for signal in signals.forever() {
match signal {
SIGINT | SIGTERM => {
// 优雅关闭
vmm.shutdown();
break;
}
_ => {}
}
}
}seccomp 过滤
rust
// src/main.rs
fn setup_seccomp(matches: &ArgMatches) {
let seccomp = matches.get_one::<String>("seccomp")
.map(|s| s.parse::<SeccompAction>().unwrap())
.unwrap_or(SeccompAction::KillProcess);
// 获取过滤器
let filter = get_seccomp_filter(Thread::Vmm)?;
// 应用过滤器
apply_filter(&filter)?;
}错误处理
错误类型
rust
// src/main.rs
#[derive(Error, Debug)]
enum Error {
#[error("Failed to create API EventFd: {0}")]
CreateApiEventFd(#[source] std::io::Error),
#[error("Failed to create Hypervisor: {0}")]
CreateHypervisor(#[source] hypervisor::HypervisorError),
#[error("Failed to start VMM thread: {0}")]
StartVmmThread(#[source] vmm::Error),
#[error("Error parsing config: {0}")]
ParsingConfig(vmm::config::Error),
// ...
}错误处理流程
完整启动流程
配置示例
最小配置
json
{
"kernel": "/path/to/vmlinux",
"disks": [
{ "path": "/path/to/rootfs.ext4" }
],
"cpus": {
"boot_vcpus": 1
},
"memory": {
"size": 268435456
}
}完整配置
json
{
"kernel": "/path/to/vmlinux",
"cmdline": "console=ttyS0 root=/dev/vda1",
"disks": [
{
"path": "/path/to/rootfs.ext4",
"readonly": false,
"direct": true,
"io_uring": true
}
],
"net": [
{
"tap": "tap0",
"ip": "192.168.1.1",
"mac": "00:11:22:33:44:55",
"num_queues": 2,
"queue_size": 256
}
],
"cpus": {
"boot_vcpus": 2,
"max_vcpus": 4,
"topology": {
"threads_per_core": 2,
"cores_per_die": 1,
"dies_per_package": 1,
"packages": 1
}
},
"memory": {
"size": 536870912,
"shared": false,
"hugepages": false
},
"serial": {
"mode": "null"
},
"console": {
"mode": "tty"
},
"rng": {
"src": "/dev/urandom"
}
}