Skip to content

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"
  }
}

下一步