Chinaunix首页 | 论坛 | 博客
  • 博客访问: 764426
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1150
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-17 14:32
个人简介

小公司研发总监,既当司令也当兵!

文章分类

全部博文(144)

分类: LINUX

2016-04-15 14:14:23

原文地址:linux应用层定时器 作者:wangbaolin719


  1. 使用定时器的目的无非是为了周期性的执行某一任务,或者是到了一个指定时间去执行某一个任务。要达到这一目的,一般有两个常见的比较有效的方法。一个是用linux内部的三个定时器,另一个是用sleep, usleep函数让进程睡眠一段时间,其实,还有一个方法,那就是用gettimeofday, difftime等自己来计算时间间隔,然后时间到了就执行某一任务,但是这种方法效率低,所以不常用。

  2. 1、alarm
  3. 如果不要求很精确的话,用 alarm() 和 signal() 就够了
  4. unsigned int alarm(unsigned int seconds)
  5. 专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间。进程调用alarm后,任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
  6. 示例:
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <signal.h>
  10. void sigalrm_fn(int sig)
  11. {
  12.     printf("alarm!\n");
  13.     alarm(2);
  14.     return;
  15. }
  16. int main(void)
  17. {
  18.     signal(SIGALRM, sigalrm_fn);
  19.     alarm(2);
  20.     
  21.     while(1) pause();
  22. }

  23. 2、setitimer
  24. int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
  25. int getitimer(int which, struct itimerval *value);
  26. strcut timeval
  27. {
  28.    long tv_sec; /**/
  29.    long tv_usec; /*微秒*/
  30. };

  31. struct itimerval
  32. {
  33.    struct timeval it_interval; /*时间间隔*/
  34.    struct timeval it_value; /*当前时间计数*/
  35. };
  36. setitimer()比alarm功能强大,支持3种类型的定时器:
  37. ITIMER_REAL: 给一个指定的时间间隔,按照实际的时间来减少这个计数,当时间间隔为0的时候发出SIGALRM信号
  38. ITIMER_VIRTUAL: 给定一个时间间隔,当进程执行的时候才减少计数,时间间隔为0的时候发出SIGVTALRM信号
  39. ITIMER_PROF: 给定一个时间间隔,当进程执行或者是系统为进程调度的时候,减少计数,时间到了,发出SIGPROF信号。
  40. Setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。
  41. 下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号::
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <unistd.h>
  45. #include <signal.h>
  46. #include <time.h>
  47. #include <sys/time.h>
  48. int sec;
  49. void sigroutine(int signo){
  50.    switch (signo){
  51.    case SIGALRM:
  52.        printf("Catch a signal -- SIGALRM \n");
  53.        signal(SIGALRM, sigroutine);
  54.        break;
  55.    case SIGVTALRM:
  56.        printf("Catch a signal -- SIGVTALRM \n");
  57.        signal(SIGVTALRM, sigroutine);
  58.        break;
  59.    }
  60.    return;
  61. }

  62. int main()
  63. {
  64.    struct itimerval value, ovalue, value2;

  65.    sec = 5;
  66.    printf("process id is %d ", getpid());
  67.    signal(SIGALRM, sigroutine);
  68.    signal(SIGVTALRM, sigroutine);
  69.    value.it_value.tv_sec = 1;
  70.    value.it_value.tv_usec = 0;
  71.    value.it_interval.tv_sec = 1;
  72.    value.it_interval.tv_usec = 0;
  73.    setitimer(ITIMER_REAL, &value, &ovalue);
  74.    value2.it_value.tv_sec = 0;
  75.    value2.it_value.tv_usec = 500000;
  76.    value2.it_interval.tv_sec = 0;
  77.    value2.it_interval.tv_usec = 500000;
  78.    setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
  79.    for(;;);
  80. }

  81. 该例子的屏幕拷贝如下:
  82. localhost:~$ ./timer_test
  83. process id is 579
  84. Catch a signal – SIGVTALRM
  85. Catch a signal – SIGALRM
  86. Catch a signal – SIGVTALRM
  87. Catch a signal – SIGVTALRM
  88. Catch a signal – SIGALRM
  89. Catch a signal –GVTALRM

  90. 注意:Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做"不可靠信号",信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是:进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用 signal(),重新安装该信号。

  91. 3.用sleep以及usleep怎么实现定时执行任务。
  92. #include <signal.h>
  93. #include <unistd.h>
  94. #include <string.h>
  95. #include <stdio.h>
  96. static char msg[] = "I received a msg.\n";
  97. int len;
  98. void show_msg(int signo)
  99. {
  100.     write(STDERR_FILENO, msg, len);
  101. }

  102. int main()
  103. {
  104.     struct sigaction act;
  105.     union sigval tsval;
  106.     act.sa_handler = show_msg;
  107.     act.sa_flags = 0;
  108.     sigemptyset(&act.sa_mask);
  109.     sigaction(50, &act, NULL);
  110.     len = strlen(msg);
  111.     while ( 1 )
  112.     {
  113.         sleep(2); /*睡眠2秒*/
  114.         /*向主进程发送信号,实际上是自己给自己发信号*/
  115.         sigqueue(getpid(), 50, tsval);
  116.     }
  117.     return 0;
  118. }
  119. 看到了吧,这个要比上面的简单多了,而且你用秒表测一下,时间很准,指定2秒到了就给你输出一个字符串。所以,如果你只做一般的定时,到了时间去执行一个任务,这种方法是最简单的。

  120. 4.通过自己计算时间差的方法来定时
  121. #include <signal.h>
  122. #include <unistd.h>
  123. #include <string.h>
  124. #include <stdio.h>
  125. #include <time.h>
  126. static char msg[] = "I received a msg.\n";
  127. int len;
  128. static time_t lasttime;
  129. void show_msg(int signo)
  130. {
  131.     write(STDERR_FILENO, msg, len);
  132. }
  133. int main()
  134. {
  135.     struct sigaction act;
  136.     union sigval tsval;
  137.     act.sa_handler = show_msg;
  138.     act.sa_flags = 0;
  139.     sigemptyset(&act.sa_mask);
  140.     sigaction(50, &act, NULL);
  141.     len = strlen(msg);
  142.     time(&lasttime);
  143.     while ( 1 )
  144.     {
  145.         time_t nowtime;
  146.         /*获取当前时间*/
  147.         time(&nowtime);
  148.         /*和上一次的时间做比较,如果大于等于2秒,则立刻发送信号*/
  149.         if (nowtime - lasttime >= 2)
  150.         {
  151.             /*向主进程发送信号,实际上是自己给自己发信号*/
  152.             sigqueue(getpid(), 50, tsval);
  153.             lasttime = nowtime;
  154.         }
  155.     }
  156.     return 0;
  157. }
  158. 这个和上面不同之处在于,是自己手工计算时间差的,如果你想更精确的计算时间差,你可以把 time 函数换成gettimeofday,这个可以精确到微妙。

阅读(2509) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~