文章目录

  • 基本原理
  • 数据结构
    • QEMUTimer
    • qemu_work_item
  • 实现流程
    • 定时器注册
    • 启动cpu throttle
    • vcpu睡眠
    • 停止cpu throttle

基本原理

  • cpu throttle主要目的是限制虚机vcpu的运行,降低虚机的脏页速率。在热迁移长时间无法完成的情况下,可以使用这个手段降低脏页速率,从而促使迁移收敛。
  • cpu throttle核心思想非常简单:让处于运行状态的vcpu退出到kvm,然后返回用户态qemu,睡眠一段时间后再进入guest。cpu throttle的输入参数是vcpu睡眠时间与总时间的百分比。公式如下:
  • 这里我们将sleep_time+run_time称为一个throttle周期,qemu在限制vcpu运行时,保证了vcpu在一个throttle周期内的运行时间固定,因此,不同百分比(throttle_percentage)会导致throttle周期不同,throttle_percentage越大,vcpu睡眠的时间越多,throttle周期越长。
  • 下面我们以throttle周期内运行时间10ms为例(这是qemu的默认实现),介绍不同迁移过程中设置不同cpu throttle时qemu的工作流程:
  1. throttle percentage: 50%、run_time: 10ms、sleep_time:10ms、throttle period: 20ms
  • 下图描述的是cpu throttle设置为50的情况下每个vcpu的运行情况,由于每个throttle周期内run_time相同,都是10ms,根据比例计算得到throttle周期为20ms。因此qemu主线程中设置定时器每20ms触发一次回调,回调函数中会kick vcpu。vcpu被kick后退回到用户态,休眠10ms后会重新进入guest态运行,10ms后再次被kick,周而复始。
  1. throttle percentage: 60%、run_time: 10ms、sleep_time:15ms、throttle period: 25ms

数据结构

QEMUTimer

  • QEMUTimer用来实现定时,周期性触发回调函数,kick处于运行态的vcpu:
struct QEMUTimer {int64_t expire_time;        /* 超时时间,单位为ns */QEMUTimerList *timer_list;QEMUTimerCB *cb;      /* 定时器关联的回调函数 */void *opaque;QEMUTimer *next;int attributes;int scale;
};

qemu_work_item

  • qemu_work_item用来实现vcpu运行任务的封装,所有需要vcpu线程在用户态执行的任务会被链表链接起来,放在CPUState结构体的work_list成员中,一旦vcpu退回到用户态,vcpu线程会遍历该链表的所有任务并依次执行
struct qemu_work_item {QSIMPLEQ_ENTRY(qemu_work_item) node;      /* 需要vcpu线程在用户态执行的任务通过该成员链接到work_list链表 */run_on_cpu_func func;                     /* 任务执行时调用的具体函数 */run_on_cpu_data data;                     /* 任务执行时调用的具体函数参数 */bool free, exclusive, done;
};
struct CPUState {......QSIMPLEQ_HEAD(, qemu_work_item) work_list;   /* vcpu上所有待执行的任务有work_list维护 */......
}

#define CPU_THROTTLE_PCT_MIN 1                   /* 允许cpu睡眠的最小占比 */
#define CPU_THROTTLE_PCT_MAX 99                 /* 允许cpu睡眠的最大占比 */
#define CPU_THROTTLE_TIMESLICE_NS 10000000      /* 每个throttle周期固定运行10ms */

实现流程

定时器注册

  • cpu throttle通过定时器周期性kick vcpu实现,需要注册qemu定时器,这个动作包含在qemu主线程启动过程中,流程如下:
qemu_initcpu_timers_initcpu_throttle_init/* 注册throttle timer* 超时单位:ns* timer回调:cpu_throttle_timer_tick* 超时时间:无* 超时时间类型:虚拟机运行时间* */throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, cpu_throttle_timer_tick, NULL);

