半虚拟化中的CPU调度优化策略

半虚拟化中的CPU调度优化策略

关键词:半虚拟化、CPU调度、性能优化、虚拟机监控器、虚拟化技术、调度算法、资源分配

摘要:本文将深入探讨半虚拟化环境中的CPU调度优化策略。我们将从基础概念出发,逐步分析半虚拟化的特点及其对CPU调度的特殊要求,详细介绍各种优化策略的原理和实现方式,并通过实际案例展示这些策略如何提升虚拟化环境的性能。文章最后还将展望该领域的未来发展趋势和技术挑战。

背景介绍

目的和范围

本文旨在全面解析半虚拟化环境中的CPU调度优化技术,帮助读者理解虚拟化环境下的资源分配机制和性能优化方法。内容涵盖从基础概念到高级优化策略的全方位知识。

预期读者

本文适合对虚拟化技术有一定了解的云计算工程师、系统架构师、性能优化专家以及对虚拟化底层机制感兴趣的技术爱好者。

文档结构概述

文章首先介绍半虚拟化的基本概念,然后深入分析CPU调度面临的挑战,接着详细讲解各种优化策略,最后通过实际案例和未来展望结束。

术语表

核心术语定义
  • 半虚拟化(Paravirtualization):一种虚拟化技术,通过修改客户操作系统内核,使其"知道"自己运行在虚拟环境中,从而与虚拟机监控器(VMM)协同工作,提高性能。
  • 虚拟机监控器(VMM/Hypervisor):创建和运行虚拟机的软件、固件或硬件,负责管理和分配物理资源给各个虚拟机。
  • 调度域(Scheduling Domain):一组具有相似调度特性的CPU核心,调度器可以基于这些域做出更优化的决策。
相关概念解释
  • 完全虚拟化(Full Virtualization):不需要修改客户操作系统就能运行的虚拟化技术,通常依赖硬件辅助虚拟化。
  • 准虚拟化驱动(Paravirtualized Drivers):专门为虚拟化环境优化的设备驱动程序,通过减少模拟开销提高I/O性能。
缩略词列表
  • VMM: Virtual Machine Monitor (虚拟机监控器)
  • VM: Virtual Machine (虚拟机)
  • QoS: Quality of Service (服务质量)
  • NUMA: Non-Uniform Memory Access (非统一内存访问)
  • SMP: Symmetric Multi-Processing (对称多处理)

核心概念与联系

故事引入

想象你是一个学校的校长,负责安排不同班级的学生使用有限的计算机实验室。有些班级需要运行复杂的科学计算程序,有些则只需要基本的文字处理。如何公平高效地分配这些计算机资源,确保每个班级都能按时完成作业,同时不让任何计算机闲置太久?这就是CPU调度器在半虚拟化环境中面临的挑战。

核心概念解释

核心概念一:什么是半虚拟化?

半虚拟化就像让租客(虚拟机)知道他们住在合租公寓(物理主机)里,而不是独栋别墅。通过这种"知情"状态,租客们可以更好地协调资源使用,比如协商洗澡时间,而不是每个人都假装自己有独立浴室。这比完全虚拟化(每个租客都假装拥有整栋房子)效率更高,但需要租客(客户操作系统)配合修改行为。

核心概念二:CPU调度在半虚拟化中的特殊性

在半虚拟化环境中,调度器不仅要管理物理CPU核心,还要协调多个虚拟机对CPU资源的"感知"。就像学校里的老师需要知道哪些学生是"虚拟班级"的(参加在线课程),哪些是实体班级的,才能合理安排教室和资源。

核心概念三:调度优化的关键目标

调度优化的主要目标就像交通管制:1) 减少拥堵(降低延迟);2) 提高道路利用率(增加吞吐量);3) 确保紧急车辆优先(满足服务质量);4) 避免某些路段完全闲置(负载均衡)。

核心概念之间的关系

半虚拟化和CPU调度的关系

半虚拟化为CPU调度提供了更多优化机会,因为客户操作系统"知道"自己运行在虚拟环境中,可以提供更多信息给调度器,就像租客告诉房东他们确切的需求,而不是让房东猜测。

调度策略和性能的关系

