select 实现定时 + 轮询

select 定时器 + 轮询(单线程)

上周写了 怎么样获取系统时间

这周写一个 定时 + 轮询 的.

在单线程上实现这个感觉没有什么用,有点多此一举… 但是这个时间的逻辑处理还是可以的…

一、select函数

#include 
#include 
#include 


int main(int argc, char const *argv[]){
    int ret = 0;
    fd_set rfds = {0};
    struct timeval tv = {0};


    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    tv.tv_sec = 1;
    tv.tv_usec = 0;

    ret = select(1, &rfds, NULL, NULL, &tv);

    if(ret == -1){
        perror("select error");
    } else if(ret){
        printf("receive data from to STDIN\n\n");
    }else {
        printf("timeout\n\n");
    }

    return 0;
}

运行结果:

baoshaohua:定时器 bao$ ./select_learning 
timeout

baoshaohua:定时器 bao$ ./select_learning 
d
receive data from to STDIN

baoshaohua:定时器 bao$ d
bash: d: command not found

在 select函数监听标准输入0(键盘), 5s内如果有输入就返回值大于0;
如果没有输入就返回0,表示超时了.

前几天发现, 在使用 man select 的时候, 在mac上的结果没有在Linux系统的结果详细…后面会列举几点.

试了一下其他函数, 也是这样.

所以page还是得在linux上查.

二、应用实例

#include 
#include 
#include 
#include 
#include 


typedef void (* timer_func)(char *param);

//保存定时的时间
int running = 0;
struct timeval duration = {
     0};
struct timeval deadtime = {
     0};
timer_func test_func = NULL;
char *test_func_param = NULL;

int i = 0;

//每次轮询都会计算轮询的时间
void recalculate_time(struct timeval *tv)
{
     
    struct timeval now2;
    struct timeval deadtime2;

    gettimeofday(&now2, NULL);
    memcpy(&deadtime2, &now2, sizeof(struct timeval));
    deadtime2.tv_sec += 1;

    if(timercmp(&deadtime2, &deadtime, >)){
     
        // printf("333deadtime.tv_sec[%ld], deadtime.tv_usec[%d] \n", deadtime.tv_sec, deadtime.tv_usec);
        // printf("333deadtime2.tv_sec[%ld], deadtime2.tv_usec[%d] \n", deadtime2.tv_sec, deadtime2.tv_usec);
        // printf("333now2.tv_sec[%ld], now2.tv_usec[%d] \n\n", now2.tv_sec, now2.tv_usec);

        memcpy(&deadtime2, &deadtime, sizeof(struct timeval));
        test_func(test_func_param);
        

        // printf("333deadtime.tv_sec[%ld], deadtime.tv_usec[%d] \n", deadtime.tv_sec, deadtime.tv_usec);
        // printf("333deadtime2.tv_sec[%ld], deadtime2.tv_usec[%d] \n", deadtime2.tv_sec, deadtime2.tv_usec);
        // printf("333now2.tv_sec[%ld], now2.tv_usec[%d] \n\n", now2.tv_sec, now2.tv_usec);
    }

    if(timercmp(&deadtime2, &now2, >)){
     
        printf("[0000]\n\n");
        timersub(&deadtime2, &now2, tv);
    } else {
     
        printf("[1111]\n\n");
        timerclear(tv);
        running = 0; 
    }

    // printf("[%d]\n\n", ++i);
    // printf("222tv.tv_sec[%ld], tv.tv_usec[%d] \n\n", tv->tv_sec, tv->tv_usec);
}

//处理定时模块
int dispatch_timer_module()
{
     
    int ret = 0;
    fd_set rfds = {
     0};
    // fd_set wfds = {0};
    struct timeval tv = {
     0};

//如果在这,轮询的第一秒终端输入才可以收到数据..其他时间不可以
    // FD_ZERO(&rfds);
    // FD_ZERO(&wfds);
    // FD_SET(0, &rfds);
    // FD_SET(1, &wfds);

    running = 1;

    while(running){
     
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        
        recalculate_time(&tv);
// 在退出时,将修改每个文件描述符集,以指示哪个文件描述符实际更改了状态。(因此,如果在循环中使用select(),则必须在每次调用之前重新初始化集合。)
        ret = select(1, &rfds, NULL, NULL, &tv);
        printf("ret [%d]\n", ret); //超时返回0
        if(ret < 0){
      //出错
            perror("select error:");
            
            continue;
        }

        if(ret > 0){
     
            printf("[data]\n");
            goto end;
        }
        
    }


end:
    return ret;
}


//注册超时调用的函数, 超时时间, 死亡时间等
int add_timer_module(int sec, timer_func func, void *param)
{
     
    int ret = 0;
    struct timeval now1 = {
     0};
    struct timeval deadtime1 = {
     0};

    gettimeofday(&now1, NULL);

    printf("111now1.tv_sec[%ld], now1.tv_usec[%d] \n\n", now1.tv_sec, now1.tv_usec);

    duration.tv_sec = sec / 1000;
    duration.tv_usec = (sec % 1000)*1000;

    printf("111duration.tv_sec[%ld], duration.tv_usec[%d] \n\n", duration.tv_sec, duration.tv_usec);

    timeradd(&now1, &duration, &deadtime);
    test_func = func;
    test_func_param = param;

    printf("111deadtime.tv_sec[%ld], deadtime.tv_usec[%d] \n\n", deadtime.tv_sec, deadtime.tv_usec);

    dispatch_timer_module();

    return ret;

}


void timeout_handle_func(char *str)
{
     
    printf("[timeout] %s\n", str);
}

int main(int argc,char const *argv[]){
     
    char buf[10] = "bao";

    add_timer_module(5*1000, timeout_handle_func, buf);


    printf("\n\n");
    struct timeval tt = {
     0};
    gettimeofday(&tt, NULL);
    printf("tt.tv_sec[%ld], tt.tv_usec[%d] \n\n", tt.tv_sec, tt.tv_usec);
    timerclear(&tt);
    printf("tt.tv_sec[%ld], tt.tv_usec[%d] \n\n", tt.tv_sec, tt.tv_usec);


    return 0;
}

运行结果(超时的结果):

111now1.tv_sec[1587736629], now1.tv_usec[602669] 

111duration.tv_sec[5], duration.tv_usec[0] 

111deadtime.tv_sec[1587736634], deadtime.tv_usec[602669] 

[0000]

ret [0]
[0000]

ret [0]
[0000]

ret [0]
[0000]

ret [0]
[timeout] bao
[0000]

ret [0]
[timeout] bao
[1111]

ret [0]


tt.tv_sec[1587736634], tt.tv_usec[607379] 

tt.tv_sec[0], tt.tv_usec[0] 

1、

add_timer_modul这个函数.
第一个参数试设置的超时时间.
第二个参数试超时调用的函数.
第三个参数试第二个参数的参数.

2、实现了在5s内如果键盘没有输入就返回超时.并调用超时函数.并且按照1秒轮询…
如果有输入的话,就退出超时…

3、单线程,感觉没什么意义…可以做成多线程, 去轮询其他的模块.

小插曲

问题1: 只有在一运行的时候才能输入, 其他时候不退出…

select 函数每次执行完, 描述符集就改变了, 刚开始我并没有每次循环将0放到描述符集中

问题2: 会发现超时的时候会出现两次timeout

其实第一次并没有真正超时,

比如, 死亡时间是10.100, 现在时间是 9.101 ,当 9.101 + 1的时候以为是超时了,其实没有…


欢迎关注公众号:

select 实现定时 + 轮询_第1张图片

你可能感兴趣的:(C语言,c语言,嵌入式)