zoukankan      html  css  js  c++  java
  • Linux定时器 使用

    1.alarm

    alarm()执行后,进程将继续执行,在后期(alarm以后)的执行过程中将会在seconds秒后收到信号SIGALRM并执行其处理函数。

    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    void sigalrm_fn(int sig)
    {
        printf("alarm! ");
        alarm(2);
        return;
    }
    int main(void)
    {
        signal(SIGALRM, sigalrm_fn);
        alarm(1);
        while(1) pause();
    }

    2.alarm定时器,但是只能精确到秒,然而我们如果需要用到更精准的怎么办?

    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。

    #include <stdio.h>    // for printf()  
     
    #include <unistd.h>   // for pause()  
     
    #include <signal.h>   // for signal()  
     
    #include <string.h>   // for memset()  
     
    #include <sys/time.h> // struct itimeral. setitimer()  
      
      
      
    void printMsg(int);  
      
      
      
    int main() {  
      
      // Get system call result to determine successful or failed  
      
      int res = 0;  
      
      // Register printMsg to SIGALRM  
      
      signal(SIGALRM, printMsg);  
      
        
      
      struct itimerval tick;  
      
      // Initialize struct  
      
      memset(&tick, 0, sizeof(tick));  
      
      // Timeout to run function first time  
      
      tick.it_value.tv_sec = 1;  // sec  
      
      tick.it_value.tv_usec = 0; // micro sec.  
      
      // Interval time to run function  
      
      tick.it_interval.tv_sec = 1;  
      
      tick.it_interval.tv_usec = 0;  
      
      // Set timer, ITIMER_REAL : real-time to decrease timer,  
      
      //                          send SIGALRM when timeout  
      
      res = setitimer(ITIMER_REAL, &tick, NULL);  
      
      if (res) {  
      
        printf("Set timer failed!!/n");  
      
      }  
      
      
      
      // Always sleep to catch SIGALRM signal  
      
      while(1) {  
      
        pause();  
      
      }  
      
      
      return 0;    
      
    }  
      
      
    void printMsg(int num) {  
      
      printf("%s","Hello World!!
    ");  
      
    }  

    setitimer不会引起线程的阻塞、也不会引起线程的切换动作,就是简单的启动一个定时器,开始定时,而且这种定时应该是基于内核的,(windwos的settimer是基于一种消息的模型);setitimer虽然有三种类型ITIMER_REAL,ITIMER_VIRTUAL ITIMER_PROF,但是在同一时间同一进程,一种类型的只能有1个setitimer;
    如果我们需要多个定时器怎么办?

    [code=C/C++]
    #include<stdio.h>   
    #include<stdlib.h>   
    #include<time.h>   
    #include<sys/time.h>   
    #include<errno.h>   
    #include<string.h>   
    #include<unistd.h>   
    #include<sys/types.h>   
    #include<sys/select.h>   
      
      
    int main(int argc, char **argv)  
    {  
        unsigned int nTimeTestSec = 0;  
        unsigned int nTimeTest = 0;  
        struct timeval tvBegin;  
        struct timeval tvNow;  
        int ret = 0;  
        unsigned int nDelay = 0;  
        struct timeval tv;  
        int fd = 1;  
        int i = 0;  
        struct timespec req;  
      
        unsigned int delay[20] =   
            {500000, 100000, 50000, 10000, 1000, 900, 500, 100, 10, 1, 0};  
        int nReduce = 0; //误差   
      
        fprintf(stderr, "%19s%12s%12s%12s ", "fuction", "time(usec)", "realtime", "reduce");  
        fprintf(stderr, "---------------------------------------------------- ");  
        for (i = 0; i < 20; i++)  
        {  
            if (delay[i] <= 0)  
                break;  
            nDelay = delay[i];  
            //test sleep   
            gettimeofday(&tvBegin, NULL);  
            ret = usleep(nDelay);  
            if(ret == -1)  
            {  
                fprintf(stderr, "usleep error, errno=%d [%s] ", errno, strerror(errno));  
            }  
            gettimeofday(&tvNow, NULL);  
            nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;  
            nReduce = nTimeTest - nDelay;  
      
             fprintf (stderr, " usleep       %8u   %8u   %8d ", nDelay, nTimeTest,nReduce);  
      
             //test nanosleep   
             req.tv_sec = nDelay/1000000;  
             req.tv_nsec = (nDelay%1000000) * 1000;  
      
             gettimeofday(&tvBegin, NULL);  
             ret = nanosleep(&req, NULL);  
             if (-1 == ret)  
             {  
                fprintf (stderr, " nanousleep   %8u   not support ", nDelay);  
             }  
             gettimeofday(&tvNow, NULL);  
             nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;  
             nReduce = nTimeTest - nDelay;  
             fprintf (stderr, " nanosleep    %8u   %8u   %8d ", nDelay, nTimeTest,nReduce);  
      
             //test select   
             tv.tv_sec = 0;  
             tv.tv_usec = nDelay;  
      
             gettimeofday(&tvBegin, NULL);  
             ret = select(0, NULL, NULL, NULL, &tv);  
             if (-1 == ret)  
             {  
                fprintf(stderr, "select error. errno = %d [%s] ", errno, strerror(errno));  
             }  
      
             gettimeofday(&tvNow, NULL);  
             nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;  
             nReduce = nTimeTest - nDelay;  
             fprintf (stderr, " select       %8u   %8u   %8d ", nDelay, nTimeTest,nReduce);  
      
             //pselcet   
             req.tv_sec = nDelay/1000000;  
             req.tv_nsec = (nDelay%1000000) * 1000;  
      
             gettimeofday(&tvBegin, NULL);  
             ret = pselect(0, NULL, NULL, NULL, &req, NULL);  
             if (-1 == ret)  
             {  
                fprintf(stderr, "select error. errno = %d [%s] ", errno, strerror(errno));  
             }  
      
             gettimeofday(&tvNow, NULL);  
             nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;  
             nReduce = nTimeTest - nDelay;  
             fprintf (stderr, " pselect      %8u   %8u   %8d ", nDelay, nTimeTest,nReduce);  
      
             fprintf (stderr, "-------------------------------- ");  
      
        }  
          
        return 0;  

    [/code]

    int msSleep(long ms) {

        struct timeval tv;

        tv.tv_sec = 0;

        tv.tv_usec = ms;

         return select(0, NULL, NULL, NULL, &tv);

    }


    上面这段代码作者有这样的话
    “老大建议我们在对精度要求较高的情况下使用select()作为定时器,最大的好处就是不会影响信号处理线程安全,而且精度能得到保证。在这个实验中,当时间延时时间较长时,select和pselect表现较差,当时间小于1毫秒时,他们的精确度便提高了,表现与usleep、nanosleep不相上下,有时精度甚至超过后者。

    查了下上面4个函数,select,和sleep是可重入函数,在使用的时候会引起线程的切换;所以有“不会影响信号处理线程安全”而usleep,nanosleep,不可重入函数,程序是在暂停状态,也就是不能线程切换;但是不知道setitimer会不会记时;

    select定时器:

    在编写程序时,我们经常会用到定时器。首先看看select函数原型如下:

    复制代码代码如下:

    int select(int nfds, fd_set *readfds, fd_set *writefds,
                      fd_set *exceptfds, struct timeval *timeout);


    参数说明:
    slect的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),位数组的每一位代表其对应的描述符是否需要被检查。
    select的第二三四个参数表示需要关注读、写、错误事件的文件描述符位数组,这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件。所以每次调用select前都需重新初始化fdset。
    timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。
    利用select实现定时器,需要利用其timeout参数,注意到:
     1)select函数使用了一个结构体timeval作为其参数。
     2)select函数会更新timeval的值,timeval保持的值为剩余时间。
    如果我们指定了参数timeval的值,而将其他参数都置为0或者NULL,那么在时间耗尽后,select函数便返回,基于这一点,我们可以利用select实现精确定时。
    timeval的结构如下:

    复制代码代码如下:

    struct timeval{
    long tv_sec;/*secons*
    long tv_usec;/*microseconds*/
    }


    我们可以看出其精确到microseconds也即微妙。
    一、秒级定时器

    复制代码代码如下:

    void seconds_sleep(unsigned seconds){
        struct timeval tv;
        tv.tv_sec=seconds;
        tv.tv_usec=0;
        int err;
        do{
           err=select(0,NULL,NULL,NULL,&tv);
        }while(err<0 && errno==EINTR);
    }


     二、毫秒级别定时器

    复制代码代码如下:

    void milliseconds_sleep(unsigned long mSec){
        struct timeval tv;
        tv.tv_sec=mSec/1000;
        tv.tv_usec=(mSec%1000)*1000;
        int err;
        do{
           err=select(0,NULL,NULL,NULL,&tv);
        }while(err<0 && errno==EINTR);
    }


     三、微妙级别定时器

    复制代码代码如下:

    void microseconds_sleep(unsigned long uSec){
        struct timeval tv;
        tv.tv_sec=uSec/1000000;
        tv.tv_usec=uSec%1000000;
        int err;
        do{
            err=select(0,NULL,NULL,NULL,&tv);
        }while(err<0 && errno==EINTR);
    }


    现在我们来编写几行代码看看定时效果吧。

    复制代码代码如下:

    #include <stdio.h>
    #include <sys/time.h>
    #include <errno.h>
    int main()
    {
        int i;
        for(i=0;i<5;++i){
        printf("%d ",i);
        //seconds_sleep(1);
        //milliseconds_sleep(1500);
        microseconds_sleep(1900000);
        }
    }


     注:timeval结构体中虽然指定了一个微妙级别的分辨率,但内核支持的分别率往往没有这么高,很多unix内核将超时值向上舍入成10ms的倍数。此外,加上内核调度延时现象,即定时器时间到后,内核还需要花一定时间调度相应进程的运行。因此,定时器的精度,最终还是由内核支持的分别率决定。

     

    linux下定时器的实现(select+线程)

     更好的计时器类实现:LINUX RTC机制实现计时器类
          很多时候需要在LINUX下用到定时器,但像setitimer()和alarm()这样的定时器有时会和sleep()函数发生冲突,这样就给编程带来了很大的困难。
        写了一个定时器的类,使用select进行精确定时。而且可以在系统中创建不限数量的定时器,且互不干扰。类的内部采用线程实现。即线程+select。代码如下:

    CTimer.h:
    /*
    * CTimer.h
    *
    * Created on: 2009-7-13
    *      Author: DEAN
    */

    //////////////////////////////////////////////////////////////////////////
    // This class provide a timer to finish some works.
    // Call SetTimer() to set the timer_interval. Call StartTimer()
    // to enable it and call StopTimer() to stop it.
    // The work you want to do should be written on OnTimer
    // function.
    //////////////////////////////////////////////////////////////////////////

    #ifndef CTIMER_H_
    #define CTIMER_H_

    #include <pthread.h>
    #include <sys/time.h>

    class CTimer
    {
    private:
        pthread_t thread_timer;                                            //用于声明线程ID   sizeof(pthread_t)=4;
        long m_second, m_microsecond;
        static void *OnTimer_stub(void *p)
        {
            (static_cast<CTimer*>(p))->thread_proc();       //static_cast < type-id > ( expression )  该运算符把expression转换为type-id类型;
        }
        void thread_proc();                                                   //
        void OnTimer();
    public:
        CTimer();
        CTimer(long second, long microsecond);
        virtual ~CTimer();
        void SetTimer(long second,long microsecond);
        void StartTimer();
        void StopTimer();
    };
    #endif /* CTIMER_H_ */

    CTimer.cpp:
    /*
    * CTimer.cpp
    *
    * Created on: 2009-7-13
    *      Author: DEAN
    */

    #include "CTimer.h"
    #include <iostream>
    #include <sys/select.h>
    #include <time.h>
    #include <pthread.h>

    using namespace std;
    //////////////////////////public methods//////////////////////////
    CTimer::CTimer():
        m_second(0), m_microsecond(0)
    {
    }

    CTimer::CTimer(long second, long microsecond) :
        m_second(second), m_microsecond(microsecond)
    {
    }

    CTimer::~CTimer()
    {
    }

    void CTimer::SetTimer(long second, long microsecond)
    {
        m_second = second;
        m_microsecond = microsecond;
    }

    void CTimer::StartTimer()
    {
        pthread_create(&thread_timer, NULL, OnTimer_stub, this);
    }

    void CTimer::StopTimer()
    {
        pthread_cancel(thread_timer);
        pthread_join(thread_timer, NULL); //wait the thread stopped
    }

    //////////////////////////private methods//////////////////////////
    void CTimer::thread_proc()
    {
        while (true)
        {
            OnTimer();
            pthread_testcancel();
            struct timeval tempval;
            tempval.tv_sec = m_second;
            tempval.tv_usec = m_microsecond;
            select(0, NULL, NULL, NULL, &tempval);
        }
    }

    void CTimer::OnTimer()
    {
        cout<<"Timer once..."<<endl;
    }

    示例代码main.cpp:
    /*
    * main.cpp
    *
    * Created on: 2009-7-19
    *      Author: DEAN
    */

    #include <iostream>
    #include "CTimer.h"

    using namespace std;

    int main()
    {
        CTimer t1(1,0),t2(1,0);    //构造函数,设两个定时器,以1秒为触发时间。参数1是秒,参数2是微秒。
        t1.StartTimer();
        t2.StartTimer();
        sleep(10);
        return 0;
    }

        使用的话其实很简单,只要写一下OnTimer()函数的内容就行了,定时器会在每个定时器触发时调用此函数。里面用到的一个点是使用类的成员函数作为线程体的执行函数,需要进行一下静态类型转换。在上面已标出:
        static void *OnTimer_stub(void *p)
        {
            (static_cast<CTimer*>(p))->thread_proc();
        }
        有了这个类以后,使用定时器就方便多了:-

    在LINUX中经常要使用计时器,而在LINUX环境下使用计时器不像WINDOWS环境下那样一个SETTIMER()方便,主要有三种方式:使用SLEEP/USLEEP+单独线程;SETITMER加处理信号SIGALRM,或者是RTC机制。这里我讲到的是使用RTC机制实现计时器类。这种方法最为优越,它与传统意义上的SLEEP和SIGALRM信号是分离的,它的运行不受SLEEP的影响,而像SETITMER等都会受到SLEEP的影响,因为它们使用的是同一时钟。
        以前用select实现的计时器类(http://hi.baidu.com/susdisk/blog/item/03f70d35e8e2e182a61e1288.html)其实并不是真正的计时器,它是一个循环,只是在处理完一次ONTIMER()事件后停下了一秒,然后再接着一次ONTIMER(),这其实并不是真正的计时器。真正的计时器应该是不管是否在处理ONTIMER()事件,它都会触发。
        RTC(real-time clock)。现在可以使用LINUX下的RTC机制来编写计时器类,这个类是完全意义上的计时器,经过测试,也基本不占用cpu时间,因为它采用的是底层的硬件时钟,rtc的文档中说的很明白,它与系统时钟最大的区别在于即使它在机器耗能非常低的情况下,也会触发此时钟信号。它也与SLEEP、SETITIMER等函数是完全独立的,就是说,使用这个计时器类,你依然可以使用各种SLEEP函数等,互不影响,这一点我觉得是最重要的。

    参考:

    http://www.cnblogs.com/processakai/archive/2012/04/11/2442294.html

    http://blog.csdn.net/lxmky/article/details/7669296

  • 相关阅读:
    IntelliJ IDEA 设置代码提示或自动补全的快捷键
    spring框架学习(一)
    java数据库连接池技术原理(浅析)
    Spring事务管理
    Android invalidate 用法
    sharedPreferences存储文件
    Android Service组件
    SQLite版本升级
    在android.app.Application中定义全局变量 .
    android观察者模式
  • 原文地址:https://www.cnblogs.com/youxin/p/4324366.html
Copyright © 2011-2022 走看看