alarm 信号

严劲
2023-12-01
在Linux实现一个定时器,不像Win32下那样直观。在Win32调用SetTimer就行了,在Linux下则没有相应函数可以直接调用。定时器作为一个常用的功能,在Linux当然也有相应实现。下面我们看看几种常用的方法。 
  
要实现定时器功能,最土的办法实现莫过于用sleep/usleep来实现了。当然,它会阻塞当前线程,除了处理定时功能外,什么活也干不了。当然要解决这个问题不难,创建一个单独的线程来负责定时器,其它线程负责正常的任务就行了。 
  
要实现定时器功能,最简单的办法就是ALARM信号。这种方法简单,也相应的缺陷:用信号实现效率较低;   最小精度为1秒,无法实现高精度的定义器。简单示例: 
#include   <stdio.h> 
#include   <signal.h> 
  
static   void   timer(int   sig) 
{ 
        if(sig   ==   SIGALRM) 
        { 
                printf( "timer\n "); 
        } 
  
        return; 

  
int   main(int   argc,   char*   argv[]) 
{ 
        signal(SIGALRM,   timer); 
  
        alarm(1); 
  
        getchar(); 
  
        return   0; 

  
(setitimer和alarm有类似的功能,也是通过信号来实现) 
  
最优雅的方法是使用RTC机制。利用select函数,你可以用单线程实现定时器,同时还可以处理其它任务。简单示例: 
  
#include   <stdio.h> 
#include   <linux/rtc.h> 
#include   <sys/ioctl.h> 
#include   <sys/time.h> 
#include   <sys/types.h> 
#include   <fcntl.h> 
#include   <unistd.h> 
#include   <errno.h> 
  
int   main(int   argc,   char*   argv[]) 
{ 
        unsigned   long   i   =   0; 
        unsigned   long   data   =   0; 
        int   retval   =   0; 
        int   fd   =   open   ( "/dev/rtc ",   O_RDONLY); 
  
        if(fd   <   0) 
        { 
                perror( "open "); 
                exit(errno); 
        } 
  
        /*Set   the   freq   as   4Hz*/ 
        if(ioctl(fd,   RTC_IRQP_SET,   4)   <   0) 
        { 
                perror( "ioctl(RTC_IRQP_SET) "); 
                close(fd); 
                exit(errno); 
        } 
        /*Set   the   freq   as   4Hz*/ 
        if(ioctl(fd,   RTC_IRQP_SET,   4)   <   0) 
        { 
                perror( "ioctl(RTC_IRQP_SET) "); 
                close(fd); 
                exit(errno); 
        } 
  
        /*   Enable   periodic   interrupts   */ 
        if(ioctl(fd,   RTC_PIE_ON,   0)   <   0) 
        { 
                perror( "ioctl(RTC_PIE_ON) "); 
                close(fd); 
                exit(errno); 
        } 
  
        for(i   =   0;   i   <   100;   i++) 
        { 
                if(read(fd,   &data,   sizeof(unsigned   long))   <   0) 
                { 
                        perror( "read "); 
                        close(fd); 
                        exit(errno); 
                } 
                printf( "timer\n "); 
        } 
        /*   Disable   periodic   interrupts   */ 
        ioctl(fd,   RTC_PIE_OFF,   0); 
        close(fd); 
  
        return   0; 

Linux下定时器使用   
Linux下的定时器有两种,以下分别介绍:   

              1、alarm   
              如果不要求很精确的话,用   alarm()   和   signal()   就够了   
                      unsigned   int   alarm(unsigned   int   seconds)   
              专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间。进程调用alarm后,任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。   

              示例:   
              #include   <stdio.h>   
              #include   <unistd.h>   
              #include   <signal.h>   

              void   sigalrm_fn(int   sig)   
              {   
                              /*   Do   something   */   
                              printf( "alarm!\n ");   

                              alarm(2);   
                              return;   
              }   

              int   main(void)   
              {   
                              signal(SIGALRM,   sigalrm_fn);   
                              alarm(2);   

                              /*   Do   someting   */   
                              while(1)   pause();   
              }   


              2、setitimer   
              int   setitimer(int   which,   const   struct   itimerval   *value,   struct   itimerval   *ovalue));   
              setitimer()比alarm功能强大,支持3种类型的定时器:   

              ITIMER_REAL   :     以系统真实的时间来计算,它送出SIGALRM信号。       
              ITIMER_VIRTUAL   :     以该行程真正有执行的时间来计算,它送出SIGVTALRM信号。       
              ITIMER_PROF   :     以行程真正有执行及在核心中所费的时间来计算,它送出SIGPROF信号。       
              Setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。   
              Setitimer()调用成功返回0,否则返回-1。   

              下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号::   
              #include   <stdio.h>   
              #include   <stdlib.h>   
              #include   <unistd.h>   
              #include   <signal.h>   
              #include   <time.h>   
              #include   <sys/time.h>   

              int   sec;   
              void   sigroutine(int   signo) 
{   

                      switch   (signo){   
                      case   SIGALRM:   
                              printf( "Catch   a   signal   --   SIGALRM   \n ");   
                              signal(SIGALRM,   sigroutine);   
                              break;   
                      case   SIGVTALRM:   
                              printf( "Catch   a   signal   --   SIGVTALRM   \n ");   
                              signal(SIGVTALRM,   sigroutine);   
                              break;   
                      }   
                      return;   
              }   

              int   main()   
              {   
                      struct   itimerval   value,   ovalue,   value2;   
        
                      sec   =   5;   
                      printf( "process   id   is   %d   ",   getpid());   
                      signal(SIGALRM,   sigroutine);   
                      signal(SIGVTALRM,   sigroutine);   
                      value.it_value.tv_sec   =   1;   
                      value.it_value.tv_usec   =   0;   
                      value.it_interval.tv_sec   =   1;   
                      value.it_interval.tv_usec   =   0;   
                      setitimer(ITIMER_REAL,   &value,   &ovalue);   

                      value2.it_value.tv_sec   =   0;   
                      value2.it_value.tv_usec   =   500000;   
                      value2.it_interval.tv_sec   =   0;   
                      value2.it_interval.tv_usec   =   500000;   
                      setitimer(ITIMER_VIRTUAL,   &value2,   &ovalue);   
                      for(;;)   
                              ;   
              }   


               该例子的屏幕拷贝如下:   

              localhost:~$   ./timer_test   
              process   id   is   579   
              Catch   a   signal   –   SIGVTALRM   
              Catch   a   signal   –   SIGALRM   
              Catch   a   signal   –   SIGVTALRM   
              Catch   a   signal   –   SIGVTALRM   
              Catch   a   signal   –   SIGALRM   
              Catch   a   signal   –GVTALRM   


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