[Timer] 01.QEMU 定时器概览
QEMU 定时器概览
目录
1.2.Linux 获取时间 - clock_gettime()
2.2.4.定时器链表集合 - QEMUTimerListGroup
3.1.1.创建全局定时器链表集合 - timerlistgroup_init()
3.1.2.创建各个类型的定时器链表 - timerlist_new()
3.2.1.创建定时器 - timer_new_full()
3.2.2.初始化定时器将其添加至链表 - timer_init_full()
1.基本概念
-
频率与时间的关系如下:
-
晶振频率为 20MHz,则 20*1000*1000 个晶振脉冲为 1s;
-
-
不同单位的时间换算方式如下:
-
1s(秒)= 1000ms(毫秒)
-
1ms(毫秒)= 1000μs(微秒)
-
1μs(微秒)= 1000ns(纳秒)
-
1ns(纳秒)= 1000ps(皮秒)
-
1s = 1*1000*1000*1000ns
-
1.1.Linux 的时钟信息 - timespec
Linux 保存时钟信息的结构体为 timespec,保存秒和纳秒:
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
1.2.Linux 获取时间 - clock_gettime()
通过 clock_gettime() 获取系统的时钟信息:
int clock_gettime(clockid_t clk_id, struct timespec *tp);
传入参数的含义如下:
-
clk_id 指定时钟类型;
-
tp 指向 timespec 结构体,用于存储获取的时间值;
clock_gettime() 支持多种时钟类型,每种类型都有不同的用途和特性:
-
CLOCK_REALTIME:系统实时时间,从 Epoch(1970-01-01 00:00:00 UTC)开始计时,可以更改;
-
CLOCK_MONOTONIC:系统运行时间,从系统启动时开始计时,不受系统时间调整的影响;
-
CLOCK_PROCESS_CPUTIME_ID:当前进程的 CPU 时间;
-
CLOCK_THREAD_CPUTIME_ID:当前线程的 CPU 时间;
-
CLOCK_MONOTONIC_RAW:类似于 CLOCK_MONOTONIC,但不受 NTP 调整的影响;
-
CLOCK_REALTIME_COARSE:CLOCK_REALTIME 的低精度版本,速度更快;
-
CLOCK_MONOTONIC_COARSE:CLOCK_MONOTONIC 的低精度版本,速度更快;
-
CLOCK_BOOTTIME:类似于 CLOCK_MONOTONIC,但包括系统挂起的时间;
-
CLOCK_TAI:国际原子时,不受闰秒影响;
2.QEMU 定时器
2.1.时钟类型
-
QEMU_CLOCK_REALTIME:实时时钟,即使虚拟机停止也会继续运行,不受宿主机时钟变化影响;
-
QEMU_CLOCK_VIRTUAL:虚拟机时钟,仅在虚拟机运行时运行,虚拟机停止时停止;
-
QEMU_CLOCK_HOST:实时时钟,受宿主机时钟影响(使用 gettimeofday() 接口),虚拟机停止也继续运行,受 NTP 网络时钟、宿主机时钟修改等因素影响
-
QEMU_CLOCK_VIRTUAL_RT:虚拟机时钟,icount 模式使用纳秒计数;
// include/qemu/timer.h
typedef enum {
QEMU_CLOCK_REALTIME = 0,
QEMU_CLOCK_VIRTUAL = 1,
QEMU_CLOCK_HOST = 2,
QEMU_CLOCK_VIRTUAL_RT = 3,
QEMU_CLOCK_MAX
} QEMUClockType;
2.2.基本数据类型
2.2.1.定时器链表节点 - QEMUTimer
QEMU 定时器节点按链表形式组织:

每个链表节点表示一个定时器:
// include/qemu/timer.h
struct QEMUTimer {
int64_t expire_time; /* in nanoseconds */
QEMUTimerList *timer_list;
QEMUTimerCB *cb; // 回调函数,到达expire_time后触发
void *opaque;
QEMUTimer *next;
int attributes;
int scale;
};
-
expire_time 为定时器的超时时间(定时时长),单位为纳秒;
-
*cb 为定时器的超时回调函数,当定时器运行时间达到 expire_time 时触发定时器超时,并调用该回调函数;
2.2.2.链表头节点 - QEMUClock
定时器链表头结点定义时钟类型:RealTimer、Virtual、Host、Virtual_RT
QEMUClock 使用 QLIST_HEAD() 宏定义链表头结点,timerlists 保存了该类型定时器链表的头结点
// util/qemu-timer.c
typedef struct QEMUClock {
QLIST_HEAD(, QEMUTimerList) timerlists; // 定时器列表头结点
QEMUClockType type; // 时钟类型
bool enabled;
} QEMUClock;
定义 qemu_clocks 数组保存链表头结点,即每种类型的时钟都有专门的链表,而不是将各种类型的时钟放在一起
// util/qemu-timer.c
static QEMUClock qemu_clocks[QEMU_CLOCK_MAX];
2.2.3.定时器链表 - QEMUTimerList
每种类型的定时器均有一个专门的链表,以 REALTIME 类型的定时器链表为例:

// util/qemu-timer.c
struct QEMUTimerList {
QEMUClock *clock;
QemuMutex active_timers_lock;
QEMUTimer *active_timers;
QLIST_ENTRY(QEMUTimerList) list;
QEMUTimerListNotifyCB *notify_cb;
void *notify_opaque;
/* lightweight method to mark the end of timerlist's running */
QemuEvent timers_done_ev;
};
2.2.4.定时器链表集合 - QEMUTimerListGroup
保存四种类型的定时器链表,main_loop_tlg 为全局变量

// include/qemu/timer.h
struct QEMUTimerListGroup {
QEMUTimerList *tl[QEMU_CLOCK_MAX];
};
-------------------------------------------
// include/qemu/timer.h
extern QEMUTimerListGroup main_loop_tlg;
3.初始化流程
QEMU 定时器创建分为三个部分:
-
创建定时器链表;
-
创建对应类型的定时器;
-
设置超时时间;
3.1.创建定时器链表
3.1.1.创建全局定时器链表集合 - timerlistgroup_init()
定时器链表集合 tlg 是 AioContext 的成员之一:
// include/block/aio.h
struct AioContext {
GSource source;
...
/* TimerLists for calling timers - one per clock type. Has its own
* locking.
*/
QEMUTimerListGroup tlg;
虚拟机启动时执行 AIO 初始化,创建 AIO Context 时创建定时器链表集合 main_loop_tlg:
|--> aio_context_new()
|--> ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
|--> timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
-------------------------------------------------------------------------
// util/qemu-timer.c
void timerlistgroup_init(QEMUTimerListGroup *tlg,
QEMUTimerListNotifyCB *cb, void *opaque)
{
QEMUClockType type;
for (type = 0; type < QEMU_CLOCK_MAX; type++) {
tlg->tl[type] = timerlist_new(type, cb, opaque);
}
}
3.1.2.创建各个类型的定时器链表 - timerlist_new()
timerlist_new() 按照给定的类型,创建对应的定时器链表
// util/qemu-timer.c
QEMUTimerList *timerlist_new(QEMUClockType type,
QEMUTimerListNotifyCB *cb,
void *opaque)
{
QEMUTimerList *timer_list;
QEMUClock *clock = qemu_clock_ptr(type);
timer_list = g_new0(QEMUTimerList, 1);
qemu_event_init(&timer_list->timers_done_ev, true);
timer_list->clock = clock;
timer_list->notify_cb = cb;
timer_list->notify_opaque = opaque;
qemu_mutex_init(&timer_list->active_timers_lock);
QLIST_INSERT_HEAD(&clock->timerlists, timer_list, list);
return timer_list;
}
3.2.创建定时器 - timer_new()
timer_new() 函数接受 4 个参数:
-
type:时钟类型,QEMU_CLOCK_REALTIME、QEMU_CLOCK_VIRTUAL、...;
-
scale:时间单位,纳秒 SCALE_NS、微秒 SCALE_US,毫秒 SCALE_MS;
-
*cb:回调函数;
-
*opaque:回调函数所使用的参数;
// include/qemu/timer.h
static inline QEMUTimer *timer_new(QEMUClockType type, int scale,
QEMUTimerCB *cb, void *opaque)
{
return timer_new_full(NULL, type, scale, 0, cb, opaque);
}
除此以外,还可以使用时间单位对应的接口创建定时器,这些接口均调用 timer_new()
// include/qemu/timer.h
static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb,
void *opaque)
{
return timer_new(type, SCALE_NS, cb, opaque);
}
-------------------------------------------------------------------------
// include/qemu/timer.h
static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb,
void *opaque)
{
return timer_new(type, SCALE_US, cb, opaque);
}
-------------------------------------------------------------------------
// include/qemu/timer.h
static inline QEMUTimer *timer_new_ms(QEMUClockType type, QEMUTimerCB *cb,
void *opaque)
{
return timer_new(type, SCALE_MS, cb, opaque);
}
3.2.1.创建定时器 - timer_new_full()
timer_new() 调用 timer_new_full() 完成定时器创建,并在创建后立即进行初始化:
// include/qemu/timer.h
static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group,
QEMUClockType type,
int scale, int attributes,
QEMUTimerCB *cb, void *opaque)
{
QEMUTimer *ts = g_new0(QEMUTimer, 1);
timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque);
return ts;
}
3.2.2.初始化定时器将其添加至链表 - timer_init_full()
timer_init_full() 将该定时器添加至对应类型的链表,并设置该定时器的回调函数、定时精度等信息:
// util/qemu-timer.c
void timer_init_full(QEMUTimer *ts,
QEMUTimerListGroup *timer_list_group, QEMUClockType type,
int scale, int attributes,
QEMUTimerCB *cb, void *opaque)
{
if (!timer_list_group) {
timer_list_group = &main_loop_tlg;
}
ts->timer_list = timer_list_group->tl[type];
ts->cb = cb;
ts->opaque = opaque;
ts->scale = scale;
ts->attributes = attributes;
ts->expire_time = -1;
}
3.3.设置超时时间 - timer_mod()
定时器创建后,调用 timer_mod() 设置超时时长,需要注意的是,在传入超时时长 expire_time 时,需要加上当前的系统时间,即 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time
// util/qemu-timer.c
void timer_mod(QEMUTimer *ts, int64_t expire_time)
{
timer_mod_ns(ts, expire_time * ts->scale);
}
----------------------------------------------------------
// util/qemu-timer.c
void timer_mod_ns(QEMUTimer *ts, int64_t expire_time)
{
QEMUTimerList *timer_list = ts->timer_list;
bool rearm;
qemu_mutex_lock(&timer_list->active_timers_lock);
timer_del_locked(timer_list, ts);
rearm = timer_mod_ns_locked(timer_list, ts, expire_time);
qemu_mutex_unlock(&timer_list->active_timers_lock);
if (rearm) {
timerlist_rearm(timer_list);
}
}
----------------------------------------------------------
// util/qemu-timer.c
static bool timer_mod_ns_locked(QEMUTimerList *timer_list,
QEMUTimer *ts, int64_t expire_time)
{
QEMUTimer **pt, *t;
/* add the timer in the sorted list */
pt = &timer_list->active_timers;
for (;;) {
t = *pt;
if (!timer_expired_ns(t, expire_time)) {
break;
}
pt = &t->next;
}
ts->expire_time = MAX(expire_time, 0);
ts->next = *pt;
qatomic_set(pt, ts);
return pt == &timer_list->active_timers;
}
4.获取时间 - qemu_clock_get_ns()
该接口最终调用 Linux 获取时钟的接口 clock_gettime(),时间单位为纳秒 ns
// util/qemu-timer.c
int64_t qemu_clock_get_ns(QEMUClockType type)
{
switch (type) {
case QEMU_CLOCK_REALTIME:
return get_clock();
default:
case QEMU_CLOCK_VIRTUAL:
return cpus_get_virtual_clock();
case QEMU_CLOCK_HOST:
return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime());
case QEMU_CLOCK_VIRTUAL_RT:
return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock());
}
}
----------------------------------------------------------
// system/cpus.c
int64_t cpus_get_virtual_clock(void)
{
if (cpus_accel && cpus_accel->get_virtual_clock) {
return cpus_accel->get_virtual_clock();
}
return cpu_get_clock();
}
----------------------------------------------------------
// system/cpu-timers.c
int64_t cpu_get_clock(void)
{
int64_t ti;
unsigned start;
do {
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
ti = cpu_get_clock_locked();
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
return ti;
}
----------------------------------------------------------
// system/cpu-timers.c
int64_t cpu_get_clock_locked(void)
{
int64_t time;
time = timers_state.cpu_clock_offset;
if (timers_state.cpu_ticks_enabled) {
time += get_clock();
}
return time;
}
----------------------------------------------------------
// include/qemu/timer.h
static inline int64_t get_clock(void)
{
if (use_rt_clock) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); // Linux 获取时间的接口
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
} else {
return get_clock_realtime();
}
}
更多推荐



所有评论(0)