Linux下使用select实现精确定时器

本文最后更新于2017年8月5日,已超过 1 年没有更新,如果文章内容失效,还请反馈给我,谢谢!

=Start=

缘由:

学习需要

正文:

参考解答:

首先看看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参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    fd_set rfds;
    /* Watch stdin (fd 0) to see when it has input. */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    struct timeval tv;
    /* Wait up to five seconds. */
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    int retval;
    retval = select(1, &rfds, NULL, NULL, &tv);
    /* Don't rely on the value of tv now! */

    if (retval == -1)
        perror("select()");
    else if (retval)
        printf("Data is available now.\n");
    /* FD_ISSET(0, &rfds) will be true. */
    else
        printf("No data within five seconds.\n");

    printf("Hi, this is just a printf to test if select() will block code below select() or not.\n");  // result: block

    return EXIT_SUCCESS;
}

利用select实现定时器,需要利用其timeout参数,注意到:
1)select函数使用了一个结构体timeval作为其参数。
2)select函数会更新timeval的值,timeval保持的值为剩余时间。

如果我们指定了参数timeval的值,而将其他参数都置为0或者NULL,那么在时间耗尽后,select函数便返回,基于这一点,我们可以利用select实现精确定时。

timeval的结构如下:

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

我们可以看出其可以精确到 microseconds 也即微秒。下面是实际代码:

#include <stdio.h>
#include <sys/time.h> /* struct timeval */
#include <time.h> /* time() */
#include <errno.h>

#include <inttypes.h>
#include <math.h>
void print_current_time_with_ms(void)
{
    long            ms; // Milliseconds
    time_t          s;  // Seconds
    struct timespec spec;
    clock_gettime(CLOCK_REALTIME, &spec);

    s  = spec.tv_sec;
    ms = round(spec.tv_nsec / 1.0e6); // Convert nanoseconds to milliseconds

    printf("Current time: %"PRIdMAX".%03ld seconds since the Epoch\n", (intmax_t)s, ms);
}

void print_current_timestamp(void)
{
    time_t ltime; /* calendar time */
    ltime = time(NULL); /* get current cal time */
    // printf("%s", asctime(localtime(&ltime)));
    printf("%s", ctime(&ltime));
}

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);
}

int main(int argc, char const *argv[])
{
	int i;
    for(i=0; i<5; ++i) {
	    // printf("[%d]\t%lu\n", i, (unsigned long)time(NULL));
	    // print_current_time_with_ms();
	    print_current_timestamp();
	    // seconds_sleep(1);
	    // milliseconds_sleep(1500);
	    microseconds_sleep(1900000);
    }
    return 0;
}
参考链接:
Linux C的select函数的使用
linux c中select使用技巧
linux c/c++网络编程之—select模型
linux c学习笔记—-select函数详解
linux下使用select实现精确定时器
Linux下获取当前时间(毫秒精度) # clock_gettime()
=END=

声明: 除非注明,ixyzero.com文章均为原创,转载请以链接形式标明本文地址,谢谢!
https://ixyzero.com/blog/archives/3505.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注