Linux内核时钟系统和定时器实现( 三 )


下面是测试代码:
#include <unistd.h>#include <signal.h>#include <sys/time.h>#include <IOStream>void sig_handler(int signo){std::cout<<"recieve sigal: "<<signo<<std::endl;}int main(){signal(SIGALRM, sig_handler);struct itimerval timer_set;//启动时间(5s后启动)timer_set.it_value.tv_sec = 5;timer_set.it_value.tv_usec = 0;//间隔定时器间隔:2stimer_set.it_interval.tv_sec = 2;timer_set.it_interval.tv_usec = 0;if(setitimer(ITIMER_REAL, &timer_set, NULL) < 0){std::cout<<"start timer failed..."<<std::endl;return 0;}int temp;std::cin>>temp;return 0;}2.3 POSIX定时器
POSIX定时器的是为了解决间隔定时器itimer的以下问题:

  • 一个进程同一时刻只能有一个同一种类型(ITIMER_REAL, ITIMER_PROF, ITIMER_VIRT)的itimer 。POSIX定时器在一个进程中可以创建任意多个timer 。
  • itimer定时器到期后,只能通过信号(SIGALRM,SIGVTALRM,SIGPROF)的方式通知进程,POSIX定时器到期后不仅可以通过信号进行通知,还可以使用自定义信号,还可以通过启动一个线程来进行通知 。
  • itimer支持us级别,POSIX定时器支持ns级别 。
POSIX定时器提供的定时器API如下:
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);int timer_gettime(timer_t timerid,struct itimerspec *value);int timer_getoverrun(timer_t timerid);int timer_delete (timer_t timerid);其中时间结构itimerspec定义如下:该结构和itimer的itimerval结构用处和含义类似,只是提供了ns级别的精度
struct itimerspec{struct timespec it_interval;// 时间间隔struct timespec it_value;// 首次到期时间};struct timespec{time_ttv_sec//Seconds.longtv_nsec//Nanoseconds.};it_value表示定时间经过这么长时间到时,当定时器到时候,就会将it_interval的值赋给it_value 。如果it_interval等于0,那么表示该定时器不是一个时间间隔定时器,一旦it_value到期后定时器就回到未启动状态 。
timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
创建一个POSIX timer,在创建的时候,需要指出定时器的类型,定时器超时通知机制 。创建成功后通过参数返回创建的定时器的ID 。
参数clock_id用来指定定时器时钟的类型,时钟类型有以下6种:
CLOCK_REALTIME:系统实时时间,即日历时间;
  • CLOCK_MONOTONIC:从系统启动开始到现在为止的时间;
  • CLOCK_PROCESS_CPUTIME_ID:本进程启动到执行到当前代码,系统CPU花费的时间;
  • CLOCK_THREAD_CPUTIME_ID:本线程启动到执行到当前代码,系统CPU花费的时间;
  • CLOCK_REALTIME_HR:CLOCK_REALTIME的细粒度(高精度)版本;
  • CLOCK_MONOTONIC_HR:CLOCK_MONOTONIC的细粒度版本;
struct sigevent设置了定时器到期时的通知方式和处理方式等,结构的定义如下:
struct sigevent{int sigev_notify;//设置定时器到期后的行为int sigev_signo;//设置产生信号的信号码union sigvalsigev_value; //设置产生信号的值void (*sigev_notify_function)(union sigval);//定时器到期,从该地址启动一个线程pthread_attr_t *sigev_notify_attributes;//创建线程的属性}union sigval{int sival_int;//integer valuevoid *sival_ptr; //pointer value}如果sigevent传入NULL,那么定时器到期会产生默认的信号,对CLOCK_REALTIMER来说,默认信号就是SIGALRM,如果要产生除默认信号之外的其他信号,程序必须将evp->sigev_signo设置为期望的信号码 。
如果几个定时器产生了同一个信号,处理程序可以用 sigev_value来区分是哪个定时器产生了信号 。要实现这种功能,程序必须在为信号安装处理程序时,使用struct sigaction的成员sa_flags中的标志符SA_SIGINFO 。
sigev_notify的值可取以下几种:
  • SIGEV_NONE:定时器到期后什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息 。
  • SIGEV_SIGNAL:定时器到期后,内核会将sigev_signo所指定的信号,传送给进程,在信号处理程序中,si_value会被设定为sigev_value的值 。
  • SIGEV_THREAD:定时器到期后,内核会以sigev_notification_attributes为线程属性创建一个线程,线程的入口地址为sigev_notify_function,传入sigev_value作为一个参数 。
timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue)
创建POSIX定时器后,该定时器并没有启动,需要通过timer_settime()接口设置定时器的到期时间和周期触发时间 。


推荐阅读