不同的调度策略就像不同的交通规则:有的像环岛(公平但可能有等待),有的像红绿灯(严格分时),有的像智能交通系统(动态调整)。选择正确的策略对虚拟化环境性能至关重要。

资源分配和QoS的关系

好的调度策略能在保证基本服务质量(QoS)的前提下灵活分配资源,就像医院既要保证急诊优先,又要合理安排常规门诊,还要充分利用医疗资源。

核心概念原理和架构的文本示意图

+-------------------+       +-------------------+       +-------------------+
|  客户操作系统     |       |  虚拟机监控器     |       |  物理硬件         |
|  (修改过的内核)   |<----->|  (VMM/Hypervisor) |<----->|  (CPU/内存/IO等)  |
+-------------------+       +-------------------+       +-------------------+
        ^                                                        ^
        |                                                        |
        |                 调度优化策略                           |
        +--------------------------------------------------------+

Mermaid 流程图

高优先级任务
普通任务
客户操作系统请求CPU
VMM调度决策
立即分配CPU
加入运行队列
根据调度策略选择下一个任务
执行任务
任务完成或时间片用完
保存状态并释放CPU

核心算法原理 & 具体操作步骤

半虚拟化环境中的CPU调度算法需要考虑虚拟化层的特殊需求。下面我们分析几种主要的优化策略及其实现原理。

1. 信用调度算法(Credit Scheduler)

信用调度是Xen半虚拟化中常用的算法,它给每个VM分配"信用值",调度基于这些信用值进行决策。

# 简化的信用调度算法Python实现
class VCPU:
    def __init__(self, vm_id, priority):
        self.vm_id = vm_id
        self.credits = 0
        self.priority = priority  # 可以是'high', 'normal', 'low'
        self.state = 'idle'  # 'idle', 'running', 'blocked'

class CreditScheduler:
    def __init__(self, num_cores):
        self.run_queues = {i: [] for i in range(num_cores)}  # 每个核心一个运行队列
        self.credit_balance = {}  # VM ID -> 剩余信用
        self.default_credit = 100  # 默认分配的信用值
        
    def add_vcpu(self, vcpu):
        if vcpu.vm_id not in self.credit_balance:
            self.credit_balance[vcpu.vm_id] = self.default_credit
        # 简单的初始分配策略:轮询分配到最空闲的核心
        least_loaded_core = min(self.run_queues, key=lambda k: len(self.run_queues[k]))
        self.run_queues[least_loaded_core].append(vcpu)
        
    def schedule(self):
        for core, queue in self.run_queues.items():
            if not queue:
                continue
                
            # 按优先级和信用值排序
            queue.sort(key=lambda v: (-v.priority, -self.credit_balance[v.vm_id]))
            
            # 选择第一个可运行的VCPU
            for i, vcpu in enumerate(queue):
                if vcpu.state == 'idle':
                    # 消耗信用
                    self.credit_balance[vcpu.vm_id] -= 1
                    vcpu.state = 'running'
                    # 将选中的VCPU移到队列前端
                    queue.pop(i)
                    queue.insert(0, vcpu)
                    break
                    
    def recharge_credits(self):
        # 定期补充信用值
        for vm_id in self.credit_balance:
            self.credit_balance[vm_id] = self.default_credit

2. 完全公平调度器(CFS)的虚拟化优化

Linux的CFS算法也可以适应半虚拟化环境,通过调整虚拟运行时间(vruntime)的计算方式:

// 简化的虚拟化感知CFS调度器修改 (Linux内核风格)
struct sched_entity {
    u64     vruntime;           // 虚拟运行时间
    u64     exec_start;         // 开始执行时间
    u64     sum_exec_runtime;   // 总执行时间
    // ... 其他字段
};

static void update_curr_virtualized(struct cfs_rq *cfs_rq)
{
    struct sched_entity *curr = cfs_rq->curr;
    u64 now = rq_clock_task(rq_of(cfs_rq));
    u64 delta_exec;
    
    if (!curr)
        return;
    
    delta_exec = now - curr->exec_start;
    if (unlikely((s64)delta_exec <= 0))
        return;
    
    curr->exec_start = now;
    
    // 关键修改:根据VM优先级调整vruntime增长速率
    if (task_is_virtualized(curr)) {
        struct vm_task *vtask = task_vm_struct(curr);
        delta_exec = adjust_for_vm_priority(delta_exec, vtask->priority);
    }
    
    curr->sum_exec_runtime += delta_exec;
    curr->vruntime += calc_delta_fair(delta_exec, curr);
}

