KVM虚拟化技术-实战与原理解析
Posted Arnold Lu@南京
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KVM虚拟化技术-实战与原理解析相关的知识,希望对你有一定的参考价值。
Linux下操作系统编程有两本经典APUE即《Advanced Programming in the UNIX Environment》和TLPI《The Linux Programming Interface》,中文版对应《UNIX环境高级编程(第3版)》和《Linux/UNIX系统编程》。
TLPI洋洋洒洒英文版1506页,中文版1176页;一共64章节,明显是作为工具书使用。通过目录可以了解本书的结构,以及作者的组织形式。
- 背景知识及概念:共3章分别介绍了UNIX、C、Linux历史;Linux和UNIX基本概念;Linux和UNIX系统编程基本概念。
- 系统编程接口基本特性:共9章分别介绍了文件I/O、进程、内存分配、用户和组、进程凭证、时间、系统限制和选项、获取系统和进程信息。
- 系统编程接口高级特性:共11章分别介绍了文件I/O缓冲、文件系统、文件属性、扩展属性、访问控制列表、目录和链接、监控文件时间、信号、定时器。
- 进程、程序及线程:共10章分别介绍了进程创建、终止,监控子进程,执行程序;POSIX线程。
- 进程及程序高级主题:共9章分别介绍了进程组、会话及任务控制;进程优先级和进程调度;进程资源;守护进程;编写安全的特权程序;能力;登陆记账;共享库。
- 进程间通信:共13章分别介绍了IPC概览;管道和FIFO;System V IPC消息队列、信号量及共享内存;内存映射;虚拟内存操作;POSIX消息队列、信号量及共享内存;文件锁定。
- 套接字和网络编程:共6章。
- 高级I/O主题:共3章分别介绍了中断、其他I/O模型、伪终端。
关于本书的示例有两个版本Distribution version和Book version,在这里可以找到他们的详细信息;为了便于使用将其放到https://github.com/arnoldlu/tlpi。
第10章 时间
1. 日历时间
常用的获取时间的函数time和gettimeofday两种:
#include <sys/time.h> int gettimeofday(struct timeval *tv, struct timezone *tz); Returns 0 on success, or –1 on error struct timeval { __kernel_time_t tv_sec; /* seconds */ __kernel_suseconds_t tv_usec; /* microseconds */ };
#include <time.h> time_t time(time_t *timep); Returns number of seconds since the Epoch,or (time_t) –1 on error
time函数基于gettimeofday实现,time精度秒级,gettimeofday可以达到微妙。
2. 时间转换函数
下图是从内核获取时间的两种方式(time和gettimeofday),以及基于此进行的时间转换(timeval、time_t、tm)。
time对应的设置时间函数是stime,gettimeofday对应的是gettimeofday。
time_t和timeval关系,time_t是timeval的秒部分对齐,四舍五入。
time_t到tm转换:gmtime()、localtime()。
tm到time_t转换:mktime()。
time_t到文本:ctime()。
tm到文本:asctime()、strftime()。
文本到tm:strptime()。
3. 时区
/etc/localtime
4. 地区
5. 更新系统时钟
6. 软件时钟
7. 进程时间
#include <sys/times.h> clock_t times(struct tms *buf); Returns number of clock ticks (sysconf(_SC_CLK_TCK)) since “arbitrary” time in past on success, or (clock_t) –1 on error
8. 总结
第23章 定时器与休眠
1.间隔定时器setitimer和alarm
两个重要的结构体:定时器参数struct itimerval和表示时间struct timerval。
struct itimerval { struct timeval { |
setitimer和alarm原型:
#include <sys/time.h> #include <unistd.h> |
setitimer可以指定三种不同类型定时器:ITIMER_REAL(SIGALARM)、ITIMER_VIRTUAL(SIGVTALRM)、ITIMER_PROF(SIGPROF)。
- 进程可以拥有三种定时器,但是每种只能设置一个。
- 只能通过发送信号的方式来通知定时器到期,也不能改变到期信号时产生的信号。
- alarm()和setitimer()针对同一进程共享同一实时定时器。
- 三种定时器的参考时间不同,ITIMER_REAL(真实时间)、ITIMER_VIRTUAL(进程虚拟时间,用户CPU时间)、ITIMER_PROF(进程的用户和内核时间总和)。
- 如果这些信号不设置处理函数,则默认会终止进程。
- 如果一个间隔式定时器到期多次,且相应信号遭到阻塞时,那么只会调用一次信号处理函数。
- 分辨率微秒级。
Notes:简单讲alarm基于setitimer,setitimer有三种形式。
两者的系统调用如下:
asmlinkage long sys_getitimer(int which, struct itimerval __user *value); asmlinkage long sys_setitimer(int which, struct itimerval __user *value, struct itimerval __user *ovalue); asmlinkage long sys_alarm(unsigned int seconds);
对应的原型在:
kernel/itimer.c SYSCALL_DEFINE3(setitimer, int, which, struct itimerval __user *, value, struct itimerval __user *, ovalue) SYSCALL_DEFINE2(getitimer, int, which, struct itimerval __user *, value) kernel/timer.c SYSCALL_DEFINE1(alarm, unsigned int, seconds)
从代码看alarm和setitimer之间的关系:
unsigned int alarm_setitimer(unsigned int seconds) { struct itimerval it_new, it_old; #if BITS_PER_LONG < 64 if (seconds > INT_MAX) seconds = INT_MAX; #endif it_new.it_value.tv_sec = seconds;----------------------------alarm精度是秒级,setitimer是微妙级别。 it_new.it_value.tv_usec = 0; it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; do_setitimer(ITIMER_REAL, &it_new, &it_old);-----------------从这里可以看出alarm是setitimer的ITIMER_REAL形式,所以两者不能混用。容易造成混乱。 /* * We can\'t return 0 if we have an alarm pending ... And we\'d * better return too much than too little anyway */ if ((!it_old.it_value.tv_sec && it_old.it_value.tv_usec) || it_old.it_value.tv_usec >= 500000) it_old.it_value.tv_sec++; return it_old.it_value.tv_sec; }
do_setitimer/do_getitimer都对ITIMER_REAL、ITIMER_VIRTUAL、ITIMER_PROF分别进行了处理。这三个定时器都是内嵌在struct task_struct中:
ITIMER_REAL:借用task_struct->signal->real_timer,超时函数式it_real_fn。创建以真实时间倒计时的定时器,到期发送SIGALRM给进程。
ITIMER_VIRTUAL:task_struct->signal->it[CPUCLOCK_VIRT]。创建以进程虚拟时间(用户模式下CPU时间)倒计时的定时器,到期会产生信号SIGVTALRM。
ITIMER_PROF:task_struct->signal->it[CPUCLOCK_PROF]。创建一个profiling定时器,以进程时间(用户+内核CPU时间总和)倒计时。
setitimer和alarm异同?
- alarm是setitimer的ITIMER_REAL形式
- alarm精度为秒,setitimer为微妙级(但受受限于软件时钟的频率)
- alarm只提供一次性实时定时器,setitimer可以提供周期性定时器。
alarm基于setitimer,那么setitimer又是基于什么呢?
ITIMER_REAL基于hrtimer,ITIMER_VIRTUAL和ITIMER_PROF基于POSIX CPU timer。这里重点看一下hrtimer相关内容。
task_struct->signal->real_timer是由谁创建的?do_fork-->copy_process-->copy_signal-->hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL),超时函数是it_real_fn。
注意这里hrtimer的两个参数时钟类型是CLOCK_MONOTONIC单调次增,防跳变;timer模式是HRTIMER_MODE_REL是一个相对时间。
enum hrtimer_restart it_real_fn(struct hrtimer *timer) { struct signal_struct *sig = container_of(timer, struct signal_struct, real_timer); trace_itimer_expire(ITIMER_REAL, sig->leader_pid, 0); kill_pid_info(SIGALRM, SEND_SIG_PRIV, sig->leader_pid);--------发送SIFALRM给调用进程。 return HRTIMER_NORESTART; }
测试验证
- 不同which差异?结果:同时触发,这三者占用的时间ITIMER_REAL< ITIMER_PROF <ITIMER_VIRTUAL,可见后两者受进程调度影响较大。
- alarm和setitimer冲突问题?结果:alarm和ITIMER_REAL存在冲突,两者是基于同样的setitimer。
- setitimer定时器只能存在一个?结果:可以三个,同一类型只能一个。
2.定时器的调度及精度
- 系统可能会在定时器到期的瞬间之后才去调度其所属进程,这取决于当前负载和对进程的调度。
- setitimer()定时器虽然可能延迟一定时间,但是后续的定时器仍然按照固定间隔。比如2秒定时器,2.3超时,下一个应该在4.3秒超时。
- 虽然setitimer精度达到微妙,但是受制于软件时钟频率。比如jiffy为4ms,间隔为19100微妙,实际间隔是20ms。
- 高分辨率定时器需要内核CONFIG_HIGH_RES_TIMERS。
3.为阻塞操作设置超时
4.休眠一段时间
低分辨率休眠sleep()
#include <unistd.h> |
高分辨率休眠nanosleep()
#define _POSIX_C_SOURCE 199309 request指定了休眠持续时间,理论上精度可以达到纳秒级,但受制于软件时钟间隔。如果间隔并非间隔值,者向上取整。 nanosleep()不基于信号实现,但是可以通过型号处理函数来中断,如SIGINT(Ctrl-C)。 |
Notes:
sleep、nanosleep、clock_nanosleep区别?
- sleep睡眠秒级,nanosleep理论上可以到达纳秒级别(但受制于软件时钟间隔大小,会向上取整)
- 部分系统sleep基于alram/setitimer来实现,nanosleep则不会使用信号实现函数
- clock_nanosleep设置flags,TIMER_ABSTIME。
- clock_nanosleep不同时钟来测量休眠间隔clockid。
- nanosleep(HRTIMER_MODE_REL、CLOCK_MONOTONIC),clock_nanosleep(HRTIMER_MODE_ABS/ HRTIMER_MODE_REL、CLOCK_REALTIME/CLOCK_MONOTONIC/),TIMER_ABSTIME避免进程嗜睡问题。
如下sleep、usleep、nanosleep、clock_nanosleep代码可以看出区别:
libc/unistd/sleep.c unsigned int sleep (unsigned int seconds) { struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = 0 }; sigset_t set; struct sigaction oact; unsigned int result; ... /* Run nanosleep, with SIGCHLD blocked if SIGCHLD is SIG_IGNed. */ result = nanosleep (&ts, &ts);----------------------------------------基于nanosleep if (result != 0) { /* Got EINTR. Return remaining time. */ result = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L); } ... } unsigned int sleep (unsigned int seconds) { struct sigaction act, oact; sigset_t set, oset; unsigned int result, remaining; time_t before, after; int old_errno = errno; ... remaining = alarm(seconds);--------------------------------------------在没有定义REALTIME相关内容情况下,使用alarm实现。 ... return result > seconds ? 0 : seconds - result; } libc/unistd/usleep.c #if defined __UCLIBC_HAS_REALTIME__ int usleep (__useconds_t usec) { const struct timespec ts = { .tv_sec = (long int) (usec / 1000000), .tv_nsec = (long int) (usec % 1000000) * 1000ul }; return nanosleep(&ts, NULL);-----------------------------------根据REALTIME是否定义,是则基于nanosleep;否则基于select实现。 } #else /* __UCLIBC_HAS_REALTIME__ */ int usleep (__useconds_t usec) { struct timeval tv; tv.tv_sec = (long int) (usec / 1000000); tv.tv_usec = (long int) (usec % 1000000); return select(0, NULL, NULL, NULL, &tv); } #endif /* __UCLIBC_HAS_REALTIME__ */ kernel/hrtimer.c SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, struct timespec __user *, rmtp) { struct timespec tu; if (copy_from_user(&tu, rqtp, sizeof(tu))) return -EFAULT; if (!timespec_valid(&tu)) return -EINVAL; return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);---基于hrtimer_nanosleep的CLOCK_MONOTONIC时钟,相对时间。 } kernel/posix-timer.c SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, const struct timespec __user *, rqtp, struct timespec __user *, rmtp) { struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec t; if (!kc) return -EINVAL; if (!kc->nsleep) return -ENANOSLEEP_NOTSUP; if (copy_from_user(&t, rqtp, sizeof (struct timespec))) return -EFAULT; if (!timespec_valid(&t)) return -EINVAL; return kc->nsleep(which_clock, flags, &t, rmtp);------------------------除了POSIX CPU Timer和AlarmTImer外,也是基于hrtimer_nanosleep,但是多了which_clock的选择。 }
测试验证
- nanosleep能达到的精度?是多少?受制于hrtimer?用户空间很难精确测量精度,系统调用、进程调度都会影响到实际定时时间段。
- sleep是否基于nanosleep?根据是否定义REALTIME功能来定。
通过strace查看sleep和nanosleep可以看出,sleep基于nanosleep实现。
5.POSIX时钟
POSIX时钟API必须以-lrt选项进行编译,从而与librt函数库链接,主要系统调用包括获取当前值的clock_gettime()、返回时钟分辨率的clock_getres()、以及更新时钟的clock_settime()。
要测定特定进程或线程所消耗的CPU时间,可以借助clock_getcpuclockid/pthread_getcpuclockid来获取时钟ID,接着再以此返回ID去调用clock_gettime(),从而获得进程或线程耗费的CPU时间。pid为0是,clock_getcpuclockid()返回调用进程的CPU时间时钟ID。
#define _POSIX_C_SOURCE 199309 int clock_settime(clockid_t clockid, const struct timespec *tp); int clock_getcpuclockid(pid_t pid, clockid_t *clockid); int pthread_getcpuclockid(pthread_t thread, clockid_t *clockid); int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request, struct timespec *remain); |
CLOCK_REALTIME时钟是一种系统级时钟,用于度量真实时间。
CLOCK_MONOTONIC系统启动后就不会发生改变,适用于那些无法容忍系统时钟发生跳跃性变化的应用。Linux上这种时钟对事件的测量食欲系统启动。
CLOCK_PROCESS_CPUTIME_ID时钟测量调用进程所消耗的用户和系统CPU时间。
CLOCK_THREAD_CPUTIME_ID时钟用于测量进程中单条线程的用户和系统CPU时间。
Linux特有的clock_nanosleep()系统调用也可以暂停调用进程,知道经理一段指定时间,亦或是收到信号才恢复运行。
int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request, struct timespec *remain); |
默认情况下(flags为0),request指定休眠间隔是相对时间;如果flags设定TIMER_ABSTIME,request则表示clockid时钟所测量的绝对时间。
相对时间嗜睡问题:如果只是先获取当前时间,计算与目标时间差距,再以相对时间进行休眠,进程可能执行到一半就奔抢占了,结果实际休眠时间回避预期要久。如果被信号处理函数中断并使用循环重启休眠,则“嗜睡”问题尤其明显。如果信号频率很高,则按相对时间休眠的进程则会有较大时间误差。
如何避免嗜睡:先调用clock_gettime()获取时间,加上期望休眠的时间量,再以TIMER_ABSTIME标识调用clock_nanosleep()函数。指定TIMER_ABSTIME,不再使用参数remain。如果信号中断clock_nanosleep()调用,再次调用该函数来重启休眠时,request参数不变。clock_nanosleep()和nanosleep()另一区别在可以选择不同的时钟来测量休眠间隔时间。
Notes:
在介绍POSIX时钟和定时器之前,先介绍一下POSIX都有那些类型的时钟。
CLOCK_REALTIME:可设定的系统级实时时钟。用于度量真实时间。
CLOCK_MONOTONIC :不可设定的恒定态时钟。系统启动后就不会发生改变。
CLOCK_PROCESS_CPUTIME_ID:每进程CPU时间的时钟。测量调用进程所消耗的用户和系统CPU时间。
CLOCK_THREAD_CPUTIME_ID:每线程CPU时间的时钟。测量调用线程所消耗的用户和系统CPU时间。
CLOCK_MONOTONIC_RAW:提供了对纯基于硬件时间的访问。不受NTP时间调整的影响。
CLOCK_REALTIME_COARSE:类似于CLOCK_REALTIME,适用于希望以最小代价获取较低分辨率时间戳的程序。返回值分辨率为jiffy。
CLOCK_MONOTONIC_COARSE:类似于CLOCK_MONOTONIC,适用于希望以最小代价获取较低分辨率时间戳的程序。返回值分辨率为jiffy。
CLOCK_BOOTTIME:
CLOCK_REALTIME_ALARM:
CLOCK_BOOTTIME_ALARM:
kernel/posix-timer.c
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,----------------设置POSIX时间,调用k_clock->clock_set const struct timespec __user *, tp) SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,----------------获取POSIX时间,调用k_clock->clock_get struct timespec __user *,tp) SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,----------------调整时间,调用k_clock->clock_adj struct timex __user *, utx) SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,-----------------获取时钟精度,由硬件决定,调用k_clock->clock_getres struct timespec __user *, tp) SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,--睡眠纳秒数,调用k_clock->nsleep const struct timespec __user *, rqtp, struct timespec __user *, rmtp)
clock_getcpuclockid
CLOCK_PROCESS_CPUTIME_ID类型的clock_gettime
pthread_getcpuclockid
CLOCK_THREAD_CPUTIME_ID类型的clock_gettime
6.POSIX间隔式定时器
编译时需要使用-lrt选项。
#define _POSIX_C_SOURCE 199309 int timer_settime(timer_t timerid, int flags,const struct itimerspec *value, struct itimerspec *old_value); int timer_gettime(timer_t timerid, struct itimerspec *curr_value); int timer_delete(timer_t timerid); int timer_getoverrun(timer_t timerid); |
POSIX定时器API生命周期阶段如下:
- 创建定时器timer_create(),并定义到期时对进程的通知方法,函数返回参数timerid所指向的缓冲区中放置定时器句柄,供后续调用中指代该定时器之用。timer_t用于标识定时器。
- 启动/停止定时器timer_settime()。
- 获取定时器当前值timer_gettime(),返回由timerid指定的间隔以及剩余时间。
- 获取定时器溢出值timer_getoverrun()。
- 删除定时器timer_delete(),对于已启动的定时器,会在移除前自动将其停止;如果定时器因到期已经在待定(pending),那么信号会保持这一状态;如果进程终止,会自动删除所有定时器。
Notes:
kernel/posix-timers.c中: SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,-----创建timer,时钟基准which_clock struct sigevent __user *, timer_event_spec, timer_t __user *, created_timer_id) SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,---------------获取timer剩余时间 struct itimerspec __user *, setting) SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)------------获取timer当前值,时间间隔以及剩余时间 SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,---启动或停止timer const struct itimerspec __user *, new_setting, struct itimerspec __user *, old_setting) SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)----------------删除timer
POSIX定时器分为三类,初始化路径如下:
init_posix_timers--> posix_timers_register_clock-|-> CLOCK_REALTIME |-> CLOCK_MONOTONIC |-> CLOCK_MONOTONIC_RAW |-> CLOCK_REALTIME_COARSE |-> CLOCK_MONOTONIC_COARSE |-> CLOCK_BOOTTIME alarmtimer_init-->posix_timers_register_clock-|->CLOCK_REALTIME_ALARM |->CLOCK_BOOTTIME_ALARM init_posix_cpu_timers-->posix_timers_register_clock-|->CLOCK_PROCESS_CPUTIME_ID |->CLOCK_THREAD_CPUTIME_ID
从posix_timers_register_clock可知,每个clock都必须具备clock_get和clock_getres,然后加入posix_clocks。
void posix_timers_register_clock(const clockid_t clock_id, struct k_clock *new_clock) { if ((unsigned) clock_id >= MAX_CLOCKS) { printk(KERN_WARNING "POSIX clock register failed for clock_id %d\\n", clock_id); return; } if (!new_clock->clock_get) { printk(KERN_WARNING "POSIX clock id %d lacks clock_get()\\n", clock_id); return; } if (!new_clock->clock_getres) {---------------------------------------clock_get和clock_getres必备 printk(KERN_WARNING "POSIX clock id %d lacks clock_getres()\\n", clock_id); return; } posix_clocks[clock_id] = *new_clock;----------------------------------posix_clocks保存所有相关timer信息 }
结构体k_clock提供了关于clock的操作函数,clock_getres/clock_set/clock_get/clock_adj/nsleep/nsleep_restart对应POSIX时钟系统调用;timer_create/timer_set/timer_del/timer_get对应POSIX定时器。
struct k_clock { int (*clock_getres) (const clockid_t which_clock, struct timespec *tp); int (*clock_set) (const clockid_t which_clock, const struct timespec *tp); int (*clock_get) (const clockid_t which_clock, struct timespec * tp); int (*clock_adj) (const clockid_t which_clock, struct timex *tx); int (*timer_create) (struct k_itimer *timer); int (*nsleep) (const clockid_t which_clock, int flags, struct timespec *, struct timespec __user *); long (*nsleep_restart) (struct restart_block *restart_block); int (*timer_set) (struct k_itimer * timr, int flags, struct itimerspec * new_setting, struct itimerspec * old_setting); int (*timer_del) (struct k_itimer * timr); #define TIMER_RETRY 1 void (*timer_get) (struct k_itimer * timr, struct itimerspec * cur_setting); };
系统调用和k_clock对应关系:
syscall |
k_clock |
clock_getres |
clock_getres |
clock_settime |
clock_set |
clock_gettime |
clock_get |
clock_adjtime |
clock_adj |
clock_nanosleep |
nsleep/nsleep_restart |
timer_create |
timer_create |
timer_gettime |
timer_get |
timer_getoverrun |
|
timer_settime |
timer_set |
timer_delete |
timer_del |
从上面的POSIX定时器初始化可以看出分为三类:POSIX定时器、POSIX Alarm定时器、POSIX CPU时间定时器。
k_itimer是POSIX定时器结构体,从其it union可以看出有real、cpu、alarm、mmtimer几种类型的定时器。
struct k_itimer { struct list_head list; /* free/ allocate list */ spinlock_t it_lock; clockid_t it_clock; /* which timer type */ timer_t it_id; /* timer id */ int it_overrun; /* overrun on pending signal */ int it_overrun_last; /* overrun on last delivered signal */ int it_requeue_pending; /* waiting to requeue this timer */ #define REQUEUE_PENDING 1 int it_sigev_notify; /* notify word of sigevent struct */ struct signal_struct *it_signal; union { struct pid *it_pid; /* pid of process to send signal to */ struct task_struct *it_process; /* for clock_nanosleep */ }; struct sigqueue *sigq; /* signal queue entry. */ union { struct { struct hrtimer timer; ktime_t interval; } real; struct cpu_timer_list cpu; struct { unsigned int clock; unsigned int node; unsigned long incr; unsigned long expires; } mmtimer; struct { struct alarm alarmtimer; ktime_t interval; } alarm; struct rcu_head rcu; } it; };
kernel/posix-timers.c
CLOCK_REALTIME/ CLOCK_MONOTONIC/CLOCK_BOOTTIME三种时钟不但提供时钟功能,还提供定时器功能。这三种定时器都是基于hrtimer实现的。
CLOCK_MONOTONIC_RAW/CLOCK_REALTIME_COARSE/CLOCK_MONOTONIC_COARSE三种类型的时钟只提供获取时间和获取精度接口。
kernel/posix-cpu-timers.c
CLOCK_PROCESS_CPUTIME_ID/ CLOCK_THREAD_CPUTIME_ID
只提供了timer_create接口,没有timer_set等。
struct k_clock process = { .clock_getres = process_cpu_clock_getres, .clock_get = process_cpu_clock_get, .timer_create = process_cpu_timer_create, .nsleep = process_cpu_nsleep, .nsleep_restart = process_cpu_nsleep_restart, }; struct k_clock thread = { .clock_getres = thread_cpu_clock_getres, .clock_get = thread_cpu_clock_get, .timer_create = thread_cpu_timer_create, };
kernel/time/alarmtimer.c
CLOCK_REALTIME_ALARM/CLOCK_BOOTTIME_ALARM
struct k_clock alarm_clock = { .clock_getres = alarm_clock_getres, .clock_get = alarm_clock_get, .timer_create = alarm_timer_create, .timer_set = alarm_timer_set, .timer_del = alarm_timer_del, .timer_get = alarm_timer_get, .nsleep = alarm_timer_nsleep, };
系统在suspend时,也会执行alarmtimer设备的suspend。在系统即将进入睡眠时,查找alarm_bases(包括ALARM_REALTIME和ALARM_BOOTTIME)的timerqueue,找出最近一次要超时的timer。将这个timer转换成RTC Timer,这样就会将系统从suspend状态唤醒。
/* Suspend hook structures */ static const struct dev_pm_ops alarmtimer_pm_ops = { .suspend = alarmtimer_suspend, };
static struct platform_driver alarmtimer_driver = { .driver = { .name = "alarmtimer", .pm = &alarmtimer_pm_ops, } }; |
和传统定时器相比POSIX的优势?
- 传统setitimer定时器只有三种定时器,每种只能设置一个。timer_create可以提供多种类型(include/linux/time.h CLOCK_REALTIME…),每种类型多个定时器共存。
- 只能通过发送信号来通知定时器到期。timer_create的evp参数决定定时器到期通知方式,SIGEV_NONE、SIGEV_SIGNAL、SIGEV_THREAD、SIGEV_THREAD_ID。可以选择通过执行线程函数来获取定时器通知。
- 如果一个间隔式定时器到期多次,且响应信号遭到阻塞是,只调用一次信号处理函数。无从知晓定时器是否溢出。timer_getoverrun可以获知超时定时器数目。
- setitimer定时器分辨率只能达到微妙级。POSIX定时器提供纳秒级。
下面整理一下不同定时器ID、定时器时钟基准、获取时间函数:
定时器ID | 定时器时钟 | 延时 | 时间函数 | 是否具备唤醒 |
是否计算 suspend时间 |
CLOCK_REALTIME | HRTIMER_BASE_REALTIME | common_nsleep | posix_clock_realtime_get | X | X |
CLOCK_REALTIME_COARSE | X | X | posix_get_realtime_coarse | X | X |
CLOCK_REALTIME_ALARM | HRTIMER_BASE_REALTIME | alarm_timer_nsleep | alarm_clock_get | X | |
CLOCK_MONOTONIC | HRTIMER_BASE_MONOTONIC | common_nsleep | posix_ktime_get_ts | X | X |
CLOCK_MONOTONIC_RAW | X | X | posix_get_monotonic_raw | X | X |
CLOCK_MONOTONIC_COARSE | X | X | posix_get_monotonic_coarse | X | X |
CLOCK_BOOTTIME | HRTIMER_BASE_BOOTTIME | common_nsleep | posix_get_boottime | X | |
CLOCK_BOOTTIME_ALARM | HRTIMER_BASE_BOOTTIME | alarm_timer_nsleep | alarm_clock_get | ||
CLOCK_PROCESS_CPUTIME_ID | X | process_cpu_nsleep | process_cpu_clock_get | X | X |
CLOCK_THREAD_CPUTIME_ID | X | X | thread_cpu_clock_get | X | X |
测试验证
- POSIX不同类型的定时器区别?归根结底还是基础时钟的不同,还有是否具备_ALARM。
- 多个POSIX定时器共存?可以多个定时器共存。
7.文件描述符定时器:timerfd API
Linux特有的timerfd API,可以从文件描述符中读取其所创建定时器的到期通知,也可以用使用select()、poll()、epoll()监控。
#include <sys/timerfd.h> int timerfd_create(int clockid, int flags); clockid可以设置为CLOCK_REALTIME或者CLOCK_MONOTONIC。 相当于open创建一个句柄,可以使用close()关闭响应文件描述符。 int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); int timerfd_gettime(int fd, struct itimerspec *curr_value); |
timerfd_settime()启动了定时器,可以通过read()来读取定时器到期信息。read缓冲区必须容纳uint64_t类型,读取返回值是已发生的到期次数。
执行./demo_timerfd 1:1 100,
以上是关于KVM虚拟化技术-实战与原理解析的主要内容,如果未能解决你的问题,请参考以下文章