启动cpu throttle

  • 迁移线程通过调用cpu_throttle_set函数触发cpu throttle的定时器,参数为vcpu睡眠时间与throttle周期的百分比,qemu定义全局静态变量throttle_percentage来控制throttle的运行,throttle_percentage大于0时触发cpu throttle,等于0时停止cpu throttle,流程如下:
void cpu_throttle_set(int new_throttle_pct)
{/** boolean to store whether throttle is already active or not,* before modifying throttle_percentage* 取出全局变量throttle_percentage判断throttle是否启动*/bool throttle_active = cpu_throttle_active();/* 确保睡眠百分比在1-99范围内 *//* Ensure throttle percentage is within valid range */new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);qatomic_set(&throttle_percentage, new_throttle_pct);/* 如果throttle没有启动,触发 */if (!throttle_active) {cpu_throttle_timer_tick(NULL);}
}
  • cpu_throttle_set对输入进行了检查并查看throttle是否启动,如果没有启动则触发,启动cpu throttle的真正工作在cpu_throttle_timer_tick中完成:
static void cpu_throttle_timer_tick(void *opaque)
{CPUState *cpu;double pct;/* Stop the timer if needed */if (!cpu_throttle_get_percentage()) {       /* 检查throttle是否有必要停止 */return;}CPU_FOREACH(cpu) {                           /* 遍历虚机每个vcpu */if (!qatomic_xchg(&cpu->throttle_thread_scheduled, 1)) { /* 如果vcpu启动throttle任务 */async_run_on_cpu(cpu, cpu_throttle_thread,              /* 将cpu_throttle_thread任务添加到vcpu的work_list链表中 */RUN_ON_CPU_NULL);}}/* 根据睡眠百分比计算throttle周期,将其设置为throttle timer的超时时间 * 从这里可以看出,throttle timer的超时时间与睡眠百分比有关**/pct = (double)cpu_throttle_get_percentage() / 100;timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +CPU_THROTTLE_TIMESLICE_NS / (1 - pct));
}
  • cpu_throttle_thread函数封装成任务并挂到vcpu的work_list链表的工作由async_run_on_cpu完成,继续分析:
void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
{struct qemu_work_item *wi;/* 将cpu_throttle_thread封装成qemu_work_item */wi = g_malloc0(sizeof(struct qemu_work_item));wi->func = func;wi->data = data;wi->free = true;/* 挂入work_list链表 */queue_work_on_cpu(cpu, wi);
}
  • 完成work挂入work_list链表的操作后,queue_work_on_cpu最后会kick vcpu,让vcpu从guest态返回到用户态,如下:
static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
{qemu_mutex_lock(&cpu->work_mutex);QSIMPLEQ_INSERT_TAIL(&cpu->work_list, wi, node);   /* 任务添加到链表 */wi->done = false;qemu_mutex_unlock(&cpu->work_mutex);/* 使vcpu从guest态退出到用户态,处理work_list上的任务 */qemu_cpu_kick(cpu);
}

vcpu睡眠

  • 在分析vcpu如何执行睡眠任务前,我们先追溯一下vcpu线程工作流程:
x86_cpu_realizefnqemu_init_vcpucpus_accel->create_vcpu_thread(cpu)    <=>  kvm_start_vcpu_threadqemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn, cpu, QEMU_THREAD_JOINABLE);
  • qemu主线在实例化vcpu时会创建vcpu线程kvm_vcpu_thread_fn,此后vcpu线程会一直运行直到虚机被销毁,vcpu线程工作流程如下:
static void *kvm_vcpu_thread_fn(void *arg)
{......do {if (cpu_can_run(cpu)) {/* 下发KVM_RUN命令字让vcpu进入guest态运行* 用户态vcpu线程睡眠 * 迁移线程kick vcpu线程时,内核发送IPI核间中断到vcpu所在物理核* 硬件会让vcpu从guest态退出,返回到用户态* 然后用户态vcpu线程被唤醒,kvm_cpu_exec函数返回* */kvm_cpu_exec(cpu);......}/* 用户态vcpu线程被唤醒后,调用qemu_wait_io_event处理相关工作* 其中就包括执行work_list上的任务* */qemu_wait_io_event(cpu);} while (!cpu->unplug || cpu_can_run(cpu));......
}
  • 从上面分析可知,qemu_wait_io_event是vcpu处理work_list上任务的入口,继续分析:
qemu_wait_io_eventqemu_wait_io_event_commonprocess_queued_cpu_workwi->func(cpu, wi->data)  <=>  cpu_throttle_thread
  • process_queued_cpu_work函数会遍历work_list上的work并逐一执行work注册的任务,包括实现cpu throttle注册的cpu_throttle_thread任务,我们分析cpu_throttle_thread函数的具体实现:
static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
{double pct;double throttle_ratio;int64_t sleeptime_ns, endtime_ns;/* 取出全局变量throttle_percentage判断是否继续睡眠 */if (!cpu_throttle_get_percentage()) {return;}/* 根据睡眠百分比计算得到线程要睡眠的时间 */pct = (double)cpu_throttle_get_percentage() / 100;throttle_ratio = pct / (1 - pct);/* Add 1ns to fix double's rounding error (like 0.9999999...) *//* 算出线程要睡眠的时间 */sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1);endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns;/* 如果线程睡眠时间大于0并且cpu没有终止运行,进入睡眠 */while (sleeptime_ns > 0 && !cpu->stop) {if (sleeptime_ns > SCALE_MS) {/* 如果睡眠时间大于1ms,我们将以ns为单位的sleeptime_ns转化为ms * 让线程在halt_cond信号量上休眠等待sleeptime_ns,休眠过程中如果* 信号量halt_cond由信号发来,线程会被唤醒* */qemu_cond_timedwait_iothread(cpu->halt_cond,sleeptime_ns / SCALE_MS);} else {/* 如果睡眠的时间小于1ms,直接睡眠 */qemu_mutex_unlock_iothread();g_usleep(sleeptime_ns / SCALE_US);qemu_mutex_lock_iothread();}/* 更新睡眠时间 */sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME);}/* 完成睡眠,设置throttle_thread_scheduled为0表示vcpu的throttle线程终止 */qatomic_set(&cpu->throttle_thread_scheduled, 0);
}
  • 这里有个疑问,为什么睡眠时间大于1ms和小于1ms要分开处理,个人猜测是为了处理cpu终止的情况,由于睡眠时间大于1ms,时间较长,这个过程中有可能qemu主线程想让vcpu线程终止,qemu主线程会设置cpu->stop为true,并在cpu->halt_cond广播,此时throttle线程睡在cpu->halt_cond上就会被唤醒,然后更新睡眠时间,如果检查到睡眠时间还是大于0但cpu->stop为true,那么throttle线程就不会再进入循环继续睡眠而直接终止。从而达到终止throttle线程的目的。对于睡眠时间小于1ms,qemu可能认为时间较短影响很小。

停止cpu throttle

  • 停止cpu throttle通过cpu_throttle_stop完成,非常简单,直接设置全局变量throttle_percentage为0,定时器的回调函数和throttle线程都会检查这个变量,一旦为0,就会直接返回
void cpu_throttle_stop(void)
{qatomic_set(&throttle_percentage, 0);
}