static u64 adjust_for_vm_priority(u64 delta, int vm_priority)
{
    // 高优先级VM任务的实际执行时间计算为较少,使其vruntime增长更慢
    // 从而在CFS的公平选择机制中获得更多CPU时间
    switch (vm_priority) {
        case VM_PRIO_HIGH:
            return delta * 7 / 10;  // 只计70%的实际时间
        case VM_PRIO_NORMAL:
            return delta;
        case VM_PRIO_LOW:
            return delta * 12 / 10; // 计120%的实际时间
        default:
            return delta;
    }
}

3. 负载感知调度(Load-Aware Scheduling)

负载感知调度通过监控各VM的实际负载动态调整分配策略:

// Go语言实现的简化负载感知调度器
package main

import (
	"container/heap"
	"time"
)

type VMStats struct {
	VMID           string
	CPUDemand      float64 // 最近CPU需求 (0-1)
	MemoryPressure float64 // 内存压力指标
	IOWait         float64 // IO等待时间比例
	LastUpdated    time.Time
}

type LoadAwareScheduler struct {
	physicalCores int
	vmStats       map[string]*VMStats
	scoreFunc     func(*VMStats) float64
}

// NewLoadAwareScheduler 创建新的负载感知调度器
func NewLoadAwareScheduler(cores int) *LoadAwareScheduler {
	return &LoadAwareScheduler{
		physicalCores: cores,
		vmStats:       make(map[string]*VMStats),
		scoreFunc:     defaultScoreFunction,
	}
}

// defaultScoreFunction 计算VM的综合负载评分
func defaultScoreFunction(stats *VMStats) float64 {
	// 综合CPU需求、内存压力和IO等待计算负载评分
	cpuWeight := 0.6
	memWeight := 0.3
	ioWeight := 0.1
	
	// 简单的线性加权计算
	score := cpuWeight*stats.CPUDemand + 
	         memWeight*stats.MemoryPressure + 
	         ioWeight*stats.IOWait
	
	// 考虑数据新鲜度,超过5秒的数据降权
	if time.Since(stats.LastUpdated) > 5*time.Second {
		score *= 0.7
	}
	
	return score
}

// UpdateStats 更新VM的负载统计信息
func (s *LoadAwareScheduler) UpdateStats(vmID string, stats *VMStats) {
	stats.LastUpdated = time.Now()
	s.vmStats[vmID] = stats
}

// Schedule 执行调度决策,返回应该分配到各核心的VM列表
func (s *LoadAwareScheduler) Schedule() map[int][]string {
	// 按负载评分排序VM
	type vmScore struct {
		vmID  string
		score float64
	}
	
	var vmScores []vmScore
	for vmID, stats := range s.vmStats {
		vmScores = append(vmScores, vmScore{
			vmID:  vmID,
			score: s.scoreFunc(stats),
		})
	}
	
	// 按评分从高到低排序
	sort.Slice(vmScores, func(i, j int) bool {
		return vmScores[i].score > vmScores[j].score
	})
	
	// 使用最小堆来平衡各核心的负载
	coreHeap := &CoreHeap{}
	heap.Init(coreHeap)
	for i := 0; i < s.physicalCores; i++ {
		heap.Push(coreHeap, &CoreLoad{
			CoreID:    i,
			TotalLoad: 0,
			VMs:       []string{},
		})
	}
	
	// 将高负载VM分配到当前最空闲的核心
	for _, vs := range vmScores {
		leastLoadedCore := heap.Pop(coreHeap).(*CoreLoad)
		leastLoadedCore.VMs = append(leastLoadedCore.VMs, vs.vmID)
		leastLoadedCore.TotalLoad += vs.score
		heap.Push(coreHeap, leastLoadedCore)
	}
	
	// 构建返回结果
	result := make(map[int][]string)
	for coreHeap.Len() > 0 {
		core := heap.Pop(coreHeap).(*CoreLoad)
		result[core.CoreID] = core.VMs
	}
	
	return result
}

// CoreLoad 表示核心的负载情况
type CoreLoad struct {
	CoreID    int
	TotalLoad float64
	VMs       []string
}

