1.时钟相关的API函数原型

#include <unistd.h>
unsigned int sleep(unsigned int seconds);
unsigned int alarm(unsigned int seconds);
int usleep(useconds_t usec);
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value);
           struct itimerval {
               struct timeval it_interval; /* next value */
               struct timeval it_value;    /* current value */
           };

           struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };

2.sleep的实现

    sleep其实大家应该都不陌生,很多语言中都有相应的实现,其作用就是设置一个时延,等待时延,挂起进程。等待时间到达了就恢复进程的运行。在linux中sleep的是实现是使用alarm这个API函数来实现的。实现步骤如下:

一:为SIGALRM设置一个处理函数(SIGALRM是一个时钟信号,时间到了内核就会向进程发送SIGALRM信号)

二:.调用alarm(num_seconds);设置定时器

三:调用pause挂起进程。

这就是一个sleep函数的处理过程,代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

void wakeup(int);
void do_thing();
int main ( int argc, char *argv[] )
{
        printf("about to sleep for seconds\n");
        signal(SIGALRM,wakeup);
        alarm(4);
        pause();
        printf("Morning so son?\n");
        return EXIT_SUCCESS;
}
        /* ----------  end of function main  ---------- */

void wakeup(int signum)
{

                printf("Alarm received from kernel\n");
}

    alarm是一个linux下的计时器,它的第一个用途就是用来作为时延,另一个用途是调度一个在将来某个时刻发生的动作同时做些其他事情。

通过alarm设置计时器,然后继续做别的事情,当计时器到达0,信号发送,处理函数被调用。

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

void do_thing();
int main ( int argc, char *argv[] )
{
        printf("about to sleep for seconds\n");
        signal(SIGALRM,do_thing);
        alarm(4);
        while(1){
                sleep(2);
                printf("继续执行\n");
        }
        return EXIT_SUCCESS;
}
        /* ----------  end of function main  ---------- */

void do_thing()
{
        printf("4秒后执行的\n");
}

sleep,alarm都只能处理秒级的时延和计时器。在linux中提供了ulseep函数提供一个精度更高的时延


3.进程的三个计时器

每一个进程都有三个计时器,分别是:

ITIMER_REAL 用来真实时间的。如果手表记录一样。当这个计时器时间用尽就会发送SIGLRM消息

ITIMER_VIRTUAL 这个计时器只有在用户运行在用户态的时候才计时

ITIMER_PROF 这个计时器用于进程运行在用户态或由该进程调用而陷入核心态时计时。

三个计时器,难道计算机又三个时钟嘛?其实不然,计算机只有一个时钟。其工作方式是每个进程都有自己的计数器,cpu每隔一个时间片就遍历一下所有进程为每一个进程的计时器做递减。


4.间隔计时器编程

间隔计时器和普通的计时器不同,这个计时器有两个值,一个是初始时间间隔,另外一个是重复间隔设置。下面是一个间隔计时器的例子:

#include<stdio.h>
#include<sys/time.h>
#include<signal.h>
#include<stdlib.h>
void countdown(int);
int set_ticker(int);
int main ( int argc, char *argv[] )
{

        //设置信号处理函数
        signal(SIGALRM,countdown);
        //设置计时器
        if(set_ticker(500) == -1)
                perror("set_ticker:");
        else
           //进程继续运行
                while(1)
                done_other_thing();
        return EXIT_SUCCESS;
}
        /* ----------  end of function main  ---------- */


void done_other_thing()
{
        sleep(1);
        printf("*****\n");
}


void countdown (int signum)
{
        static int num=10;
        printf("%d..",num--);
        fflush(stdout);
        if(num<0){
                printf("DONE! \n");
                exit(0);
        }
}
        /* -----  end of function countdown  ----- */
int set_ticker(int n_msecs)
{
        struct itimerval new_timeset;
        long n_sec,n_usecs;
        n_sec = n_msecs / 1000;
        n_usecs = (n_msecs % 1000) *1000L;
        new_timeset.it_interval.tv_sec = n_sec;
        new_timeset.it_interval.tv_usec = n_usecs;
        new_timeset.it_value.tv_sec = n_sec;
        new_timeset.it_value.tv_usec = n_usecs;
        return setitimer(ITIMER_REAL,&new_timeset,NULL);
}

    上面的运行过程如下:首先设置了信号处理函数countdown,然后设置了计时器,初始值是500毫秒也就是500毫秒后计时器时间到,重复间隔也是500。初始500毫秒到达后,又会重新设置计时器为500毫秒。设置好计时器后程序继续向下运行做其他时间了。500毫秒到了后就会出发countdown函数。这个函数会打印数字,并递减静态变量num的值,重复执行十次后num的值就小于0了。然后再次出发countdown的时候就退出整个程序了。