cpu throttle原理浅析相关推荐

  1. 计算机风冷散热系统的原理,CPU散热原理及散热方式浅析

    摘要:分析了三种常见热传递的基本原理及影响传热效果的因素,在此基础上,剖析了CPU常见四种散热方式的工作原理.用到的热传递技术及不同散热方式的特点,并结合散热原理给出了CPU散热系统日常维护的方法:最 ...

  2. redis单线程原理___Redis为何那么快-----底层原理浅析

    redis单线程原理 redis单线程问题 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程. 1. 为什么说redis能够快速执行 ...

  3. GPU加速原理浅析及代码实现

    GPU加速原理浅析及代码实现 一.CUDA简介 二.GPU架构特点 三.CUDA线程模型 四.CUDA内存模型 五.CUDA编程规范 **第一个要掌握的编程要点**:**我们怎么写一个能在GPU跑的程 ...

  4. linux软中断是什么机制,Linux软中断原理浅析

    Linux软中断原理浅析 Linux软中断原理浅析 Linux中的软中断机制用于中对时间要求最严格以及最重要的中断下半部进行使用.在系统设计过 程中,大家都清楚中断上下文不能处理太多的事情,需要快速的 ...

  5. Chapter 2.PHP8.1 新特性fiber及原理浅析

    欢迎来到「我是真的狗杂谈世界」,关注不迷路 前言 很早就听说PHP8.1出了Fiber(又称纤程),但一直也没时间捣鼓它, 正好前段时间在整理PHP的新特性/功能,想看看有没有什么可以给日常开发带来便 ...

  6. Word2vec原理浅析及gensim中word2vec使用

    本文转载于以下博客链接:Word2vec原理浅析:https://blog.csdn.net/u010700066/article/details/83070102: gensim中word2vec使 ...

  7. CPU Cache原理与示例

    CPU Cache原理与示例 基础知识 现在的 CPU 多核技术,都会有几级缓存,老的 CPU 会有两级内存(L1 和 L2),新的CPU会有三级内存(L1,L2,L3 ),如下图所示: 其中:  ...

  8. Python标准库queue模块原理浅析

    Python标准库queue模块原理浅析 本文环境python3.5.2 queue模块的实现思路 作为一个线程安全的队列模块,该模块提供了线程安全的一个队列,该队列底层的实现基于Python线程th ...

  9. Python标准库threading模块Condition原理浅析

    Python标准库threading模块Condition原理浅析 本文环境python3.5.2 threading模块Condition的实现思路 在Python的多线程实现过程中,在Linux平 ...

最新文章

  1. 糟糕的css用法 1
  2. 无监督分类:聚类分析(K均值)
  3. matlab 万年历,matlab时钟万年历 matlab时钟万年历(附word说明文档): 程序主要 联合开发网 - pudn.com...
  4. python3.7怎么安装turtle_python3绘图程序教学:载入和查询Turtle模组(一)
  5. iPad有望带来全面鼠标支持 大幅提升游戏体验
  6. python中while true的用法_解析Python中while true的使用
  7. matlab打包多个m文件,MATLAB GUI多个m文件和fig如何生成exe文件
  8. 生产者与消费者 代码实现 java
  9. c语言系统主函数流程图,c语言流程图【调解方式】
  10. endnotex8使用教程_EndNote X8使用教程
  11. 油猴(Tampermonkey)使用教程
  12. 全自动抠图换背景软件下载_智能抠图换背景软件-手挥自动背景更换软件
  13. matlab直流电机pid调速仿真,直流电机双闭环PID调速系统仿真设计
  14. 万恶之源-python加深
  15. 常用软件的 Linux 版本
  16. 叶史瓦大学计算机科学,本地知名新西兰留学咨询平台排名
  17. 安卓短信转发qq邮箱
  18. 路由器把服务器的地址修改,路由器修改服务器地址
  19. Tiled结合Unity做地图——Tiled2Unity
  20. Druid.io index_realtime实时任务源码分析

热门文章

  1. 怎么改图片大小格式?如何更改图片的尺寸?
  2. 写一个计算器,要求实现加减乘除功能
  3. html样式错位,如何DIV+CSS网页错位_DIV+CSS_网页错位_CSS教程_课课家
  4. 通达OA v11.9 getdata任意命令执行漏洞复现+利用
  5. R语言分析蛋白质组学数据:飞行时间质谱(MALDI-TOF)法、峰值检测、多光谱比较...
  6. 显示硬件发展与视频开发系列(6)----显示标注与视频处理单元(5):TPU
  7. [计算机网络安全实验] DNS攻击实验
  8. 重度游戏热度居高不下,中轻度游戏如何突出重围?
  9. 全新PHP程序开发在线工单管理系统源码,售后工单系统
  10. 智云通CRM:如何判断客户忠诚度的高低?