// CoreHeap 是实现heap.Interface的最小堆
type CoreHeap []*CoreLoad

func (h CoreHeap) Len() int           { return len(h) }
func (h CoreHeap) Less(i, j int) bool { return h[i].TotalLoad < h[j].TotalLoad }
func (h CoreHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *CoreHeap) Push(x interface{}) {
	*h = append(*h, x.(*CoreLoad))
}

func (h *CoreHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

数学模型和公式 & 详细讲解

1. 信用调度算法的数学模型

信用调度可以建模为一个动态资源分配问题。设系统有N个物理CPU核心,M个虚拟机,每个VM i有一个权重w_i表示其优先级。

信用分配公式:
C i ( t + 1 ) = C i ( t ) + w i × Δ T − u i ( t ) C_i(t+1) = C_i(t) + w_i \times \Delta T - u_i(t) Ci(t+1)=Ci(t)+wi×ΔTui(t)

其中:

  • C i ( t ) C_i(t) Ci(t) 是VM i在时间t的信用余额
  • Δ T \Delta T ΔT 是信用补充周期
  • u i ( t ) u_i(t) ui(t) 是VM i在周期t内实际使用的CPU时间

调度决策选择信用余额最高的可运行VCPU:
next VCPU = arg max ⁡ j ∈ runqueue ( C j ( t ) ) \text{next VCPU} = \argmax_{j \in \text{runqueue}} (C_j(t)) next VCPU=jrunqueueargmax(Cj(t))

2. 负载均衡的熵模型

我们可以使用熵的概念来量化系统负载均衡程度。定义系统在时间t的熵为:

H ( t ) = − ∑ k = 1 N p k ( t ) log ⁡ p k ( t ) H(t) = -\sum_{k=1}^{N} p_k(t) \log p_k(t) H(t)=k=1Npk(t)logpk(t)

其中 p k ( t ) p_k(t) pk(t)是第k个核心的负载占总负载的比例:

p k ( t ) = L k ( t ) ∑ i = 1 N L i ( t ) p_k(t) = \frac{L_k(t)}{\sum_{i=1}^{N} L_i(t)} pk(t)=i=1NLi(t)Lk(t)

其中 L k ( t ) L_k(t) Lk(t)是核心k在时间t的负载。熵值越大,表示负载分布越均匀。调度算法的目标可以表述为最大化H(t)。

3. 响应时间预测模型

对于实时性要求高的VM,我们需要预测任务响应时间。设VM i的任务到达率为 λ i \lambda_i λi,服务率为 μ i \mu_i μi,在信用调度下的响应时间可以近似为:

R i ≈ 1 μ i − λ i + C max − C i 2 × w i R_i \approx \frac{1}{\mu_i - \lambda_i} + \frac{C_{\text{max}} - C_i}{2 \times w_i} Riμiλi1+2×wiCmaxCi

其中 C max C_{\text{max}} Cmax是系统中最大的信用余额。这个模型表明响应时间由两部分组成:常规队列等待时间和信用调度引入的额外延迟。

项目实战:代码实际案例和详细解释说明

开发环境搭建

我们将基于QEMU/KVM和libvirt实现一个半虚拟化环境的调度优化实验。环境搭建步骤:

  1. 安装Ubuntu Server 22.04 LTS
  2. 安装虚拟化组件:
    sudo apt update
    sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
    sudo systemctl enable --now libvirtd
    
  3. 验证安装:
    kvm-ok  # 应显示"KVM acceleration can be used"
    virsh list --all  # 应显示空列表或已有虚拟机
    

源代码详细实现和代码解读

我们将修改libvirt的调度器插件来实现一个自定义的信用调度策略:

/* 自定义信用调度器插件 - libvirt调度器接口实现 */

#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <libvirt/internal.h>
#include <unistd.h>
#include <math.h>

#define DEFAULT_CREDIT 1000
#define RECHARGE_INTERVAL 10000  // 10秒(毫秒)

typedef struct _virCustomSchedulerPrivate virCustomSchedulerPrivate;
struct _virCustomSchedulerPrivate {
    virMutex lock;
    GHashTable *vmCredits;  // VM UUID -> 信用结构
    int timerID;
};

typedef struct {
    char *vmUUID;
    int credits;
    int weight;
    time_t lastRecharge;
} VMCreditInfo;

/* 初始化调度器 */
static int
virCustomSchedulerInit(virSchedulerPtr scheduler)
{
    virCustomSchedulerPrivate *priv;
    
    if (VIR_ALLOC(priv) < 0)
        return -1;
    
    if (virMutexInit(&priv->lock) < 0) {
        VIR_FREE(priv);
        return -1;
    }
    
    priv->vmCredits = virHashCreate(10, NULL);
    if (!priv->vmCredits) {
        virMutexDestroy(&priv->lock);
        VIR_FREE(priv);
        return -1;
    }
    
    scheduler->privateData = priv;
    
    // 设置定时器定期补充信用
    priv->timerID = virEventAddTimeout(RECHARGE_INTERVAL,
                                      virCustomSchedulerRechargeTimer,
                                      scheduler, NULL);
    return 0;
}

/* 补充信用定时器回调 */
static void
virCustomSchedulerRechargeTimer(int timerID, void *opaque)
{
    virSchedulerPtr scheduler = opaque;
    virCustomSchedulerPrivate *priv = scheduler->privateData;
    GHashTableIter iter;
    gpointer key, value;
    
    virMutexLock(&priv->lock);
    
    g_hash_table_iter_init(&iter, priv->vmCredits);
    while (g_hash_table_iter_next(&iter, &key, &value)) {
        VMCreditInfo *info = value;
        int recharge = info->weight * DEFAULT_CREDIT / 100;
        info->credits += recharge;
        info->lastRecharge = time(NULL);
    }
    
    virMutexUnlock(&priv->lock);
    
    // 重新设置定时器
    virEventUpdateTimeout(priv->timerID, RECHARGE_INTERVAL);
}

/* 分配CPU资源的决策函数 */
static int
virCustomSchedulerAllocateResources(virSchedulerPtr scheduler,
                                   virDomainDefPtr def,
                                   virNodeInfoPtr nodeinfo,
                                   unsigned int ncpus,
                                   unsigned int *vcpus)
{
    virCustomSchedulerPrivate *priv = scheduler->privateData;
    VMCreditInfo *info;
    int maxCredit = -1;
    int bestCPU = 0;
    
    virMutexLock(&priv->lock);
    
    // 查找VM的信用信息
    info = g_hash_table_lookup(priv->vmCredits, def->uuid);
    if (!info) {
        // 新VM,初始化信用
        info = g_new0(VMCreditInfo, 1);
        info->vmUUID = g_strdup(def->uuid);
        info->credits = DEFAULT_CREDIT;
        info->weight = def->sched.weight;  // 从域定义获取权重
        info->lastRecharge = time(NULL);
        g_hash_table_insert(priv->vmCredits, info->vmUUID, info);
    }
    
    // 选择信用最高的CPU
    for (int i = 0; i < ncpus; i++) {
        if (vcpus[i] > maxCredit) {
            maxCredit = vcpus[i];
            bestCPU = i;
        }
    }
    
    // 消耗信用
    info->credits -= 1;
    
    virMutexUnlock(&priv->lock);
    
    return bestCPU;
}

/* 其他必要接口实现... */

/* 注册调度器 */
virSchedulerDriver customSchedulerDriver = {
    .name = "CUSTOM",
    .init = virCustomSchedulerInit,
    .allocateResources = virCustomSchedulerAllocateResources,
    /* 其他函数指针... */
};

代码解读与分析

这个自定义调度器插件实现了以下关键功能:

  1. 信用管理:为每个VM维护信用账户,定期补充信用值
  2. 权重分配:根据VM配置的权重比例分配信用
  3. 调度决策:选择信用最高的CPU分配给请求的VM
  4. 线程安全:使用互斥锁保护共享数据结构

关键设计点:

  • 信用补充采用周期性定时器,避免频繁操作
  • 新VM自动获得默认信用值
  • 调度决策简单高效,适合实时调度
  • 与libvirt核心解耦,通过标准接口交互

实际应用场景

场景一:云计算平台的多租户资源分配

在OpenStack等云平台中,半虚拟化CPU调度优化可以:

  1. 保证高优先级租户(如付费更高的客户)获得更稳定的性能
  2. 防止"吵闹的邻居"问题(一个VM占用过多CPU影响其他VM)
  3. 实现更精细的SLA(服务等级协议)保障

场景二:实时应用虚拟化

对延迟敏感的实时应用(如金融交易系统)需要:

  1. 保证最坏情况下的响应时间界限
  2. 减少调度抖动(vCPU被抢占导致的延迟)
  3. 提供CPU预留(固定分配)和限额机制

场景三:高密度服务器整合

在整合多个工作负载到少量物理服务器时:

  1. 通过负载感知调度提高整体吞吐量
  2. 利用NUMA感知调度减少跨节点内存访问
  3. 动态调整分配应对工作负载变化

工具和资源推荐

性能分析工具

  1. perf:Linux性能分析工具,可分析调度事件
    perf stat -e 'sched:*' -a sleep 10
    
  2. turbostat:监控CPU频率和C状态
  3. virsh top:libvirt提供的虚拟机资源监控

调优工具

  1. taskset:手动设置CPU亲和性
  2. chrt:调整进程调度策略和优先级
  3. virsh vcpuinfo/vcpupin:管理虚拟CPU分配

学习资源

  1. 《Systems Performance: Enterprise and the Cloud》 Brendan Gregg
  2. Xen官方文档中的调度器章节
  3. Linux内核文档中的sched/目录

未来发展趋势与挑战

发展趋势

  1. 机器学习驱动的调度:使用AI模型预测工作负载并优化调度
  2. 异构计算调度:协调CPU、GPU、FPGA等不同计算单元
  3. 边缘计算场景优化:为低延迟、高波动的边缘环境设计新算法

技术挑战

  1. 安全隔离:防止通过调度器侧信道攻击
  2. 能效与性能平衡:满足绿色计算要求
  3. 超大规模扩展:支持数万个VM的调度决策

总结:学到了什么?

核心概念回顾

  1. 半虚拟化:通过修改客户操作系统提高性能的虚拟化技术
  2. CPU调度优化:在半虚拟化环境中公平高效分配CPU资源的方法
  3. 信用调度:基于信用值的分配策略,兼顾公平和优先级

概念关系回顾

  1. 半虚拟化通过客户机协作使调度优化更有效
  2. 不同调度算法适用于不同场景(公平性vs实时性)
  3. 负载感知和NUMA感知可以显著提升复杂环境性能

思考题:动动小脑筋

思考题一:

在一个有8个物理核心的服务器上运行10个VM,其中3个运行数据库(高优先级),5个运行应用服务器(中优先级),2个运行批处理作业(低优先级)。你会如何设计调度策略?

思考题二:

假设你要为自动驾驶模拟平台设计虚拟化环境,需要同时满足高计算需求的物理模拟和低延迟的传感器数据处理,CPU调度需要考虑哪些特殊因素?

附录:常见问题与解答

Q1: 半虚拟化和完全虚拟化在调度上有何本质区别?

A1: 半虚拟化中客户操作系统知道被虚拟化,可以与调度器协作提供更多信息(如真实负载、优先级),而完全虚拟化中调度器只能被动观察VM行为。这使得半虚拟化调度可以做出更优决策。

Q2: 为什么信用调度适合多租户云环境?

A2: 信用调度通过权重机制自然支持差异化服务等级(SLA),且信用消耗/补充的机制直观易懂,便于向租户解释资源分配原则,也便于实现资源配额和限制。

Q3: 如何监控和诊断CPU调度问题?

A3: 关键指标包括:各VM的CPU就绪时间(ready%)、调度延迟、信用余额变化。工具如xl sched-credit(Xen)、virsh vcpuinfo(KVM)可查看调度状态,perf可分析调度事件。

扩展阅读 & 参考资料

  1. Xen Credit Scheduler Deep Dive - https://wiki.xenproject.org/wiki/Credit_Scheduler
  2. Linux Kernel Scheduling - https://docs.kernel.org/scheduler/
  3. VMware CPU Scheduler White Paper - https://www.vmware.com/resources/techresources/
  4. “Scheduling in Virtualized Environments” - IEEE Transactions on Parallel and Distributed Systems
  5. KVM Forum Presentations on Scheduling - https://www.linux-kvm.org/page/KVM_Forum
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值