Linux进程的计时器和间隔计时器


比较常用的时间控制函数就是sleep();让我们当前的进程休眠指定的秒数。

系统中的每个进程都有一个私有的闹钟。这个闹钟很像一个计时器,可以设置在一定秒数后的闹钟。 时间一到,时钟就发送一个信号SIGALRM到进程。除非为SIGALRM设置了处理函数,否则信号将杀死这个进程。sleep函数由3个步骤组成:

     1.为SIGALRM设置一个处理函数;

     2.调用alarm(num_seconds);

     3.调用pause。

系统调用pause挂起进程直到信号到达。任何信号都可以唤醒进程,而非仅仅等待SIGALRM。

  1. #include<stdio.h>   
  2. #include<signal.h>   
  3.   
  4. int main()  
  5. {  
  6.     void wakeup(int);  
  7.   
  8.     printf("about to sleep for 4 second\n");  
  9.     signal(SIGALRM, wakeup);  //  SIGALRM handler   
  10.     alarm(4);      //set clock       unsigned old = alarm(unsigned seconds);  old---> the rest seconds if there is a signal.   
  11.     pause();       // freeze here   
  12.     printf("Morning so soon ?\n");  
  13. }  
  14.   
  15. void wakeup(int signum)  
  16. {  
  17. #ifndef SHHHH   
  18.     printf("Alarm received from kernel\n");  
  19. #endif   
  20. }  
比sleep()更精确的延时是usleep(), 它可以精确到微秒。将当前进程挂起n微秒或者直到有一个不能被忽略的信号到达。

进程的三种计时器: 真实、进程和实用

1.ITIMER_REAL : 这个计时器计量真实的时间,无论进程在用户态还是核心态用了多少处理器时间它都记录。当这个计时器用尽,发送SIGALRM消息。

2.ITMER_VIRTUAL: 只有进程在用户态运行时才计时。当虚拟计时器用尽时,发送SIGVTALRM消息。这个时间30s比真实时间30s长。

3.ITIMER_PROF: 这个计时器在进程运行于用户态或由该进程调用而陷入核心态时计时。当这个计时器用尽,发送SIGPROF信号。


在程序中使用间隔计时器要稍微复杂一点,要选择计时器的类型,然后需要选择初始间隔和重复间隔,还要设置在struct itimerval中的值,然后将这个结构体通过调用setitimer传给计时器。为了读取计时器设置,使用getitimer。

  1. #include<stdio.h>   
  2. #include<sys/time.h>   
  3. #include<signal.h>   
  4.   
  5. int set_ticker(int n_msecs);  
  6.   
  7. int main()  
  8. {  
  9.     void countdown(int );  
  10.   
  11.     signal(SIGALRM, countdown);  
  12.     if(set_ticker(500) == -1)  
  13.         perror("set_ticker");  
  14.     else  
  15.         while(1)  
  16.         {  
  17.             pause();  
  18.         }  
  19.     return 0;  
  20. }  
  21.   
  22. void countdown(int signum)  
  23. {  
  24.     static int num = 10;  
  25.     printf("%d ..", num--);  
  26.     fflush(stdout);  
  27.     if(num < 0)   
  28.     {  
  29.         printf("DONE! \n");  
  30.         exit(0);  
  31.     }  
  32.       
  33. }  
  34.   
  35. int set_ticker(int n_msecs)  
  36. {  
  37.     struct itimerval new_timeset;  
  38.     long n_sec, n_usecs;  
  39.   
  40.     n_sec = n_msecs / 1000;  
  41.   
  42.     n_usecs = (n_msecs % 1000) * 1000L;  
  43.   
  44.     new_timeset.it_interval.tv_sec = n_sec;  // set reload   设置初始间隔   
  45.     new_timeset.it_interval.tv_usec = n_usecs; //new ticker value   
  46.   
  47.     new_timeset.it_value.tv_sec = n_sec;   //store this   设置重复间隔   
  48.     new_timeset.it_value.tv_usec = n_usecs; //and this   
  49.   
  50.     return setitimer(ITIMER_REAL, &new_timeset, NULL);   
  51.       //#include<sys/time.h>  getitimer(int which, struct itimerval *val)   
  52.       //setitimer(int which, const struct itimerval* newval, struct itimerval *oldval);   
  53.       //which 指定哪种计时器  ITMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF.    
  54. }  
使用signal函数设置countdown来处理SIGALRM信号。然后通过set_ticker来设置微秒数。 set_ticker通过装载初始间隔和重复间隔设置间隔计时器。每个间隔是由两个值组成:秒数和微秒数,如同实数的整数部分和小数部分。计时器开始计时时,控制返回到main。

进入死循环后,调用pause。大约每500us,控制跳转到countdown函数。当num到0时,调用exit结束。

struct itimeal的结构如下:

  1. struct itimerval  
  2. {  
  3.     struct timeval it_value;   //初始时间间隔,第一次发信号使用的时间,之后都不用了。   
  4.     struct timeval it_interval;  //重复时间间隔,第一次发信号之后使用的时间。   
  5. };  
  6.   
  7. struct timeval  
  8. {  
  9.     time_t tv_sec;      //second   
  10.     SUSEconds_t tv_usec;    //microseconds   
  11. };  

内核在每个时钟脉冲时,遍历所有的间隔计时器,使用这样的手段来管理每个进程的真实计时器。而其他两个计时器,在程序运行的特定时刻进行计时。通过这样的机制,来实现每个进程三个计时器的管理。我们通过setitimer系统调用,得到了更高的精度控制计时器,同时能够以固定的时间间隔发送信号。当然了,这个时间全可以随时修改的,在中断中改变一下,就可以了。

相关内容