c实现的几种定时器

作者: 介和 | 来源:发表于2019-01-05 19:25 被阅读0次

    原文:https://www.cnblogs.com/BKjungle/p/6127807.html

    1.linux下调用系统函数alarm(),setitimer(),sleep(),usleep()(实现微妙定时),

    2.单纯c语言实现gettimeofday()(微妙定时),time(),

    3.windows可用Sleep()实现微秒级定时

    1.alarm()

    #include 

    unsigned int alarm(unsigned int seconds);

    函数返回值

    成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。不阻塞!!!

    出错:-1

    作用:  调用 alarm 函数即设定一个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发 SIGALRM 信号,默认处理动作是终止当前进程。闹钟返

    回值是 0 或者是以前设定的闹钟时间还余下的秒数。如果 seconds 值为 0,表示取消以前设定的闹钟,函数的返回值仍然

    是以前设定的闹钟时间还余下的秒数。

    ------ alarm

    #include 

    #include 

    int main(void)

    {

    int counter;

    alarm(1);

    for(counter=0; 1; counter++)

    printf("counter=%d ", counter);

    return 0;

    }

    这个程序的作用是 1 秒钟之内不停地数数,1 秒钟到了就被 SIGALRM 信号终

    止。

    -----------》》1.1 配合pause函数实现sleep函数!

    #include 

    int pause(void);

    pause 函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终止,pause 函数没有机会返回;如果信号的处理动作是忽略,则

    进程继续处于挂起状态,pause 不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后 pause 返回-1,errno 设置为 EINTR,所以 pause 只有出错

    的返回值(想想以前还学过什么函数只有出错返回值?)。错误码 EINTR 表示“被信号中断”。

    下面我们用 alarm 和 pause 实现 sleep(3)函数,称为 mysleep。

    mysleep

    #include 

    #include 

    #include 

    void sig_alrm(int signo)

    {

    /* nothing to do */

    }

    unsigned int mysleep(unsigned int nsecs)

    {

    struct sigaction newact, oldact;

    unsigned int unslept;

    newact.sa_handler = sig_alrm;

    sigemptyset(&newact.sa_mask);

    newact.sa_flags = 0;

    sigaction(SIGALRM, &newact, &oldact);

    alarm(nsecs);

    pause();

    unslept = alarm(0);

    sigaction(SIGALRM, &oldact, NULL);

    return unslept;

    }

    int main(void)

    {

    while(1){

    mysleep(2);

    printf("Two seconds passed\n");

    }

    return 0;

    }

    1. main 函数调用 mysleep 函数,后者调用 sigaction 注册了 SIGALRM 信号        的处理函数 sig_alrm。

    2. 调用 alarm(nsecs)设定闹钟。

    3. 调用 pause 等待,内核切换到别的进程运行。

    4. nsecs 秒之后,闹钟超时,内核发 SIGALRM 给这个进程。

    5. 从内核态返回这个进程的用户态之前处理未决信号,发现有 SIGALRM 信号,其处理函数是 sig_alrm。

    6. 切换到用户态执行 sig_alrm 函数,进入 sig_alrm 函数时 SIGALRM 信号被自动屏蔽,从 sig_alrm 函数返回时 SIGALRM 信号自动解除屏蔽。然后

    自动执行系统调用 sigreturn 再次进入内核,再返回用户态继续执行进程的主控制流程(main 函数调用的 mysleep 函数)。

    7. pause 函数返回-1,然后调用 alarm(0)取消闹钟,调用 sigaction 恢复SIGALRM 信号以前的处理动作。

    2.setitimer()----------------------- 

    常用到的函数:

    #include 

    int getitimer (int which, struct itimerval* value);

    int setitimer (int which, struct itimerval* newvalue, struct itimerval* oldvalue);

    which有三种状态:

    ITIMER_REAL: 对指定时间值,按自然时间计数, 时间到发出SIGALRM信号.

    ITIMER_VIRTUAL: 对指定时间值, 当只在用户态时(进程执行的时候)计数,  时间到发出SIGVTALRM信号.

    ITIMER_PROF: 对指定时间值, 用户态或内核态(进程执行与系统为进程调度)都计数, 时间到, 发出SIGPROF信号, 与ITIMER_VIRTVAL联合, 常用来计算系统内核时间和用户时间.

    struct timeval

    {

    long tv_sec;/* 秒 */

    long tv_usec;/* 微秒 */

    };

    struct itimerval

    {

    struct timeval it_interval; /* 时间间隔 *///循环定时时间

    struct timeval it_value;/* 当前时间计数 */第一次计时时间

    };

    it_interval用来指定每隔多长时间执行任务, it_value用来保存当前时间离执行任务还有多长时间. 比如说, 你指定it_interval为2秒(微秒为0), 开始的时候我们把it_value的时间也设定为2秒(微秒为0), 当过了一秒, it_value就减少一个为1, 再过1秒, 则it_value又减少1, 变为0, 这个时候发出信号(告诉用户时间到了, 可以执行任务了), 并且系统自动把it_value的置重置为it_interval的值, 即2秒, 再重新计数.

     -------------------------------代码实现

    #include

    #include

    #include

    #include

    #include

    #include

    static char msg[] = "time is running out.\n";

    static int len;

    /* time's up */

    void prompt_info (int signo)

    {

    write (STDERR_FILENO, msg, len);

    }

    void init_sigaction (void)

    {

    struct sigaction tact;

    tact.sa_handler = prompt_info;

    tact.sa_flags = 0;

    sigemptyset (&tact.sa_mask);

    sigaction (SIGALRM, &tact, NULL);

    }

    void init_time ()

    {

    struct itimerval value;

    value.it_value.tv_sec = 2;

    value.it_value.tv_usec = 0;

    value.it_interval = value.it_value;

    /* set ITIMER_REAL */

    setitimer (ITIMER_REAL, &value, NULL);

    }

    int main (int argc, char** argv)

    {

    len = strlen (msg);

    init_sigaction ();

    init_time ();

    while (1);

    exit (0);

    }

    该程序的ITMER_REAL定时器,每隔2秒钟都会发送一个SIGALRM信号,当主函数接收到了这个信号之后,调用信号处理函数prompt_info在标准错误上输出time is running out这个字符串。

    对于ITIMER_VIRTUAL和ITIMER_PROF的使用方法类似,当你在setitimer里面设置的定时器为ITIMER_VIRTUAL的时候,你把sigaction里面的SIGALRM改为SIGVTALARM, 同理,ITIMER_PROF对应SIGPROF。

    不过,你可能会注意到,当你用ITIMER_VIRTUAL和ITIMER_PROF的时候,你拿一个秒表,你会发现程序输出字符串的时间间隔会不止2秒,甚至5-6秒才会输出一个,那是因为cpu在用户与内核切换之间也会浪费时间,这段时间是不计入在指定时间范围之内的。

    3.time()或gettimeofday()利用时间差来计算--------------------------------------------------------------------------------------------------------------------------

    1. #include 

    2. #include 

    3. #include 

    4. #include 

    5. #include  //包含time()函数

    6.#include <sys/time.h>//包含gettimeofday()函数

    7. staticchar msg[] = "I received a msg.\n"; 

    8. int len;

    9. static time_t lasttime;

    10. void show_msg(int signo)

    11. {

    12.     write(STDERR_FILENO, msg, len);

    13. }

    14. intmain()

    15. {

    16.     structsigaction act;

    17.     unionsigval tsval;

    18.

    19.     act.sa_handler = show_msg;

    20.     act.sa_flags = 0;

    21.     sigemptyset(&act.sa_mask);

    22.     sigaction(50, &act, NULL);

    23.

    24.     len = strlen(msg);

    25.     time(&lasttime);

    26.     while( 1 )

    27.     {

    28.         time_tnowtime;

    29.         /*获取当前时间*/

    30.         time(&nowtime);

    31.         /*和上一次的时间做比较,如果大于等于2秒,则立刻发送信号*/

    32.         if(nowtime - lasttime >= 2)

    33.         {

    34.             /*向主进程发送信号,实际上是自己给自己发信号*/

    35.             sigqueue(getpid(), 50, tsval);

    36.             lasttime = nowtime;

    37.         }

    38.     }

    39.     return0;

    40. }

    如果你想更精确的计算时间差,你可以把 time 函数换成gettimeofday,这个可以精确到微妙。

    上面介绍的几种定时方法各有千秋,在计时效率上、方法上和时间的精确度上也各有不同,采用哪种方法,就看你程序的需要

    4 sleep实现方法-------------------- 

    下面我们来看看用sleep以及usleep怎么实现定时执行任务。

    下载: timer2.c

    1. #include 

    2. #include 

    3. #include 

    4. #include 

    5.

    6. staticchar msg[] = "I received a msg.\n";

    7. int len;

    8. void show_msg(int signo)

    9. {

    10.     write(STDERR_FILENO, msg, len);

    11. }

    12. intmain()

    13. {

    14.     structsigaction act;

    15.     unionsigval tsval;

    16.

    17.     act.sa_handler = show_msg;

    18.     act.sa_flags = 0;

    19.     sigemptyset(&act.sa_mask);

    20.     sigaction(50, &act, NULL);

    21.

    22.     len = strlen(msg);

    23.     while( 1 )

    24.     {

    25.         sleep(2); /*睡眠2秒*/

    26.         /*向主进程发送信号,实际上是自己给自己发信号*/

    27.         sigqueue(getpid(), 50, tsval);

    28.     }

    29.     return0;

    30. }

    看到了吧,这个要比上面的简单多了,而且你用秒表测一下,时间很准,指定2秒到了就给你输出一个字符串。所以,如果你只做一般的定时,到了时间去执行一个任务,这种方法是最简单的。

    相关文章

      网友评论

        本文标题:c实现的几种定时器

        本文链接:https://www.haomeiwen.com/subject/lvedrqtx.html