首页 > 代码库 > KVM虚拟化技术-实战与原理解析
KVM虚拟化技术-实战与原理解析
第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(进程的用户和内核时间总和)。
- 如果这些信号不设置处理函数,则默认会终止进程。
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)。 |
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); |
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()另一区别在可以选择不同的时钟来测量休眠间隔时间。
6.POSIX间隔式定时器
#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); |
CLOCK_REALTIME时钟是一种系统级时钟,用于度量真实时间。
CLOCK_MONOTONIC系统启动后就不会发生改变,适用于那些无法容忍系统时钟发生跳跃性变化的应用。Linux上这种时钟对事件的测量食欲系统启动。
CLOCK_PROCESS_CPUTIME_ID时钟测量调用进程所消耗的用户和系统CPU时间。
CLOCK_THREAD_CPUTIME_ID时钟用于测量进程中单条线程的用户和系统CPU时间。
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,使用Ctrl-Z将其变成挂起到背景执行,fg拉倒前台,Ctrl-C终止执行。可以看出在背景执行期间有10此到期。
./demo_timerfd 1:1 100 |
8.总结
setitimer()和alarm()设定定时器,以便于在经历指定的一段实际时间后收到信号通知。
sleep()和nano_sleep()指定程序暂停执行一段特定间隔的实际时间。
timerfd_create()、timerfd_settime()、timerfd_gettime()提供一组创建定时器的接口,允许从文件秒速附中读取特定定时器通知。可以使用read()、select()、poll()、epoll()、close()来操作这些描述符。
KVM虚拟化技术-实战与原理解析